View Javadoc

1   /*
2    * Created on Apr 27, 2005, Copyright UC Regents
3    */
4   package org.telscenter.pas.steps;
5   
6   import java.awt.BorderLayout;
7   import java.awt.Component;
8   import java.awt.Dimension;
9   import java.awt.FlowLayout;
10  import java.awt.GridLayout;
11  import java.awt.event.ActionEvent;
12  import java.awt.event.ActionListener;
13  import java.awt.event.WindowEvent;
14  import java.awt.event.WindowListener;
15  import java.io.IOException;
16  import java.net.MalformedURLException;
17  import java.net.URL;
18  import java.util.logging.Logger;
19  
20  import javax.swing.AbstractAction;
21  import javax.swing.Action;
22  import javax.swing.BorderFactory;
23  import javax.swing.JButton;
24  import javax.swing.JComponent;
25  import javax.swing.JDialog;
26  import javax.swing.JPanel;
27  import javax.swing.JTextField;
28  
29  import net.sf.sail.core.util.BinaryUtils;
30  
31  import org.telscenter.pas.ui.browser.BrowserEvent;
32  import org.telscenter.pas.ui.browser.BrowserFactory;
33  import org.telscenter.pas.ui.browser.BrowserFactoryRegistry;
34  import org.telscenter.pas.ui.browser.IBrowser;
35  import org.telscenter.pas.ui.browser.IBrowserListener;
36  import org.telscenter.pas.ui.browser.NavigationPolicy;
37  import org.telscenter.pas.ui.util.PasColors;
38  
39  /***
40   * Abstract super implementation of the Pas step types, <i>DisplayPage</i> and
41   * <i>Evidence</i>.
42   * 
43   * @author turadg
44   */
45  
46  public abstract class BrowseWeb extends AbstractUrlStep {
47  
48  	/***
49  	 * 
50  	 */
51  	private static final int MAX_PROGRESS = 100;
52  
53  	protected String content;
54  	protected boolean isAuthoringHTMLPage = false;
55  	
56  	/***
57  	 * Logger for this class
58  	 */
59  	private static final Logger logger = Logger.getLogger(BrowseWeb.class
60  			.getName());
61  
62  	final static URL ERROR_URL;
63  	static {
64  		
65  		/*
66  		 * Initializes an error URL to use for when an invalid URL is set
67  		 * in a step.
68  		 */
69  		URL tempUrl = null;
70  		try {
71  			tempUrl = new URL(Messages.getString("BrowseWeb.error_url")); //$NON-NLS-1$
72  		} catch (Exception e) {
73  			e.printStackTrace();
74  		}
75  		ERROR_URL = tempUrl;
76  
77  		
78  		/*
79  		 * The try blocks below each try to initialize factories for browsers
80  		 * that will be used for displaying html and web pages.
81  		 */
82  		
83  		try {
84  			// FIXME: This is a hack for now so things will build again
85  			// This should be configured outside of this step
86  			BrowserFactory wrFactory = (BrowserFactory) Class.forName(
87  					"org.telscenter.pas.ui.browser.WebRendererFactory")
88  					.newInstance();
89  			BrowserFactoryRegistry.registerFactory(wrFactory, 3);
90  			logger.info("Registered WebRenderer browser factory");
91  		} catch (Throwable t) {
92  			logger
93  					.warning("Failed to load and register WebRenderer Browser factory - "
94  							+ t);
95  		}
96  
97  		try {
98  			// FIXME: This is a hack for now so things will build again
99  			// This should be configured outside of this step
100 			BrowserFactory jdicFactory = (BrowserFactory) Class.forName(
101 					"org.telscenter.pas.ui.browser.jdic.JDICBrowserFactory")
102 					.newInstance();
103 			BrowserFactoryRegistry.registerFactory(jdicFactory, 1);
104 			logger.info("Registered JDIC browser factory");
105 		} catch (Throwable t) {
106 			logger
107 					.warning("Failed to load and register JDIC Browser factory - "
108 							+ t);
109 		}
110 		
111 		try {
112 			// FIXME: This is a hack for now so things will build again
113 			// This should be configured outside of this step
114 			BrowserFactory mozswingFactory = (BrowserFactory) Class.forName(
115 					"org.telscenter.pas.ui.browser.mozswing.MozSwingBrowserFactory")
116 					.newInstance();
117 			BrowserFactoryRegistry.registerFactory(mozswingFactory, 1);
118 			logger.info("Registered MozSwing browser factory");
119 		} catch (Throwable t) {
120 			logger
121 					.warning("Failed to load and register MozSwing Browser factory - "
122 							+ t);
123 		}
124 		
125 		
126 	}
127 	
128 	private JButton saveLinkButton;
129 
130 	private BrowserFactory browserFactory;
131 
132 	protected IBrowser browser;
133 
134 	protected NavigationPolicy navigationPolicy;
135 
136 	private BrowseWebUI browseWebUI = null;
137 
138 	/*
139 	 * Sets the content of this BrowseWeb object which may be html
140 	 */
141 	public void setContent(String content) {
142 		if( browser != null ) {
143 			browser.setContent(content);
144 		}
145 			
146 		this.content = content;
147 	}
148 	
149 	/*
150 	 * @return the web browser component which in some cases will be used
151 	 * to preview the html or web page
152 	 */
153 	public Component getComponent() {
154 		// TODO does this UI need to be remade on every call to getComponent()?
155 		browseWebUI = new BrowseWebUI(this);
156 
157 		return  browseWebUI;
158 	}
159 
160 	/*
161 	 * @return the panel that contains the web browser component which in some 
162 	 * cases will be used to preview the html or web page
163 	 */
164 	public JComponent getWebBrowserComponent() {
165 		beforeSessionStart(); //initializes the browser and variables
166 		//initializeBeanContextResources();
167 		
168 		JPanel panel = new JPanel();
169 		panel.setLayout(new GridLayout(1, 1));
170 		Component component = browser.getComponent();
171 		browser.getComponent().repaint();
172 		panel.add(component);
173 		panel.setSize(400, 500);
174 		panel.validate();
175 		return panel;
176 	}
177 
178 	/*
179 	 * @return preview action that is performed when Preview button is clicked
180 	 */
181 	public Action getPreviewAction(boolean hasAddressBar) {
182 		return new PopupPreviewAction(hasAddressBar);
183 	}
184 	
185 	/*
186 	 * @param textField for outside link steps, this is a reference to the 
187 	 * 			text field that contains the URL so that if the user changes
188 	 * 			the link in the preview browser, they can click save link
189 	 * 			and the link will be transferred back to the authoring
190 	 * 			text field
191 	 * @return preview action that is performed when Preview button is clicked
192 	 */
193 	public Action getPreviewAction(boolean hasAddressBar, JTextField textField) {
194 		return new PopupPreviewAction(hasAddressBar, textField);
195 	}
196 	
197 	/*
198 	 * class that is used to implement the preview feature
199 	 */
200 	public class PopupPreviewAction extends AbstractAction {
201 
202 		//used as a reference to the text field for outside link
203 		JTextField textField;
204 		private boolean isSavingLink;
205 		
206 		/***
207 		 * @param hasAddressBar 
208 		 * 
209 		 */
210 		public PopupPreviewAction(boolean hasAddressBar) {
211 			this.isSavingLink = false;
212 		}
213 		
214 		/*
215 		 * constructor that sets the text field for outside link
216 		 */
217 		public PopupPreviewAction(boolean hasAddressBar, JTextField textField) {
218 			this.textField = textField;
219 			this.isSavingLink = true;
220 		}
221 		
222 		/*
223 		 * This is run when the preview button is clicked
224 		 */
225 		public void actionPerformed(ActionEvent e) {
226 			final JDialog dialog = new JDialog(); //the popup for the preview
227 			
228 			dialog.getContentPane().setLayout(new BorderLayout(0,0));
229 			
230 			//adds the browser to the middle of the popup
231 			dialog.getContentPane().add(BrowseWeb.this.getComponent(),
232 					BorderLayout.CENTER);
233 			
234 			/* sets the toolbar to be not visible because the browser already
235 			   has the toolbar imbedded in it */
236 			BrowseWeb.this.getBrowseWebUI().setBrowserToolbarPanelVisible(false);
237 			
238 			//adds the save link button to the bottom of the popup
239 			dialog.getContentPane().add(createSaveLinkButtonPanel(), 
240 					BorderLayout.SOUTH);
241 			
242 			
243 			try {
244 				//sets the URL of the browser to be the URL from the text field
245 				if(textField != null) {
246 					browser.setUrl(new URL(textField.getText()));
247 				}
248 			} catch (MalformedURLException mURLE) {
249 				//System.out.println(mURLE);
250 			}
251 			
252 		
253 			if( isSavingLink ) {
254 			//the action that is performed when the save link button is clicked
255 			saveLinkButton.addActionListener(new ActionListener(){
256 
257 				public void actionPerformed(ActionEvent arg0) {
258 					/* if this is an outside link step, the text field will not
259 					   be null, and if there is a url in the preview popup
260 					   browser, we will send that url back to the authoring
261 					   panel and update the text field with that url*/
262 					//System.out.println(browser.getUrl());
263 					if(textField != null && browser.getUrl() != null) {
264 						textField.setText(browser.getUrl().toString());
265 						setUrl(browser.getUrl());
266 					}
267 					
268 					/* closes the preview popup */
269 					dialog.setVisible(false);
270 					dialog.dispose();
271 				}
272 			});
273 			} else {
274 				saveLinkButton.setText("Close");
275 				saveLinkButton.addActionListener(new ActionListener(){
276 
277 					public void actionPerformed(ActionEvent arg0) {
278 						
279 						dialog.setVisible(false);
280 						dialog.dispose();
281 					}
282 				});
283 			}
284 			
285 			//disposes the popup when closed
286 			dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
287 			
288 			//sets the size of the preview popup
289 			dialog.setPreferredSize(new Dimension(650,500));
290 			
291 			/* makes sure this popup is sized correctly in relation to its sub
292 			   components */
293 			dialog.pack();
294 			
295 			/*
296 			 * makes sure the popup shows up on top of all the other windows
297 			 * so the user is sure not to miss it
298 			 */
299 			dialog.setVisible(true);
300 			dialog.toFront();
301 			dialog.transferFocus();
302 			//dialog.setAlwaysOnTop(true);
303 		}
304 	}
305 	
306 	/*
307 	 * @return a panel with the save link button
308 	 */
309 	protected JPanel createSaveLinkButtonPanel() {
310 		
311 		JPanel panel = new JPanel(new FlowLayout(FlowLayout.CENTER));
312 		
313 		saveLinkButton = new JButton(); //this is a global var
314 		
315 		/*
316 		 * displays save link or save page depending on the step type
317 		 */
318 		if(getType().equals("Outside Link")) {
319 			saveLinkButton.setText("Save Link");
320 			saveLinkButton.setToolTipText("Save this Link");
321 		} else if(getType().equals("Display")) {
322 			saveLinkButton.setText("Save Page");
323 			saveLinkButton.setToolTipText("Save this Page");
324 		}
325 		
326 		panel.setBackground(PasColors.browseWebBackgroundColor);
327 		panel.setBorder(BorderFactory.createEmptyBorder(6, 6, 0, 6));
328 		panel.add(saveLinkButton);
329 		panel.setOpaque(false);
330 		return panel;
331 	}
332 	
333 	
334 	/***
335 	 * build the native browser now because it will slow down the step access
336 	 */
337 	protected void beforeSessionStart() {
338 		if (browserFactory == null) {
339 			browserFactory = BrowserFactoryRegistry.getBestFactory();
340 		}
341 
342 		//gets the browser from the factory
343 		browser = browserFactory.getBrowser();
344 		
345 		//sets the content such as the html
346 		if( content != null ) {
347 			browser.setContent(content);
348 		}
349 		
350 		//listener for browser actions, I don't think we use any of these
351 		browser.addBrowserListener(new IBrowserListener() {
352 
353 			public void documentCompleted(BrowserEvent event) {
354 				logger.info(".documentCompleted()");
355 
356 			}
357 
358 			public void downloadCompleted(BrowserEvent event) {
359 				logger.info(".downloadCompleted()");
360 
361 				browseWebUI.getProgressBar().setValue(56);
362 				browseWebUI.getProgressBar().setString("DOWNLOAD COMPLETED");
363 				browseWebUI.getProgressBar().setIndeterminate(true);
364 				browseWebUI.getProgressBar().repaint();
365 				logger.info("GET "
366 						+ browseWebUI.getProgressBar().getValue());
367 			}
368 
369 			public void downloadProgress(BrowserEvent event) {
370 				logger.info(".downloadProgress()");
371 			}
372 		});
373 		
374 		//makes the url safe, is this necessary?
375 		URL safeUrl = makeSafe(browser, url);
376 		
377 		/* if we are authoring an Evidence or Display Page, we do not want to
378 		   set the URL because that will cause the author's html to not load */
379 		if(!isAuthoringHTMLPage() && url != null) {
380 			browser.setUrl(safeUrl);
381 		}
382 		
383 		//navigation policy determines what type of url strings are allowed
384 		if (navigationPolicy != null) {
385 			browser.setNavigationPolicy(navigationPolicy);
386 		}
387 	}
388 
389 	/*
390 	 * sets the url of the browser
391 	 */
392 	public void setUrl(URL url) {
393 		super.setUrl(url);
394 		if (browser != null) {
395 			URL safeUrl = makeSafe(browser, url);
396 			browser.setUrl(safeUrl);
397 		}
398 	}
399 
400 	/***
401 	 * @return the navigationPolicy
402 	 */
403 	public NavigationPolicy getNavigationPolicy() {
404 		return navigationPolicy;
405 	}
406 
407 	/***
408 	 * @param navigationPolicy
409 	 *            the navigationPolicy to set
410 	 */
411 	public void setNavigationPolicy(NavigationPolicy navigationPolicy) {
412 		if (navigationPolicy == null)
413 			throw new NullPointerException("null navigationPolicy argument");
414 		Object old = this.navigationPolicy;
415 		this.navigationPolicy = navigationPolicy;
416 		firePropertyChange("navigationPolicy", old, navigationPolicy); //$NON-NLS-1$
417 	}
418 
419 	/*
420 	 * I don't think we need this anymore because we either display online
421 	 * web pages or html pages created by the user that are retrieved from
422 	 * memory and not written to a file and then accessed by their file URL
423 	 */
424 	public static URL makeSafe(IBrowser browser, URL location) {
425 		if (location == null)
426 			location = ERROR_URL;
427 
428 		// can't just check type of 'browser' because not all classes (JNI) can
429 		// load everywhere
430 
431 		// if it's not understood by native component, convert it
432 		try {
433 			if (!isNativeSafe(location))
434 				location = BinaryUtils.toFileUrl(location);
435 		} catch (IOException e) {
436 			// TODO Auto-generated catch block
437 			logger.severe("URL -  : exception: " + e); //$NON-NLS-1$
438 		}
439 
440 		// At least Safari and jdic with mozilla on linux
441 		// need extra slashes in file urls
442 		// I don't think adding extra slashes will hurt
443 		// anything.
444 		/*
445 		 * String browserType = browser.getBrowserType(); if
446 		 * (browserType.equals("safari")) //$NON-NLS-1$ location =
447 		 * makeSafariSafe(location);
448 		 */
449 		location = addFileURLSlashes(location);
450 
451 		return location;
452 	}
453 
454 	/***
455 	 * Some browsers require file urls to more than one slash, this is intended
456 	 * to allow relative file urls. Safari requires at least 2 slashes before a
457 	 * file url is considered absolute. Firefox on linux using jdic requires 3
458 	 * slashes before a file url is considered absolute.
459 	 * 
460 	 * @param url
461 	 * @return
462 	 */
463 	private static URL addFileURLSlashes(URL url) {
464 		if (!url.getProtocol().equals("file")) //$NON-NLS-1$
465 			return url;
466 
467 		// now we know url starts with "file:"
468 
469 		String extForm = url.toExternalForm();
470 		if (extForm.startsWith("file:///")) { //$NON-NLS-1$
471 			// then it has the necessary triple slash
472 			return url;
473 		} else {
474 			// has less than 3 slashes, add a minimum of 3
475 			String path = url.getPath();
476 			URL newUrl;
477 			try {
478 				// for some reason the url string needs a total of 5 slashes
479 				// before
480 				// its toString method returns a url with 3 slashes.
481 				// there is a slash at the beginning of the path and
482 				// so that plus these 4 makes 5
483 				newUrl = new URL("file:////" + path); //$NON-NLS-1$
484 			} catch (MalformedURLException e) {
485 				// this shouldn't be possible
486 				throw new Error(e);
487 			}
488 			return newUrl;
489 		}
490 	}
491 
492 	/***
493 	 * @param location
494 	 * @return
495 	 * @throws IOException
496 	 */
497 	private static boolean isNativeSafe(URL location) throws IOException {
498 		String protocol = location.getProtocol().intern();
499 		// TODO ask JDIC to include an getSupportedProtocols() method in JDIC
500 		// Browser API
501 		return (protocol == "http" || protocol == "https" || protocol == "file"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
502 	}
503 
504 	/***
505 	 * @return the browser
506 	 */
507 	public IBrowser getBrowser() {
508 		return browser;
509 	}
510 
511 	/***
512 	 * @return the browseWebUI
513 	 */
514 	public BrowseWebUI getBrowseWebUI() {
515 		return browseWebUI;
516 	}
517 
518 	public void setBrowseWebUI(BrowseWebUI browseWebUI) {
519 		this.browseWebUI = browseWebUI;
520 	}
521 	/***
522 	 * @return the content
523 	 */
524 	public String getContent() {
525 		return content;
526 	}
527 
528 	/***
529 	 * @return the isAuthoring
530 	 */
531 	public boolean isAuthoringHTMLPage() {
532 		return isAuthoringHTMLPage;
533 	}
534 
535 	/***
536 	 * Set whether this BrowseWeb bean is being used for authoring
537 	 * so that we can determine whether to load the url or not since
538 	 * we don't want to load the url if we are authoring a Display Page
539 	 * or an Evidence page
540 	 * @param isAuthoring the isAuthoring to set
541 	 */
542 	public void setIsAuthoringHTMLPage(boolean isAuthoringHTMLPage) {
543 		this.isAuthoringHTMLPage = isAuthoringHTMLPage;
544 	}
545 
546 	public Object clone() {
547 		try {
548 			setUrl(new URL("http://"));
549 		} catch(Exception e) {
550 			
551 		}
552 		return this;
553 	}
554 }