1 19 package org.openide.awt; 20 21 import java.util.logging.Level ; 22 import java.util.logging.Logger ; 23 import org.openide.util.Mutex; 24 import org.openide.util.NbBundle; 25 import org.openide.util.RequestProcessor; 26 27 import java.awt.Dimension ; 28 import java.awt.Graphics ; 29 import java.awt.Rectangle ; 30 31 import java.beans.PropertyChangeEvent ; 32 import java.beans.PropertyChangeListener ; 33 import java.beans.PropertyChangeSupport ; 34 35 import java.io.FilterInputStream ; 36 import java.io.IOException ; 37 import java.io.InputStream ; 38 39 import java.net.URL ; 40 import java.net.URLConnection ; 41 42 import java.util.Vector ; 43 44 import javax.swing.AbstractAction ; 45 import javax.swing.ActionMap ; 46 import javax.swing.JEditorPane ; 47 import javax.swing.SwingConstants ; 48 import javax.swing.SwingUtilities ; 49 import javax.swing.event.HyperlinkEvent ; 50 import javax.swing.event.HyperlinkListener ; 51 import javax.swing.text.AbstractDocument ; 52 import javax.swing.text.DefaultEditorKit ; 53 import javax.swing.text.Document ; 54 import javax.swing.text.html.*; 55 56 57 60 final class SwingBrowserImpl extends HtmlBrowser.Impl implements Runnable { 61 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"); 67 68 private URL url; 69 70 71 private URL loadingURL; 72 private PropertyChangeSupport pcs; 73 private String statusMessage = ""; private SwingBrowser swingBrowser; 75 76 77 private Vector <Object > historyList; 78 79 80 private int historyIndex; 81 82 83 private int historyNavigating = NO_NAVIGATION; 84 private String title = null; 85 boolean fetchingTitle = false; 86 87 private static Logger LOG = Logger.getLogger(SwingBrowserImpl.class.getName()); 88 89 SwingBrowserImpl() { 90 pcs = new PropertyChangeSupport (this); 91 swingBrowser = new SwingBrowser(); 92 historyList = new Vector <Object >(5, 3); 93 historyIndex = -1; 94 swingBrowser.addPropertyChangeListener( 95 "page", new PropertyChangeListener () { 97 public void propertyChange(PropertyChangeEvent evt) { 98 if (evt.getNewValue() instanceof URL ) { 99 URL old = SwingBrowserImpl.this.url; 100 SwingBrowserImpl.this.url = (URL ) evt.getNewValue(); 101 SwingBrowserImpl.this.pcs.firePropertyChange(PROP_URL, old, url); 102 103 if (((URL ) evt.getNewValue()).equals(loadingURL)) { 104 loadingURL = null; 105 } 106 107 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 143 public java.awt.Component getComponent() { 144 return swingBrowser; 145 } 146 147 150 public void reloadDocument() { 151 synchronized (rp) { 152 try { 153 if ((url == null) || (loadingURL != null)) { 154 return; 155 } 156 157 Document doc = swingBrowser.getDocument(); 158 loadingURL = url; 159 160 if (doc instanceof AbstractDocument ) { 161 String protocol = url.getProtocol(); 162 163 if ("ftp".equalsIgnoreCase(protocol) ||"http".equalsIgnoreCase(protocol) ) { 166 ((AbstractDocument ) doc).setAsynchronousLoadPriority(Thread.NORM_PRIORITY); 167 } else { 168 ((AbstractDocument ) doc).setAsynchronousLoadPriority(-1); 169 } 170 } 171 172 rp.post(this); 173 } catch (Exception e) { 174 LOG.log(Level.WARNING, null, e); 175 pcs.firePropertyChange(PROP_STATUS_MESSAGE, null, statusMessage = "" + e); } 177 } 178 } 179 180 183 public void stopLoading() { 184 } 185 186 191 public void setURL(URL 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 e) { 202 LOG.log(Level.WARNING, null, e); 203 pcs.firePropertyChange(PROP_STATUS_MESSAGE, null, statusMessage = "" + e); } 205 } 206 } 207 208 213 public URL getURL() { 214 return url; 215 } 216 217 222 public String getStatusMessage() { 223 return statusMessage; 224 } 225 226 229 public String getTitle() { 230 if (title == null) { 231 Mutex.EVENT.readAccess(this); 232 } 233 234 return (title == null) ? NbBundle.getMessage(SwingBrowserImpl.class, "LBL_Loading") : title; } 236 237 void updateTitle() { 238 assert SwingUtilities.isEventDispatchThread(); 239 240 if (fetchingTitle) { 242 return; 244 } 245 246 fetchingTitle = true; 247 248 String oldTitle = getTitle(); 249 250 try { 251 Document d = swingBrowser.getDocument(); 252 title = (String ) d.getProperty(HTMLDocument.TitleProperty); 253 254 if ((title == null) || (title.trim().length() == 0)) { 256 URL 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"); } else { 265 int i = title.lastIndexOf("/"); 268 if ((i != -1) && (i != (title.length() - 1))) { 269 title = title.substring(i + 1); 270 } 271 } 272 273 } 275 } 276 277 if (title != null) { 278 if (title.length() > 60) { 279 title = NbBundle.getMessage( 281 SwingBrowserImpl.class, "LBL_Title", new Object [] { title.substring(0, 57) } 282 ); 283 } 284 285 if (!oldTitle.equals(title)) { 286 pcs.firePropertyChange(PROP_TITLE, oldTitle, title); } 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 requestedURL; 301 synchronized (rp) { 302 if ((this.url != null) && this.url.sameFile(url)) { 303 Document doc = swingBrowser.getDocument(); 304 305 if (doc != null) { 306 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 uhe) { 318 setStatusText( 319 NbBundle.getMessage(SwingBrowserImpl.class, "FMT_UnknownHost", new Object [] { requestedURL }) 320 ); } catch (java.net.NoRouteToHostException nrthe) { 322 setStatusText( 323 NbBundle.getMessage(SwingBrowserImpl.class, "FMT_NoRouteToHost", new Object [] { requestedURL }) 324 ); } catch (IOException ioe) { 326 setStatusText( 327 NbBundle.getMessage(SwingBrowserImpl.class, "FMT_InvalidURL", new Object [] { requestedURL }) 328 ); } 330 331 SwingUtilities.invokeLater(this); 332 } 333 } 334 335 339 void setStatusText(String s) { 340 pcs.firePropertyChange(PROP_STATUS_MESSAGE, null, statusMessage = s); } 342 343 346 public boolean isForward() { 347 return (historyIndex >= 0) && (historyIndex < (historyList.size() - 1)) && 348 (historyNavigating == NO_NAVIGATION); 349 } 350 351 353 public void forward() { 354 if (isForward()) { 355 historyNavigating = NAVIGATION_FWD; 356 setURL((URL ) historyList.elementAt(historyIndex + 1)); 357 } 358 } 359 360 363 public boolean isBackward() { 364 return (historyIndex > 0) && (historyIndex < historyList.size()) && (historyNavigating == NO_NAVIGATION); 365 } 366 367 369 public void backward() { 370 if (isBackward()) { 371 historyNavigating = NAVIGATION_BACK; 372 setURL((URL ) historyList.elementAt(historyIndex - 1)); 373 } 374 } 375 376 379 public boolean isHistory() { 380 return false; 381 } 382 383 385 public void showHistory() { 386 } 387 388 393 public void addPropertyChangeListener(PropertyChangeListener l) { 394 pcs.addPropertyChangeListener(l); 395 } 396 397 402 public void removePropertyChangeListener(PropertyChangeListener l) { 403 pcs.removePropertyChangeListener(l); 404 } 405 406 private static String findEncodingFromURL(InputStream stream) { 408 try { 409 byte[] arr = new byte[4096]; 410 int len = stream.read(arr, 0, arr.length); 411 String txt = new String (arr, 0, (len >= 0) ? len : 0).toUpperCase(); 412 413 return findEncoding(txt); 415 } catch (Exception x) { 416 x.printStackTrace(); 417 } 418 419 return null; 420 } 421 422 427 private static String findEncoding(String txt) { 428 int headLen = txt.indexOf("</HEAD>"); 430 if (headLen == -1) { 431 headLen = txt.length(); 432 } 433 434 int content = txt.indexOf("CONTENT-TYPE"); 436 if ((content == -1) || (content > headLen)) { 437 return null; 438 } 439 440 int charset = txt.indexOf("CHARSET=", content); 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); } 461 462 private class SwingBrowser extends JEditorPane { 464 private boolean lastPaintException = false; 465 466 private SwingBrowser() { 467 setEditable(false); 468 addHyperlinkListener( 469 new HyperlinkListener () { 470 public void hyperlinkUpdate(HyperlinkEvent 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 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 ex) { 482 LOG.log(Level.WARNING, null, ex); 483 } 484 } 485 } 486 } 487 } 488 ); 489 490 ActionMap actionMap = getActionMap(); 493 actionMap.put(DefaultEditorKit.upAction, new ScrollAction(-1)); 494 actionMap.put(DefaultEditorKit.downAction, new ScrollAction(1)); 495 } 496 497 512 protected InputStream getStream(URL page) throws IOException { 513 SwingUtilities.invokeLater(SwingBrowserImpl.this); 514 515 String charset = findEncodingFromURL(page.openStream()); 517 LOG.log(Level.FINE, "Url " + page + " has charset " + charset); 519 if (charset != null) { 520 putClientProperty("charset", charset); 521 } 522 523 if (Boolean.getBoolean("org.openide.awt.SwingBrowserImpl.do-not-block-awt")) { 525 setContentType("text/html"); 528 return new FilteredInputStream(page.openConnection(), SwingBrowserImpl.this); 529 } else { 530 return super.getStream(page); 531 } 532 } 533 534 public Dimension getPreferredSize() { 535 try { 536 return super.getPreferredSize(); 537 } catch (RuntimeException e) { 538 return new Dimension (400, 600); 540 } 541 } 542 543 public void paint(Graphics g) { 544 try { 545 super.paint(g); 546 lastPaintException = false; 547 } catch (RuntimeException e) { 548 if (!lastPaintException) { 551 repaint(); 552 } 553 554 lastPaintException = true; 555 } 556 } 557 558 561 private class ScrollAction extends AbstractAction { 562 int direction; 563 564 public ScrollAction(int direction) { 565 this.direction = direction; 566 } 567 568 public void actionPerformed(java.awt.event.ActionEvent e) { 569 Rectangle r = getVisibleRect(); 570 int increment = getScrollableUnitIncrement(r, SwingConstants.VERTICAL, direction); 571 r.y += (increment * direction); 572 scrollRectToVisible(r); 573 } 574 } 575 } 576 577 582 private static class FilteredInputStream extends FilterInputStream { 583 private final URLConnection conn; 584 private final SwingBrowserImpl browser; 585 586 FilteredInputStream(URLConnection conn, SwingBrowserImpl browser) { 587 super((FilterInputStream ) null); 588 this.conn = conn; 589 this.browser = browser; 590 } 591 592 private synchronized void openStream() throws IOException { 593 if (in == null) { 594 in = conn.getInputStream(); 595 } 596 } 597 598 public int available() throws IOException { 599 openStream(); 600 601 return super.available(); 602 } 603 604 public long skip(long n) throws IOException { 605 openStream(); 606 607 return super.skip(n); 608 } 609 610 public void reset() throws IOException { 611 openStream(); 612 super.reset(); 613 } 614 615 public void close() throws IOException { 616 openStream(); 617 super.close(); 618 Mutex.EVENT.readAccess(browser); 619 } 620 621 public int read(byte[] b) throws IOException { 622 openStream(); 623 624 return super.read(b); 625 } 626 627 public int read(byte[] b, int off, int len) throws IOException { 628 openStream(); 629 630 return super.read(b, off, len); 631 } 632 633 public int read() throws IOException { 634 openStream(); 635 636 return super.read(); 637 } 638 } 639 } 640 | Popular Tags |