KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > awt > SwingBrowserImpl


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19 package org.openide.awt;
20
21 import java.util.logging.Level JavaDoc;
22 import java.util.logging.Logger JavaDoc;
23 import org.openide.util.Mutex;
24 import org.openide.util.NbBundle;
25 import org.openide.util.RequestProcessor;
26
27 import java.awt.Dimension JavaDoc;
28 import java.awt.Graphics JavaDoc;
29 import java.awt.Rectangle JavaDoc;
30
31 import java.beans.PropertyChangeEvent JavaDoc;
32 import java.beans.PropertyChangeListener JavaDoc;
33 import java.beans.PropertyChangeSupport JavaDoc;
34
35 import java.io.FilterInputStream JavaDoc;
36 import java.io.IOException JavaDoc;
37 import java.io.InputStream JavaDoc;
38
39 import java.net.URL JavaDoc;
40 import java.net.URLConnection JavaDoc;
41
42 import java.util.Vector JavaDoc;
43
44 import javax.swing.AbstractAction JavaDoc;
45 import javax.swing.ActionMap JavaDoc;
46 import javax.swing.JEditorPane JavaDoc;
47 import javax.swing.SwingConstants JavaDoc;
48 import javax.swing.SwingUtilities JavaDoc;
49 import javax.swing.event.HyperlinkEvent JavaDoc;
50 import javax.swing.event.HyperlinkListener JavaDoc;
51 import javax.swing.text.AbstractDocument JavaDoc;
52 import javax.swing.text.DefaultEditorKit JavaDoc;
53 import javax.swing.text.Document JavaDoc;
54 import javax.swing.text.html.*;
55
56
57 /**
58 * Implementation of BrowserImpl in Swing.
59 */

