Source for javax.swing.JEditorPane

   1: /* JEditorPane.java --
   2:    Copyright (C) 2002, 2004, 2005  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package javax.swing;
  40: 
  41: import java.awt.Dimension;
  42: import java.io.IOException;
  43: import java.io.InputStream;
  44: import java.io.InputStreamReader;
  45: import java.io.Reader;
  46: import java.io.StringReader;
  47: import java.net.MalformedURLException;
  48: import java.net.URL;
  49: import java.util.HashMap;
  50: 
  51: import javax.accessibility.AccessibleContext;
  52: import javax.accessibility.AccessibleHyperlink;
  53: import javax.accessibility.AccessibleHypertext;
  54: import javax.accessibility.AccessibleStateSet;
  55: import javax.accessibility.AccessibleText;
  56: import javax.swing.event.HyperlinkEvent;
  57: import javax.swing.event.HyperlinkListener;
  58: import javax.swing.text.BadLocationException;
  59: import javax.swing.text.DefaultEditorKit;
  60: import javax.swing.text.Document;
  61: import javax.swing.text.EditorKit;
  62: import javax.swing.text.Element;
  63: import javax.swing.text.JTextComponent;
  64: import javax.swing.text.View;
  65: import javax.swing.text.ViewFactory;
  66: import javax.swing.text.WrappedPlainView;
  67: import javax.swing.text.html.HTML;
  68: import javax.swing.text.html.HTMLDocument;
  69: import javax.swing.text.html.HTMLEditorKit;
  70: 
  71: /**
  72:  * A powerful text editor component that can handle different types of
  73:  * content.
  74:  *
  75:  * The JEditorPane text component is driven by an instance of
  76:  * {@link EditorKit}. The editor kit is responsible for providing
  77:  * a default {@link Document} implementation, a mechanism for loading
  78:  * and saving documents of its supported content type and providing
  79:  * a set of {@link Action}s for manipulating the content.
  80:  *
  81:  * By default the following content types are supported:
  82:  * <ul>
  83:  * <li><code>text/plain</code>: Plain text, handled by
  84:  *   {@link javax.swing.text.DefaultEditorKit}.</li>
  85:  * <li><code>text/html</code>: HTML 4.0 styled text, handled by
  86:  *   {@link javax.swing.text.html.HTMLEditorKit}.</li>
  87:  * <li><code>text/rtf</code>: RTF text, handled by
  88:  *   {@link javax.swing.text.rtf.RTFEditorKit}.</li>
  89:  * </ul>
  90:  *
  91:  * @author original author unknown
  92:  * @author Roman Kennke (roman@kennke.org)
  93:  * @author Anthony Balkissoon abalkiss at redhat dot com
  94:  */
  95: public class JEditorPane extends JTextComponent
  96: {
  97:   /**
  98:    * Provides accessibility support for <code>JEditorPane</code>.
  99:    *
 100:    * @author Roman Kennke (kennke@aicas.com)
 101:    */
 102:   protected class AccessibleJEditorPane extends AccessibleJTextComponent
 103:   {
 104: 
 105:     /**
 106:      * Creates a new <code>AccessibleJEditorPane</code> object.
 107:      */
 108:     protected AccessibleJEditorPane()
 109:     {
 110:       super();
 111:     }
 112: 
 113:     /**
 114:      * Returns a description of this <code>AccessibleJEditorPane</code>. If
 115:      * this property is not set, then this returns the content-type of the
 116:      * editor pane.
 117:      *
 118:      * @return a description of this AccessibleJEditorPane
 119:      */
 120:     public String getAccessibleDescription()
 121:     {
 122:       String descr = super.getAccessibleDescription(); 
 123:       if (descr == null)
 124:         return getContentType();
 125:       else
 126:         return descr;
 127:     }
 128: 
 129:     /**
 130:      * Returns the accessible state of this <code>AccessibleJEditorPane</code>.
 131:      *
 132:      * @return  the accessible state of this <code>AccessibleJEditorPane</code>
 133:      */
 134:     public AccessibleStateSet getAccessibleStateSet()
 135:     {
 136:       AccessibleStateSet state = super.getAccessibleStateSet();
 137:       // TODO: Figure out what state must be added here to the super's state.
 138:       return state;
 139:     }
 140:   }
 141: 
 142:   /**
 143:    * Provides accessibility support for <code>JEditorPane</code>s, when the
 144:    * editor kit is an instance of {@link HTMLEditorKit}.
 145:    *
 146:    * @author Roman Kennke (kennke@aicas.com)
 147:    */
 148:   protected class AccessibleJEditorPaneHTML extends AccessibleJEditorPane
 149:   {
 150:     /**
 151:      * Returns the accessible text of the <code>JEditorPane</code>. This will
 152:      * be an instance of
 153:      * {@link JEditorPaneAccessibleHypertextSupport}.
 154:      *
 155:      * @return the accessible text of the <code>JEditorPane</code>
 156:      */
 157:     public AccessibleText getAccessibleText()
 158:     {
 159:       return new JEditorPaneAccessibleHypertextSupport();
 160:     }
 161:   }
 162: 
 163:   /**
 164:    * This is the accessible text that is returned by
 165:    * {@link AccessibleJEditorPaneHTML#getAccessibleText()}.
 166:    *
 167:    * @author Roman Kennke (kennke@aicas.com)
 168:    */
 169:   protected class JEditorPaneAccessibleHypertextSupport
 170:     extends AccessibleJEditorPane implements AccessibleHypertext
 171:   {
 172: 
 173:     /**
 174:      * Creates a new JEditorPaneAccessibleHypertextSupport object.
 175:      */
 176:     public JEditorPaneAccessibleHypertextSupport()
 177:     {
 178:       super();
 179:     }
 180:     
 181:     /**
 182:      * The accessible representation of a HTML link. 
 183:      *
 184:      * @author Roman Kennke (kennke@aicas.com)
 185:      */
 186:     public class HTMLLink extends AccessibleHyperlink
 187:     {
 188: 
 189:       /**
 190:        * The element in the document that represents the link.
 191:        */
 192:       Element element;
 193: 
 194:       /**
 195:        * Creates a new <code>HTMLLink</code>.
 196:        *
 197:        * @param el the link element
 198:        */
 199:       public HTMLLink(Element el)
 200:       {
 201:         this.element = el;
 202:       }
 203: 
 204:       /**
 205:        * Returns <code>true</code> if this <code>HTMLLink</code> is still
 206:        * valid. A <code>HTMLLink</code> can become invalid when the document
 207:        * changes.
 208:        *
 209:        * @return <code>true</code> if this <code>HTMLLink</code> is still
 210:        *         valid
 211:        */
 212:       public boolean isValid()
 213:       {
 214:         // I test here if the element at our element's start offset is the
 215:         // same as the element in the document at this offset. If this is true,
 216:         // I consider the link valid, if not, then this link no longer
 217:         // represented by this HTMLLink and therefor invalid.
 218:         HTMLDocument doc = (HTMLDocument) getDocument();
 219:         return doc.getCharacterElement(element.getStartOffset()) == element;
 220:       }
 221: 
 222:       /**
 223:        * Returns the number of AccessibleActions in this link object. In
 224:        * general, link have 1 AccessibleAction associated with them. There are
 225:        * special cases where links can have multiple actions associated, like
 226:        * in image maps.
 227:        * 
 228:        * @return the number of AccessibleActions in this link object
 229:        */
 230:       public int getAccessibleActionCount()
 231:       {
 232:         // TODO: Implement the special cases.
 233:         return 1;
 234:       }
 235: 
 236:       /**
 237:        * Performs the specified action on the link object. This ususally means
 238:        * activating the link.
 239:        *
 240:        * @return <code>true</code> if the action has been performed
 241:        *         successfully, <code>false</code> otherwise
 242:        */
 243:       public boolean doAccessibleAction(int i)
 244:       {
 245:         String href = (String) element.getAttributes().getAttribute("href");
 246:         HTMLDocument doc = (HTMLDocument) getDocument();
 247:         try
 248:           {
 249:             URL url = new URL(doc.getBase(), href);
 250:             setPage(url);
 251:             String desc = doc.getText(element.getStartOffset(),
 252:                             element.getEndOffset() - element.getStartOffset());
 253:             HyperlinkEvent ev =
 254:               new HyperlinkEvent(JEditorPane.this,
 255:                                  HyperlinkEvent.EventType.ACTIVATED, url, desc,
 256:                                  element);
 257:             fireHyperlinkUpdate(ev);
 258:             return true;
 259:           }
 260:         catch (Exception ex)
 261:           {
 262:             return false;
 263:           }
 264:       }
 265: 
 266:       /**
 267:        * Returns the description of the action at action index <code>i</code>.
 268:        * This method returns the text within the element associated with this
 269:        * link.
 270:        *
 271:        * @param i the action index
 272:        *
 273:        * @return the description of the action at action index <code>i</code>
 274:        */
 275:       public String getAccessibleActionDescription(int i)
 276:       {
 277:         HTMLDocument doc = (HTMLDocument) getDocument();
 278:         try
 279:           {
 280:             return doc.getText(element.getStartOffset(),
 281:                             element.getEndOffset() - element.getStartOffset());
 282:           }
 283:         catch (BadLocationException ex)
 284:           {
 285:             throw (AssertionError)
 286:             new AssertionError("BadLocationException must not be thrown "
 287:                                + "here.")
 288:               .initCause(ex);
 289:           }
 290:       }
 291: 
 292:       /**
 293:        * Returns an {@link URL} object, that represents the action at action
 294:        * index <code>i</code>.
 295:        *
 296:        * @param i the action index
 297:        *
 298:        * @return an {@link URL} object, that represents the action at action
 299:        *         index <code>i</code>
 300:        */
 301:       public Object getAccessibleActionObject(int i)
 302:       {
 303:         String href = (String) element.getAttributes().getAttribute("href");
 304:         HTMLDocument doc = (HTMLDocument) getDocument();
 305:         try
 306:           {
 307:             URL url = new URL(doc.getBase(), href);
 308:             return url;
 309:           }
 310:         catch (MalformedURLException ex)
 311:           {
 312:             return null;
 313:           }
 314:       }
 315: 
 316:       /**
 317:        * Returns an object that represents the link anchor. For examples, if
 318:        * the link encloses a string, then a <code>String</code> object is
 319:        * returned, if the link encloses an &lt;img&gt; tag, then an
 320:        * <code>ImageIcon</code> object is returned.
 321:        *
 322:        * @return an object that represents the link anchor
 323:        */
 324:       public Object getAccessibleActionAnchor(int i)
 325:       {
 326:         // TODO: This is only the String case. Implement all cases.
 327:         return getAccessibleActionDescription(i);
 328:       }
 329: 
 330:       /**
 331:        * Returns the start index of the hyperlink element.
 332:        *
 333:        * @return the start index of the hyperlink element
 334:        */
 335:       public int getStartIndex()
 336:       {
 337:         return element.getStartOffset();
 338:       }
 339: 
 340:       /**
 341:        * Returns the end index of the hyperlink element.
 342:        *
 343:        * @return the end index of the hyperlink element
 344:        */
 345:       public int getEndIndex()
 346:       {
 347:         return element.getEndOffset();
 348:       }
 349:       
 350:     }
 351: 
 352:     /**
 353:      * Returns the number of hyperlinks in the document.
 354:      *
 355:      * @return the number of hyperlinks in the document
 356:      */
 357:     public int getLinkCount()
 358:     {
 359:       HTMLDocument doc = (HTMLDocument) getDocument();
 360:       HTMLDocument.Iterator linkIter = doc.getIterator(HTML.Tag.A);
 361:       int count = 0;
 362:       while (linkIter.isValid())
 363:         {
 364:           count++;
 365:           linkIter.next();
 366:         }
 367:       return count;
 368:     }
 369: 
 370:     /**
 371:      * Returns the <code>i</code>-th hyperlink in the document or
 372:      * <code>null</code> if there is no hyperlink with the specified index.
 373:      *
 374:      * @param i the index of the hyperlink to return
 375:      *
 376:      * @return the <code>i</code>-th hyperlink in the document or
 377:      *         <code>null</code> if there is no hyperlink with the specified
 378:      *         index
 379:      */
 380:     public AccessibleHyperlink getLink(int i)
 381:     {
 382:       HTMLDocument doc = (HTMLDocument) getDocument();
 383:       HTMLDocument.Iterator linkIter = doc.getIterator(HTML.Tag.A);
 384:       int count = 0;
 385:       while (linkIter.isValid())
 386:         {
 387:           count++;
 388:           if (count == i)
 389:             break;
 390:           linkIter.next();
 391:         }
 392:       if (linkIter.isValid())
 393:         {
 394:           int offset = linkIter.getStartOffset();
 395:           // TODO: I fetch the element for the link via getCharacterElement().
 396:           // I am not sure that this is correct, maybe we must use
 397:           // getParagraphElement()?
 398:           Element el = doc.getCharacterElement(offset);
 399:           HTMLLink link = new HTMLLink(el);
 400:           return link;
 401:         }
 402:       else
 403:         return null;
 404:     }
 405: 
 406:     /**
 407:      * Returns the index of the link element at the character position
 408:      * <code>c</code> within the document, or <code>-1</code> if there is no
 409:      * link at the specified position.
 410:      *
 411:      * @param c the character index from which to fetch the link index
 412:      *
 413:      * @return the index of the link element at the character position
 414:      *         <code>c</code> within the document, or <code>-1</code> if there
 415:      *         is no link at the specified position
 416:      */
 417:     public int getLinkIndex(int c)
 418:     {
 419:       HTMLDocument doc = (HTMLDocument) getDocument();
 420:       HTMLDocument.Iterator linkIter = doc.getIterator(HTML.Tag.A);
 421:       int count = 0;
 422:       while (linkIter.isValid())
 423:         {
 424:           if (linkIter.getStartOffset() <= c && linkIter.getEndOffset() > c)
 425:             break;
 426:           count++;
 427:           linkIter.next();
 428:         }
 429:       if (linkIter.isValid())
 430:         return count;
 431:       else
 432:         return -1;
 433:     }
 434: 
 435:     /**
 436:      * Returns the link text of the link at index <code>i</code>, or
 437:      * <code>null</code>, if there is no link at the specified position.
 438:      *
 439:      * @param i the index of the link
 440:      *
 441:      * @return  the link text of the link at index <code>i</code>, or
 442:      *          <code>null</code>, if there is no link at the specified
 443:      *          position
 444:      */
 445:     public String getLinkText(int i)
 446:     {
 447:       HTMLDocument doc = (HTMLDocument) getDocument();
 448:       HTMLDocument.Iterator linkIter = doc.getIterator(HTML.Tag.A);
 449:       int count = 0;
 450:       while (linkIter.isValid())
 451:         {
 452:           count++;
 453:           if (count == i)
 454:             break;
 455:           linkIter.next();
 456:         }
 457:       if (linkIter.isValid())
 458:         {
 459:           int offset = linkIter.getStartOffset();
 460:           // TODO: I fetch the element for the link via getCharacterElement().
 461:           // I am not sure that this is correct, maybe we must use
 462:           // getParagraphElement()?
 463:           Element el = doc.getCharacterElement(offset);
 464:           try
 465:             {
 466:               String text = doc.getText(el.getStartOffset(),
 467:                                       el.getEndOffset() - el.getStartOffset());
 468:               return text;
 469:             }
 470:           catch (BadLocationException ex)
 471:             {
 472:               throw (AssertionError)
 473:                 new AssertionError("BadLocationException must not be thrown "
 474:                                    + "here.")
 475:                   .initCause(ex);
 476:             }
 477:         }
 478:       else
 479:         return null;
 480:     }
 481:   }
 482: 
 483:   /**
 484:    * An EditorKit used for plain text. This is the default editor kit for
 485:    * JEditorPanes.
 486:    *
 487:    * @author Roman Kennke (kennke@aicas.com)
 488:    */
 489:   private static class PlainEditorKit extends DefaultEditorKit
 490:   {
 491: 
 492:     /**
 493:      * Returns a ViewFactory that supplies WrappedPlainViews.
 494:      */
 495:     public ViewFactory getViewFactory()
 496:     {
 497:       return new ViewFactory()
 498:       {
 499:         public View create(Element el)
 500:         {
 501:           return new WrappedPlainView(el);
 502:         }
 503:       };
 504:     }
 505:   }
 506: 
 507:   private static final long serialVersionUID = 3140472492599046285L;
 508:   
 509:   private URL page;
 510:   private EditorKit editorKit;
 511:   
 512:   boolean focus_root;
 513:   
 514:   // A mapping between content types and registered EditorKit types
 515:   static HashMap registerMap;
 516:   
 517:   // A mapping between content types and used EditorKits
 518:   HashMap editorMap;  
 519: 
 520:   public JEditorPane()
 521:   {
 522:     init();
 523:     setEditorKit(createDefaultEditorKit());
 524:   }
 525: 
 526:   public JEditorPane(String url) throws IOException
 527:   {
 528:     this(new URL(url));
 529:   }
 530: 
 531:   public JEditorPane(String type, String text)
 532:   {
 533:     init();
 534:     setEditorKit(createEditorKitForContentType(type));
 535:     setText(text);
 536:   }
 537: 
 538:   public JEditorPane(URL url) throws IOException
 539:   {
 540:     init ();
 541:     setEditorKit (createEditorKitForContentType("text/html"));;
 542:     setPage(url);
 543:   }
 544:   
 545:   /**
 546:    * Called by the constructors to set up the default bindings for content 
 547:    * types and EditorKits.
 548:    */
 549:   void init()
 550:   {
 551:     editorMap = new HashMap();
 552:     registerMap = new HashMap();
 553:     registerEditorKitForContentType("application/rtf",
 554:                                     "javax.swing.text.rtf.RTFEditorKit");
 555:     registerEditorKitForContentType("text/plain",
 556:                                     "javax.swing.JEditorPane$PlainEditorKit");
 557:     registerEditorKitForContentType("text/html",
 558:                                     "javax.swing.text.html.HTMLEditorKit");
 559:     registerEditorKitForContentType("text/rtf",
 560:                                     "javax.swing.text.rtf.RTFEditorKit");
 561:   }
 562: 
 563:   protected EditorKit createDefaultEditorKit()
 564:   {
 565:     return new PlainEditorKit();
 566:   }
 567: 
 568:   /**
 569:    * Creates and returns an EditorKit that is appropriate for the given 
 570:    * content type.  This is created using the default recognized types
 571:    * plus any EditorKit types that have been registered.
 572:    * 
 573:    * @see #registerEditorKitForContentType(String, String)
 574:    * @see #registerEditorKitForContentType(String, String, ClassLoader)
 575:    * @param type the content type
 576:    * @return an EditorKit for use with the given content type
 577:    */
 578:   public static EditorKit createEditorKitForContentType(String type)
 579:   {
 580:     // TODO: Have to handle the case where a ClassLoader was specified
 581:     // when the EditorKit was registered
 582:     EditorKit e = null;
 583:     String className = (String)registerMap.get(type);
 584:     if (className != null)
 585:       {
 586:         try
 587:         {
 588:           e = (EditorKit) Class.forName(className).newInstance();
 589:         }
 590:         catch (Exception e2)
 591:         {    
 592:           // TODO: Not sure what to do here.
 593:         }
 594:       }
 595:     return e;
 596:   }
 597: 
 598:   /**
 599:    * Sends a given <code>HyperlinkEvent</code> to all registered listeners.
 600:    *
 601:    * @param event the event to send
 602:    */
 603:   public void fireHyperlinkUpdate(HyperlinkEvent event)
 604:   {
 605:     HyperlinkListener[] listeners = getHyperlinkListeners();
 606: 
 607:     for (int index = 0; index < listeners.length; ++index)
 608:        listeners[index].hyperlinkUpdate(event);
 609:   }
 610: 
 611:   /**
 612:    * Returns the accessible context associated with this editor pane.
 613:    *
 614:    * @return the accessible context associated with this editor pane
 615:    */
 616:   public AccessibleContext getAccessibleContext()
 617:   {
 618:     if (accessibleContext == null)
 619:       {
 620:         if (getEditorKit() instanceof HTMLEditorKit)
 621:           accessibleContext = new AccessibleJEditorPaneHTML();
 622:         else
 623:           accessibleContext = new AccessibleJEditorPane();
 624:       }
 625:     return accessibleContext;
 626:   }
 627: 
 628:   public final String getContentType()
 629:   {
 630:     return getEditorKit().getContentType();
 631:   }
 632: 
 633:   /**
 634:    * Returns the EditorKit. If there is no EditorKit set this method
 635:    * calls createDefaultEditorKit() and setEditorKit() first.
 636:    */
 637:   public EditorKit getEditorKit()
 638:   {
 639:     if (editorKit == null)
 640:       setEditorKit(createDefaultEditorKit());
 641:     return editorKit;
 642:   }
 643: 
 644:   /**
 645:    * Returns the class name of the EditorKit associated with the given
 646:    * content type.
 647:    * 
 648:    * @since 1.3
 649:    * @param type the content type
 650:    * @return the class name of the EditorKit associated with this content type
 651:    */
 652:   public static String getEditorKitClassNameForContentType(String type)
 653:   {
 654:     return (String) registerMap.get(type);
 655:   }
 656: 
 657:   /**
 658:    * Returns the EditorKit to use for the given content type.  If an
 659:    * EditorKit has been explicitly set via 
 660:    * <code>setEditorKitForContentType</code>
 661:    * then it will be returned.  Otherwise an attempt will be made to create
 662:    * an EditorKit from the default recognzied content types or any
 663:    * EditorKits that have been registered.  If none can be created, a
 664:    * PlainEditorKit is created.
 665:    * 
 666:    * @see #registerEditorKitForContentType(String, String)
 667:    * @see #registerEditorKitForContentType(String, String, ClassLoader)
 668:    * @param type the content type
 669:    * @return an appropriate EditorKit for the given content type
 670:    */
 671:   public EditorKit getEditorKitForContentType(String type)
 672:   {
 673:     // First check if an EditorKit has been explicitly set.
 674:     EditorKit e = (EditorKit) editorMap.get(type);
 675:     // Then check to see if we can create one.
 676:     if (e == null)
 677:       e = createEditorKitForContentType(type);
 678:     // Otherwise default to PlainEditorKit.
 679:     if (e == null)
 680:       e = new PlainEditorKit();
 681:     return e;
 682:   }
 683: 
 684:   /**
 685:    * Returns the preferred size for the JEditorPane.  
 686:    */
 687:   public Dimension getPreferredSize()
 688:   {
 689:     return super.getPreferredSize();
 690:   }
 691: 
 692:   public boolean getScrollableTracksViewportHeight()
 693:   {
 694:   /*  Container parent = getParent();
 695:     return (parent instanceof JViewport &&
 696:         parent.isValid());*/
 697:     return isValid();
 698:   }
 699: 
 700:   public boolean getScrollableTracksViewportWidth()
 701:   {
 702:     /*Container parent = getParent();
 703:     return (parent instanceof JViewport &&
 704:         parent.isValid());*/
 705:     return isValid();
 706:   }
 707: 
 708:   public URL getPage()
 709:   {
 710:     return page;
 711:   }
 712: 
 713:   protected InputStream getStream(URL page)
 714:     throws IOException
 715:   {
 716:     return page.openStream();
 717:   }
 718: 
 719:   public String getText()
 720:   {
 721:     return super.getText();
 722:   }
 723: 
 724:   public String getUIClassID()
 725:   {
 726:     return "EditorPaneUI";
 727:   }
 728: 
 729:   public boolean isFocusCycleRoot()
 730:   {
 731:     return focus_root;
 732:   }
 733: 
 734:   protected String paramString()
 735:   {
 736:     return "JEditorPane";
 737:   }
 738: 
 739:   /**
 740:    * This method initializes from a stream. 
 741:    */
 742:   public void read(InputStream in, Object desc) throws IOException
 743:   {
 744:     EditorKit kit = getEditorKit();
 745:     if (kit instanceof HTMLEditorKit && desc instanceof HTMLDocument)
 746:       {
 747:         Document doc = (Document) desc;
 748:         try
 749:           {
 750:             kit.read(in, doc, 0);
 751:           }
 752:         catch (BadLocationException ex)
 753:           {
 754:             assert false : "BadLocationException must not be thrown here.";
 755:           }
 756:       }
 757:     else
 758:       {
 759:         Reader inRead = new InputStreamReader(in);
 760:         super.read(inRead, desc);
 761:       }
 762:   }
 763: 
 764:   /**
 765:    * Establishes a binding between type and classname.  This enables
 766:    * us to create an EditorKit later for the given content type.
 767:    * 
 768:    * @param type the content type
 769:    * @param classname the name of the class that is associated with this 
 770:    * content type
 771:    */
 772:   public static void registerEditorKitForContentType(String type,
 773:                                                      String classname)
 774:   {
 775:     registerMap.put(type, classname);
 776:   }
 777: 
 778:   /**
 779:    * Establishes the default bindings of type to classname.
 780:    */
 781:   public static void registerEditorKitForContentType(String type,
 782:                                                      String classname,
 783:                                                      ClassLoader loader)
 784:   {
 785:     // TODO: Implement this properly.
 786:   }
 787: 
 788:   /**
 789:    * Replaces the currently selected content with new content represented
 790:    * by the given string.
 791:    */
 792:   public void replaceSelection(String content)
 793:   {
 794:     // TODO: Implement this properly.
 795:     super.replaceSelection(content);
 796:   }
 797: 
 798:   /**
 799:    * Scrolls the view to the given reference location (that is, the value
 800:    * returned by the UL.getRef method for the URL being displayed).
 801:    */
 802:   public void scrollToReference(String reference)
 803:   {
 804:     // TODO: Implement this properly.
 805:   }
 806: 
 807:   public final void setContentType(String type)
 808:   {
 809:     if (editorKit != null
 810:     && editorKit.getContentType().equals(type))
 811:       return;
 812:               
 813:     EditorKit kit = getEditorKitForContentType(type);
 814:             
 815:     if (kit != null)
 816:       setEditorKit(kit);
 817:   }
 818: 
 819:   public void setEditorKit(EditorKit newValue)
 820:   {
 821:     if (editorKit == newValue)
 822:       return;
 823:         
 824:     if (editorKit != null)
 825:       editorKit.deinstall(this);
 826:                 
 827:     EditorKit oldValue = editorKit;
 828:     editorKit = newValue;
 829:                     
 830:     if (editorKit != null)
 831:       {
 832:     editorKit.install(this);
 833:     setDocument(editorKit.createDefaultDocument());
 834:       }
 835:                             
 836:     firePropertyChange("editorKit", oldValue, newValue);
 837:     invalidate();
 838:     repaint();
 839:     // Reset the accessibleContext since this depends on the editorKit.
 840:     accessibleContext = null;
 841:   }
 842: 
 843:   /**
 844:    * Explicitly sets an EditorKit to be used for the given content type.
 845:    * @param type the content type
 846:    * @param k the EditorKit to use for the given content type
 847:    */
 848:   public void setEditorKitForContentType(String type, EditorKit k)
 849:   {
 850:     editorMap.put(type, k);
 851:   }
 852: 
 853:   /**
 854:    * Sets the current URL being displayed.  
 855:    */
 856:   public void setPage(String url) throws IOException
 857:   {
 858:     setPage(new URL(url));
 859:   }
 860: 
 861:   /**
 862:    * Sets the current URL being displayed.  
 863:    */
 864:   public void setPage(URL page) throws IOException
 865:   {
 866:     if (page == null)
 867:       throw new IOException("invalid url");
 868: 
 869:     try
 870:       {
 871:     this.page = page;
 872:     getEditorKit().read(page.openStream(), getDocument(), 0);
 873:       }
 874:     catch (BadLocationException e)
 875:       {
 876:     // Ignored. '0' is always a valid offset.
 877:       }
 878:   }
 879: 
 880:   /**
 881:    * Sets the text of the JEditorPane.  The argument <code>t</code>
 882:    * is expected to be in the format of the current EditorKit.  This removes
 883:    * the content of the current document and uses the EditorKit to read in the
 884:    * new text.  This allows the EditorKit to handle the String rather than just
 885:    * inserting in plain text.
 886:    * 
 887:    * @param t the text to display in this JEditorPane
 888:    */
 889:   public void setText(String t)
 890:   {
 891:     try
 892:     {
 893:       // Remove the current content.
 894:       Document doc = getDocument();
 895:       doc.remove(0, doc.getLength());
 896:       if (t == null || t == "")
 897:         return;
 898:       
 899:       // Let the EditorKit read the text into the Document.
 900:       getEditorKit().read(new StringReader(t), doc, 0);
 901:     }
 902:     catch (BadLocationException ble)
 903:     {
 904:       // TODO: Don't know what to do here.
 905:     }
 906:     catch (IOException ioe)
 907:     {
 908:       // TODO: Don't know what to do here.
 909:     }
 910:   }
 911: 
 912:   /**
 913:    * Add a <code>HyperlinkListener</code> object to this editor pane.
 914:    *
 915:    * @param listener the listener to add
 916:    */
 917:   public void addHyperlinkListener(HyperlinkListener listener)
 918:   {
 919:     listenerList.add(HyperlinkListener.class, listener);
 920:   }
 921: 
 922:   /**
 923:    * Removes a <code>HyperlinkListener</code> object to this editor pane.
 924:    *
 925:    * @param listener the listener to remove
 926:    */
 927:   public void removeHyperlinkListener(HyperlinkListener listener)
 928:   {
 929:     listenerList.remove(HyperlinkListener.class, listener);
 930:   }
 931: 
 932:   /**
 933:    * Returns all added <code>HyperlinkListener</code> objects.
 934:    *
 935:    * @return array of listeners
 936:    *
 937:    * @since 1.4
 938:    */
 939:   public HyperlinkListener[] getHyperlinkListeners()
 940:   {
 941:     return (HyperlinkListener[]) getListeners(HyperlinkListener.class);
 942:   }
 943: }