60 final class SwingBrowserImpl extends HtmlBrowser.Impl implements Runnable JavaDoc {
61     /** state of history management */
62     private static final int NO_NAVIGATION = 1;
63     private static final int NAVIGATION_BACK = 2;
64     private static final int NAVIGATION_FWD = 3;
65     private static RequestProcessor rp = new RequestProcessor("Swing Browser"); //NOI18N
66

67     /** Current URL. */
68     private URL JavaDoc url;
69
70     /** URL loaded by JEditorPane */
71     private URL JavaDoc loadingURL;
72     private PropertyChangeSupport JavaDoc pcs;
73     private String JavaDoc statusMessage = ""; // NOI18N
74
private SwingBrowser swingBrowser;
75
76     /** list of accessed URLs for back/fwd navigation */
77     private Vector JavaDoc<Object JavaDoc> historyList;
78
79     /** current position in history */
80     private int historyIndex;
81
82     /** navigation indication */
83     private int historyNavigating = NO_NAVIGATION;
84     private String JavaDoc title = null;
85     boolean fetchingTitle = false;
86
87     private static Logger JavaDoc LOG = Logger.getLogger(SwingBrowserImpl.class.getName());
88
89     SwingBrowserImpl() {
90         pcs = new PropertyChangeSupport JavaDoc(this);
91         swingBrowser = new SwingBrowser();
92         historyList = new Vector JavaDoc<Object JavaDoc>(5, 3);
93         historyIndex = -1;
94         swingBrowser.addPropertyChangeListener(
95             "page", // NOI18N
96
new PropertyChangeListener JavaDoc() {
97                 public void propertyChange(PropertyChangeEvent JavaDoc evt) {
98                     if (evt.getNewValue() instanceof URL JavaDoc) {
99                         URL JavaDoc old = SwingBrowserImpl.this.url;
100                         SwingBrowserImpl.this.url = (URL JavaDoc) evt.getNewValue();
101                         SwingBrowserImpl.this.pcs.firePropertyChange(PROP_URL, old, url);
102
103                         if (((URL JavaDoc) evt.getNewValue()).equals(loadingURL)) {
104                             loadingURL = null;
105                         }
106
107                         // update history
108
if (historyNavigating == NAVIGATION_BACK) {
109                             int idx = historyList.lastIndexOf(evt.getNewValue(), historyIndex - 1);
110
111                             if (idx != -1) {
112                                 historyIndex = idx;
113                             }
114                         } else if (historyNavigating == NAVIGATION_FWD) {
115                             int idx = historyList.indexOf(evt.getNewValue(), historyIndex + 1);
116
117                             if (idx != -1) {
118                                 historyIndex = idx;
119                             }
120                         } else {
121                             while (historyList.size() > (historyIndex + 1))
122                                 historyList.remove(historyList.size() - 1);
123
124                             historyList.add(evt.getNewValue());
125                             historyIndex = historyList.size() - 1;
126                         }
127
128                         historyNavigating = NO_NAVIGATION;
129                         pcs.firePropertyChange(PROP_BACKWARD, null, null);
130                         pcs.firePropertyChange(PROP_FORWARD, null, null);
131                         SwingUtilities.invokeLater(SwingBrowserImpl.this);
132                     }
133                 }
134             }
135         );
136     }
137
138     /**
139     * Returns visual component of html browser.
140     *
141     * @return visual component of html browser.
142     */

143     public java.awt.Component JavaDoc getComponent() {
144         return swingBrowser;
145     }
146
147     /**
148     * Reloads current html page.
149     */

150     public void reloadDocument() {
151         synchronized (rp) {
152             try {
153                 if ((url == null) || (loadingURL != null)) {
154                     return;
155                 }
156
157                 Document JavaDoc doc = swingBrowser.getDocument();
158                 loadingURL = url;
159
160                 if (doc instanceof AbstractDocument JavaDoc) {
161                     String JavaDoc protocol = url.getProtocol();
162
163                     if ("ftp".equalsIgnoreCase(protocol) // NOI18N
164
||"http".equalsIgnoreCase(protocol) // NOI18N
165
) {
166                         ((AbstractDocument JavaDoc) doc).setAsynchronousLoadPriority(Thread.NORM_PRIORITY);
167                     } else {
168                         ((AbstractDocument JavaDoc) doc).setAsynchronousLoadPriority(-1);
169                     }
170                 }
171
172                 rp.post(this);
173             } catch (Exception JavaDoc e) {
174                 LOG.log(Level.WARNING, null, e);
175                 pcs.firePropertyChange(PROP_STATUS_MESSAGE, null, statusMessage = "" + e); // NOI18N
176
}
177         }
178     }
179
180     /**
181     * Stops loading of current html page.
182     */

183     public void stopLoading() {
184     }
185
186     /**
187     * Sets current URL.
188     *
189     * @param url URL to show in the browser.
190     */

191     public void setURL(URL JavaDoc url) {
192         synchronized (rp) {
193             try {
194                 if (url == null) {
195                     return;
196                 }
197
198                 loadingURL = url;
199
200                 rp.post(this);
201             } catch (Exception JavaDoc e) {
202                 LOG.log(Level.WARNING, null, e);
203                 pcs.firePropertyChange(PROP_STATUS_MESSAGE, null, statusMessage = "" + e); // NOI18N
204
}
205         }
206     }
207
208     /**
209     * Returns current URL.
210     *
211     * @return current URL.
212     */

213     public URL JavaDoc getURL() {
214         return url;
215     }
216
217     /**
218     * Returns status message representing status of html browser.
219     *
220     * @return status message.
221     */

222     public String JavaDoc getStatusMessage() {
223         return statusMessage;
224     }
225
226     /** Returns title of the displayed page.
227     * @return title
228     */

229     public String JavaDoc getTitle() {
230         if (title == null) {
231             Mutex.EVENT.readAccess(this);
232         }
233
234         return (title == null) ? NbBundle.getMessage(SwingBrowserImpl.class, "LBL_Loading") : title; //NOI18N
235
}
236
237     void updateTitle() {
238         assert SwingUtilities.isEventDispatchThread();
239
240         // System.err.println("Update title");
241
if (fetchingTitle) {
242             // System.err.println(" ...already updating");
243
return;
244         }
245
246         fetchingTitle = true;
247
248         String JavaDoc oldTitle = getTitle();
249
250         try {
251             Document JavaDoc d = swingBrowser.getDocument();
252             title = (String JavaDoc) d.getProperty(HTMLDocument.TitleProperty);
253
254             // System.err.println("Title from document is " + title);
255
if ((title == null) || (title.trim().length() == 0)) {
256                 // System.err.println("No title from document, trying from url ");
257
URL JavaDoc url = getURL();
258
259                 if (url != null) {
260                     title = url.getFile();
261
262                     if (title.length() == 0) {
263                         title = NbBundle.getMessage(SwingBrowserImpl.class, "LBL_Untitled"); //NOI18N
264
} else {
265                         //Trim any extraneous path info
266
int i = title.lastIndexOf("/"); //NOI18N
267

268                         if ((i != -1) && (i != (title.length() - 1))) {
269                             title = title.substring(i + 1);
270                         }
271                     }
272
273                     // System.err.println("Using from url: " + title);
274
}
275             }
276
277             if (title != null) {
278                 if (title.length() > 60) {
279                     //Truncate to a reasonable tab length
280
title = NbBundle.getMessage(
281                             SwingBrowserImpl.class, "LBL_Title", new Object JavaDoc[] { title.substring(0, 57) }
282                         );
283                 }
284
285                 if (!oldTitle.equals(title)) {
286                     // System.err.println("Firing prop change from " + oldTitle + " to " + title);
287
pcs.firePropertyChange(PROP_TITLE, oldTitle, title); //NOI18N
288
}
289             }
290         } finally {
291             fetchingTitle = false;
292         }
293     }
294
295     public void run() {
296         if (SwingUtilities.isEventDispatchThread()) {
297             title = null;
298             updateTitle();
299         } else {
300             URL JavaDoc requestedURL;
301             synchronized (rp) {
302                 if ((this.url != null) && this.url.sameFile(url)) {
303                     Document JavaDoc doc = swingBrowser.getDocument();
304
305                     if (doc != null) {
306                         //force reload
307
doc.putProperty(Document.StreamDescriptionProperty, null);
308                     }
309                 }
310                 requestedURL = loadingURL;
311                 loadingURL = null;
312             }
313             try {
314
315                 swingBrowser.setPage(requestedURL);
316                 setStatusText(null);
317             } catch (java.net.UnknownHostException JavaDoc uhe) {
318                 setStatusText(
319                     NbBundle.getMessage(SwingBrowserImpl.class, "FMT_UnknownHost", new Object JavaDoc[] { requestedURL })
320                 ); // NOI18N
321
} catch (java.net.NoRouteToHostException JavaDoc nrthe) {
322                 setStatusText(
323                     NbBundle.getMessage(SwingBrowserImpl.class, "FMT_NoRouteToHost", new Object JavaDoc[] { requestedURL })
324                 ); // NOI18N
325
} catch (IOException JavaDoc ioe) {
326                 setStatusText(
327                     NbBundle.getMessage(SwingBrowserImpl.class, "FMT_InvalidURL", new Object JavaDoc[] { requestedURL })
328                 ); // NOI18N
329
}
330
331             SwingUtilities.invokeLater(this);
332         }
333     }
334
335     /**
336      * Accessor to allow a message about bad urls to be displayed - see
337      * HtmlBrowser.setURL().
338      */

339     void setStatusText(String JavaDoc s) {
340         pcs.firePropertyChange(PROP_STATUS_MESSAGE, null, statusMessage = s); // NOI18N
341
}
342
343     /** Is forward button enabled?
344     * @return true if it is
345     */

346     public boolean isForward() {
347         return (historyIndex >= 0) && (historyIndex < (historyList.size() - 1)) &&
348         (historyNavigating == NO_NAVIGATION);
349     }
350
351     /** Moves the browser forward. Failure is ignored.
352     */

353     public void forward() {
354         if (isForward()) {
355             historyNavigating = NAVIGATION_FWD;
356             setURL((URL JavaDoc) historyList.elementAt(historyIndex + 1));
357         }
358     }
359
360     /** Is backward button enabled?
361     * @return true if it is
362     */

363     public boolean isBackward() {
364         return (historyIndex > 0) && (historyIndex < historyList.size()) && (historyNavigating == NO_NAVIGATION);
365     }
366
367     /** Moves the browser forward. Failure is ignored.
368     */

369     public void backward() {
370         if (isBackward()) {
371             historyNavigating = NAVIGATION_BACK;
372             setURL((URL JavaDoc) historyList.elementAt(historyIndex - 1));
373         }
374     }
375
376     /** Is history button enabled?
377     * @return true if it is
378     */

379     public boolean isHistory() {
380         return false;
381     }
382
383     /** Invoked when the history button is pressed.
384     */

385     public void showHistory() {
386     }
387
388     /**
389     * Adds PropertyChangeListener to this browser.
390     *
391     * @param l Listener to add.
392     */

393     public void addPropertyChangeListener(PropertyChangeListener JavaDoc l) {
394         pcs.addPropertyChangeListener(l);
395     }
396
397     /**
398     * Removes PropertyChangeListener from this browser.
399     *
400     * @param l Listener to remove.
401     */

402     public void removePropertyChangeListener(PropertyChangeListener JavaDoc l) {
403         pcs.removePropertyChangeListener(l);
404     }
405
406     // encoding support; copied from html/HtmlEditorSupport
407
private static String JavaDoc findEncodingFromURL(InputStream JavaDoc stream) {
408         try {
409             byte[] arr = new byte[4096];
410             int len = stream.read(arr, 0, arr.length);
411             String JavaDoc txt = new String JavaDoc(arr, 0, (len >= 0) ? len : 0).toUpperCase();
412
413             // encoding
414
return findEncoding(txt);
415         } catch (Exception JavaDoc x) {
416             x.printStackTrace();
417         }
418
419         return null;
420     }
421
422     /** Tries to guess the mime type from given input stream. Tries to find
423      * <em>&lt;meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"&gt;</em>
424      * @param txt the string to search in (should be in upper case)
425      * @return the encoding or null if no has been found
426      */

427     private static String JavaDoc findEncoding(String JavaDoc txt) {
428         int headLen = txt.indexOf("</HEAD>"); // NOI18N
429

430         if (headLen == -1) {
431             headLen = txt.length();
432         }
433
434         int content = txt.indexOf("CONTENT-TYPE"); // NOI18N
435

436         if ((content == -1) || (content > headLen)) {
437             return null;
438         }
439
440         int charset = txt.indexOf("CHARSET=", content); // NOI18N
441

442         if (charset == -1) {
443             return null;
444         }
445
446         int charend = txt.indexOf('"', charset);
447         int charend2 = txt.indexOf('\'', charset);
448
449         if ((charend == -1) && (charend2 == -1)) {
450             return null;
451         }
452
453         if (charend2 != -1) {
454             if ((charend == -1) || (charend > charend2)) {
455                 charend = charend2;
456             }
457         }
458
459         return txt.substring(charset + "CHARSET=".length(), charend); // NOI18N
460
}
461
462     // innerclasses ..............................................................
463
private class SwingBrowser extends JEditorPane JavaDoc {
464         private boolean lastPaintException = false;
465
466         private SwingBrowser() {
467             setEditable(false);
468             addHyperlinkListener(
469                 new HyperlinkListener JavaDoc() {
470                     public void hyperlinkUpdate(HyperlinkEvent JavaDoc e) {
471                         if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
472                             if (e instanceof HTMLFrameHyperlinkEvent) {
473                                 HTMLFrameHyperlinkEvent evt = (HTMLFrameHyperlinkEvent) e;
474                                 HTMLDocument doc = (HTMLDocument) getDocument();
475                                 URL JavaDoc old = getURL();
476                                 doc.processHTMLFrameHyperlinkEvent(evt);
477                                 pcs.firePropertyChange(PROP_URL, old, e.getURL());
478                             } else {
479                                 try {
480                                     SwingBrowserImpl.this.setURL(e.getURL());
481                                 } catch (Exception JavaDoc ex) {
482                                     LOG.log(Level.WARNING, null, ex);
483                                 }
484                             }
485                         }
486                     }
487                 }
488             );
489
490             //when up/down arrow keys are pressed, ensure the whole browser content
491
//scrolls up/down instead of moving the caret position only
492
ActionMap JavaDoc actionMap = getActionMap();
493             actionMap.put(DefaultEditorKit.upAction, new ScrollAction(-1));
494             actionMap.put(DefaultEditorKit.downAction, new ScrollAction(1));
495         }
496
497         /**
498          * Fetches a stream for the given URL, which is about to
499          * be loaded by the <code>setPage</code> method.
500          * This method is expected to have the the side effect of
501          * establishing the content type, and therefore setting the
502          * appropriate <code>EditorKit</code> to use for loading the stream.
503          * <p>
504          * If debugger is not running returns super implementation.
505          * <p>
506          * If debugger runs it will set content type to text/html.
507          * Forwarding is not supported is that case.
508          * <p>Control using sysprop org.openide.awt.SwingBrowserImpl.do-not-block-awt=true.
509          *
510          * @param page the URL of the page
511          */

512         protected InputStream JavaDoc getStream(URL JavaDoc page) throws IOException JavaDoc {
513             SwingUtilities.invokeLater(SwingBrowserImpl.this);
514
515             // #53207: pre-read encoding from loaded URL
516
String JavaDoc charset = findEncodingFromURL(page.openStream());
517             LOG.log(Level.FINE, "Url " + page + " has charset " + charset); // NOI18N
518

519             if (charset != null) {
520                 putClientProperty("charset", charset);
521             }
522
523             // XXX debugger ought to set this temporarily
524
if (Boolean.getBoolean("org.openide.awt.SwingBrowserImpl.do-not-block-awt")) {
525                 // try to set contentType quickly and return (don't block AWT Thread)
526
setContentType("text/html"); // NOI18N
527

528                 return new FilteredInputStream(page.openConnection(), SwingBrowserImpl.this);
529             } else {
530                 return super.getStream(page);
531             }
532         }
533
534         public Dimension JavaDoc getPreferredSize() {
535             try {
536                 return super.getPreferredSize();
537             } catch (RuntimeException JavaDoc e) {
538                 //Bug in javax.swing.text.html.BlockView
539
return new Dimension JavaDoc(400, 600);
540             }
541         }
542
543         public void paint(Graphics JavaDoc g) {
544             try {
545                 super.paint(g);
546                 lastPaintException = false;
547             } catch (RuntimeException JavaDoc e) {
548                 //Bug in javax.swing.text.html.BlockView
549
//do nothing
550
if (!lastPaintException) {
551                     repaint();
552                 }
553
554                 lastPaintException = true;
555             }
556         }
557
558         /**
559          * An action to scroll the browser content up or down.
560          */

561         private class ScrollAction extends AbstractAction JavaDoc {
562             int direction;
563
564             public ScrollAction(int direction) {
565                 this.direction = direction;
566             }
567
568             public void actionPerformed(java.awt.event.ActionEvent JavaDoc e) {
569                 Rectangle JavaDoc r = getVisibleRect();
570                 int increment = getScrollableUnitIncrement(r, SwingConstants.VERTICAL, direction);
571                 r.y += (increment * direction);
572                 scrollRectToVisible(r);
573             }
574         }
575     }
576
577     /**
578      * FilterInputStream that delays opening of stream.
579      * The purpose is not to initialize the stream when it is created in getStream()
580      * but to do it later when the content is asynchronously loaded in separate thread.
581      */

582     private static class FilteredInputStream extends FilterInputStream JavaDoc {
583         private final URLConnection JavaDoc conn;
584         private final SwingBrowserImpl browser;
585
586         FilteredInputStream(URLConnection JavaDoc conn, SwingBrowserImpl browser) {
587             super((FilterInputStream JavaDoc) null);
588             this.conn = conn;
589             this.browser = browser;
590         }
591
592         private synchronized void openStream() throws IOException JavaDoc {
593             if (in == null) {
594                 in = conn.getInputStream();
595             }
596         }
597
598         public int available() throws IOException JavaDoc {
599             openStream();
600
601             return super.available();
602         }
603
604         public long skip(long n) throws IOException JavaDoc {
605             openStream();
606
607             return super.skip(n);
608         }
609
610         public void reset() throws IOException JavaDoc {
611             openStream();
612             super.reset();
613         }
614
615         public void close() throws IOException JavaDoc {
616             openStream();
617             super.close();
618             Mutex.EVENT.readAccess(browser);
619         }
620
621         public int read(byte[] b) throws IOException JavaDoc {
622             openStream();
623
624             return super.read(b);
625         }
626
627         public int read(byte[] b, int off, int len) throws IOException JavaDoc {
628             openStream();
629
630             return super.read(b, off, len);
631         }
632
633         public int read() throws IOException JavaDoc {
634             openStream();
635
636             return super.read();
637         }
638     }
639 }
640
Popular Tags