< prev index next >

src/java.desktop/share/classes/javax/swing/JEditorPane.java

Print this page




  32 import java.net.*;
  33 import java.util.*;
  34 import java.io.*;
  35 import java.util.*;
  36 
  37 import javax.swing.plaf.*;
  38 import javax.swing.text.*;
  39 import javax.swing.event.*;
  40 import javax.swing.text.html.*;
  41 import javax.accessibility.*;
  42 import sun.reflect.misc.ReflectUtil;
  43 
  44 /**
  45  * A text component to edit various kinds of content.
  46  * You can find how-to information and examples of using editor panes in
  47  * <a href="http://docs.oracle.com/javase/tutorial/uiswing/components/text.html">Using Text Components</a>,
  48  * a section in <em>The Java Tutorial.</em>
  49  *
  50  * <p>
  51  * This component uses implementations of the
  52  * <code>EditorKit</code> to accomplish its behavior. It effectively
  53  * morphs into the proper kind of text editor for the kind
  54  * of content it is given.  The content type that editor is bound
  55  * to at any given time is determined by the <code>EditorKit</code> currently
  56  * installed.  If the content is set to a new URL, its type is used
  57  * to determine the <code>EditorKit</code> that should be used to
  58  * load the content.
  59  * <p>
  60  * By default, the following types of content are known:
  61  * <dl>
  62  * <dt><b>text/plain</b>
  63  * <dd>Plain text, which is the default the type given isn't
  64  * recognized.  The kit used in this case is an extension of
  65  * <code>DefaultEditorKit</code> that produces a wrapped plain text view.
  66  * <dt><b>text/html</b>
  67  * <dd>HTML text.  The kit used in this case is the class
  68  * <code>javax.swing.text.html.HTMLEditorKit</code>
  69  * which provides HTML 3.2 support.
  70  * <dt><b>text/rtf</b>
  71  * <dd>RTF text.  The kit used in this case is the class
  72  * <code>javax.swing.text.rtf.RTFEditorKit</code>
  73  * which provides a limited support of the Rich Text Format.
  74  * </dl>
  75  * <p>
  76  * There are several ways to load content into this component.
  77  * <ol>
  78  * <li>
  79  * The {@link #setText setText} method can be used to initialize
  80  * the component from a string.  In this case the current
  81  * <code>EditorKit</code> will be used, and the content type will be
  82  * expected to be of this type.
  83  * <li>
  84  * The {@link #read read} method can be used to initialize the
  85  * component from a <code>Reader</code>.  Note that if the content type is HTML,
  86  * relative references (e.g. for things like images) can't be resolved
  87  * unless the &lt;base&gt; tag is used or the <em>Base</em> property
  88  * on <code>HTMLDocument</code> is set.
  89  * In this case the current <code>EditorKit</code> will be used,
  90  * and the content type will be expected to be of this type.
  91  * <li>
  92  * The {@link #setPage setPage} method can be used to initialize
  93  * the component from a URL.  In this case, the content type will be
  94  * determined from the URL, and the registered <code>EditorKit</code>
  95  * for that content type will be set.
  96  * </ol>
  97  * <p>
  98  * Some kinds of content may provide hyperlink support by generating
  99  * hyperlink events.  The HTML <code>EditorKit</code> will generate
 100  * hyperlink events if the <code>JEditorPane</code> is <em>not editable</em>
 101  * (<code>JEditorPane.setEditable(false);</code> has been called).
 102  * If HTML frames are embedded in the document, the typical response would be
 103  * to change a portion of the current document.  The following code
 104  * fragment is a possible hyperlink listener implementation, that treats
 105  * HTML frame events specially, and simply displays any other activated
 106  * hyperlinks.
 107  * <pre>
 108 
 109 &nbsp;    class Hyperactive implements HyperlinkListener {
 110 &nbsp;
 111 &nbsp;        public void hyperlinkUpdate(HyperlinkEvent e) {
 112 &nbsp;            if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
 113 &nbsp;                JEditorPane pane = (JEditorPane) e.getSource();
 114 &nbsp;                if (e instanceof HTMLFrameHyperlinkEvent) {
 115 &nbsp;                    HTMLFrameHyperlinkEvent  evt = (HTMLFrameHyperlinkEvent)e;
 116 &nbsp;                    HTMLDocument doc = (HTMLDocument)pane.getDocument();
 117 &nbsp;                    doc.processHTMLFrameHyperlinkEvent(evt);
 118 &nbsp;                } else {
 119 &nbsp;                    try {
 120 &nbsp;                        pane.setPage(e.getURL());
 121 &nbsp;                    } catch (Throwable t) {
 122 &nbsp;                        t.printStackTrace();
 123 &nbsp;                    }
 124 &nbsp;                }
 125 &nbsp;            }
 126 &nbsp;        }
 127 &nbsp;    }
 128 
 129  * </pre>
 130  * <p>
 131  * For information on customizing how <b>text/html</b> is rendered please see
 132  * {@link #W3C_LENGTH_UNITS} and {@link #HONOR_DISPLAY_PROPERTIES}
 133  * <p>
 134  * Culturally dependent information in some documents is handled through
 135  * a mechanism called character encoding.  Character encoding is an
 136  * unambiguous mapping of the members of a character set (letters, ideographs,
 137  * digits, symbols, or control functions) to specific numeric code values. It
 138  * represents the way the file is stored. Example character encodings are
 139  * ISO-8859-1, ISO-8859-5, Shift-jis, Euc-jp, and UTF-8. When the file is
 140  * passed to an user agent (<code>JEditorPane</code>) it is converted to
 141  * the document character set (ISO-10646 aka Unicode).
 142  * <p>
 143  * There are multiple ways to get a character set mapping to happen
 144  * with <code>JEditorPane</code>.
 145  * <ol>
 146  * <li>
 147  * One way is to specify the character set as a parameter of the MIME
 148  * type.  This will be established by a call to the
 149  * {@link #setContentType setContentType} method.  If the content
 150  * is loaded by the {@link #setPage setPage} method the content
 151  * type will have been set according to the specification of the URL.
 152  * It the file is loaded directly, the content type would be expected to
 153  * have been set prior to loading.
 154  * <li>
 155  * Another way the character set can be specified is in the document itself.
 156  * This requires reading the document prior to determining the character set
 157  * that is desired.  To handle this, it is expected that the
 158  * <code>EditorKit</code>.read operation throw a
 159  * <code>ChangedCharSetException</code> which will
 160  * be caught.  The read is then restarted with a new Reader that uses
 161  * the character set specified in the <code>ChangedCharSetException</code>
 162  * (which is an <code>IOException</code>).
 163  * </ol>
 164  *
 165  * <dl>
 166  * <dt><b>Newlines</b>
 167  * <dd>
 168  * For a discussion on how newlines are handled, see
 169  * <a href="text/DefaultEditorKit.html">DefaultEditorKit</a>.
 170  * </dl>
 171  *
 172  * <p>
 173  * <strong>Warning:</strong> Swing is not thread safe. For more
 174  * information see <a
 175  * href="package-summary.html#threading">Swing's Threading
 176  * Policy</a>.
 177  * <p>
 178  * <strong>Warning:</strong>
 179  * Serialized objects of this class will not be compatible with
 180  * future Swing releases. The current serialization support is
 181  * appropriate for short term storage or RMI between applications running
 182  * the same version of Swing.  As of 1.4, support for long term storage
 183  * of all JavaBeans&trade;
 184  * has been added to the <code>java.beans</code> package.
 185  * Please see {@link java.beans.XMLEncoder}.
 186  *
 187  * @beaninfo
 188  *   attribute: isContainer false
 189  * description: A text component to edit various types of content.
 190  *
 191  * @author  Timothy Prinzing
 192  * @since 1.2
 193  */
 194 @SuppressWarnings("serial") // Same-version serialization only
 195 public class JEditorPane extends JTextComponent {
 196 
 197     /**
 198      * Creates a new <code>JEditorPane</code>.
 199      * The document model is set to <code>null</code>.
 200      */
 201     public JEditorPane() {
 202         super();
 203         setFocusCycleRoot(true);
 204         setFocusTraversalPolicy(new LayoutFocusTraversalPolicy() {
 205                 public Component getComponentAfter(Container focusCycleRoot,
 206                                                    Component aComponent) {
 207                     if (focusCycleRoot != JEditorPane.this ||
 208                         (!isEditable() && getComponentCount() > 0)) {
 209                         return super.getComponentAfter(focusCycleRoot,
 210                                                        aComponent);
 211                     } else {
 212                         Container rootAncestor = getFocusCycleRootAncestor();
 213                         return (rootAncestor != null)
 214                             ? rootAncestor.getFocusTraversalPolicy().
 215                                   getComponentAfter(rootAncestor,
 216                                                     JEditorPane.this)
 217                             : null;
 218                     }
 219                 }


 239                         ? super.getDefaultComponent(focusCycleRoot)
 240                         : null;
 241                 }
 242                 protected boolean accept(Component aComponent) {
 243                     return (aComponent != JEditorPane.this)
 244                         ? super.accept(aComponent)
 245                         : false;
 246                 }
 247             });
 248         LookAndFeel.installProperty(this,
 249                                     "focusTraversalKeysForward",
 250                                     JComponent.
 251                                     getManagingFocusForwardTraversalKeys());
 252         LookAndFeel.installProperty(this,
 253                                     "focusTraversalKeysBackward",
 254                                     JComponent.
 255                                     getManagingFocusBackwardTraversalKeys());
 256     }
 257 
 258     /**
 259      * Creates a <code>JEditorPane</code> based on a specified URL for input.
 260      *
 261      * @param initialPage the URL
 262      * @exception IOException if the URL is <code>null</code>
 263      *          or cannot be accessed
 264      */
 265     public JEditorPane(URL initialPage) throws IOException {
 266         this();
 267         setPage(initialPage);
 268     }
 269 
 270     /**
 271      * Creates a <code>JEditorPane</code> based on a string containing
 272      * a URL specification.
 273      *
 274      * @param url the URL
 275      * @exception IOException if the URL is <code>null</code> or
 276      *          cannot be accessed
 277      */
 278     public JEditorPane(String url) throws IOException {
 279         this();
 280         setPage(url);
 281     }
 282 
 283     /**
 284      * Creates a <code>JEditorPane</code> that has been initialized
 285      * to the given text.  This is a convenience constructor that calls the
 286      * <code>setContentType</code> and <code>setText</code> methods.
 287      *
 288      * @param type mime type of the given text
 289      * @param text the text to initialize with; may be <code>null</code>
 290      * @exception NullPointerException if the <code>type</code> parameter
 291      *          is <code>null</code>
 292      */
 293     public JEditorPane(String type, String text) {
 294         this();
 295         setContentType(type);
 296         setText(text);
 297     }
 298 
 299     /**
 300      * Adds a hyperlink listener for notification of any changes, for example
 301      * when a link is selected and entered.
 302      *
 303      * @param listener the listener
 304      */
 305     public synchronized void addHyperlinkListener(HyperlinkListener listener) {
 306         listenerList.add(HyperlinkListener.class, listener);
 307     }
 308 
 309     /**
 310      * Removes a hyperlink listener.
 311      *
 312      * @param listener the listener
 313      */
 314     public synchronized void removeHyperlinkListener(HyperlinkListener listener) {
 315         listenerList.remove(HyperlinkListener.class, listener);
 316     }
 317 
 318     /**
 319      * Returns an array of all the <code>HyperLinkListener</code>s added
 320      * to this JEditorPane with addHyperlinkListener().
 321      *
 322      * @return all of the <code>HyperLinkListener</code>s added or an empty
 323      *         array if no listeners have been added
 324      * @since 1.4
 325      */
 326     public synchronized HyperlinkListener[] getHyperlinkListeners() {
 327         return listenerList.getListeners(javax.swing.event.HyperlinkListener.class);
 328     }
 329 
 330     /**
 331      * Notifies all listeners that have registered interest for
 332      * notification on this event type.  This is normally called
 333      * by the currently installed <code>EditorKit</code> if a content type
 334      * that supports hyperlinks is currently active and there
 335      * was activity with a link.  The listener list is processed
 336      * last to first.
 337      *
 338      * @param e the event
 339      * @see EventListenerList
 340      */
 341     public void fireHyperlinkUpdate(HyperlinkEvent e) {
 342         // Guaranteed to return a non-null array
 343         Object[] listeners = listenerList.getListenerList();
 344         // Process the listeners last to first, notifying
 345         // those that are interested in this event
 346         for (int i = listeners.length-2; i>=0; i-=2) {
 347             if (listeners[i]==HyperlinkListener.class) {
 348                 ((HyperlinkListener)listeners[i+1]).hyperlinkUpdate(e);
 349             }
 350         }
 351     }
 352 
 353 
 354     /**
 355      * Sets the current URL being displayed.  The content type of the
 356      * pane is set, and if the editor kit for the pane is
 357      * non-<code>null</code>, then
 358      * a new default document is created and the URL is read into it.
 359      * If the URL contains and reference location, the location will
 360      * be scrolled to by calling the <code>scrollToReference</code>
 361      * method. If the desired URL is the one currently being displayed,
 362      * the document will not be reloaded. To force a document
 363      * reload it is necessary to clear the stream description property
 364      * of the document. The following code shows how this can be done:
 365      *
 366      * <pre>
 367      *   Document doc = jEditorPane.getDocument();
 368      *   doc.putProperty(Document.StreamDescriptionProperty, null);
 369      * </pre>
 370      *
 371      * If the desired URL is not the one currently being
 372      * displayed, the <code>getStream</code> method is called to
 373      * give subclasses control over the stream provided.
 374      * <p>
 375      * This may load either synchronously or asynchronously
 376      * depending upon the document returned by the <code>EditorKit</code>.
 377      * If the <code>Document</code> is of type
 378      * <code>AbstractDocument</code> and has a value returned by
 379      * <code>AbstractDocument.getAsynchronousLoadPriority</code>
 380      * that is greater than or equal to zero, the page will be
 381      * loaded on a separate thread using that priority.
 382      * <p>
 383      * If the document is loaded synchronously, it will be
 384      * filled in with the stream prior to being installed into
 385      * the editor with a call to <code>setDocument</code>, which
 386      * is bound and will fire a property change event.  If an
 387      * <code>IOException</code> is thrown the partially loaded
 388      * document will
 389      * be discarded and neither the document or page property
 390      * change events will be fired.  If the document is
 391      * successfully loaded and installed, a view will be
 392      * built for it by the UI which will then be scrolled if
 393      * necessary, and then the page property change event
 394      * will be fired.
 395      * <p>
 396      * If the document is loaded asynchronously, the document
 397      * will be installed into the editor immediately using a
 398      * call to <code>setDocument</code> which will fire a
 399      * document property change event, then a thread will be
 400      * created which will begin doing the actual loading.
 401      * In this case, the page property change event will not be
 402      * fired by the call to this method directly, but rather will be
 403      * fired when the thread doing the loading has finished.
 404      * It will also be fired on the event-dispatch thread.
 405      * Since the calling thread can not throw an <code>IOException</code>
 406      * in the event of failure on the other thread, the page
 407      * property change event will be fired when the other
 408      * thread is done whether the load was successful or not.
 409      *
 410      * @param page the URL of the page
 411      * @exception IOException for a <code>null</code> or invalid
 412      *          page specification, or exception from the stream being read
 413      * @see #getPage
 414      * @beaninfo
 415      *  description: the URL used to set content
 416      *        bound: true
 417      *       expert: true
 418      */
 419     public void setPage(URL page) throws IOException {
 420         if (page == null) {
 421             throw new IOException("invalid url");
 422         }
 423         URL loaded = getPage();
 424 
 425 
 426         // reset scrollbar
 427         if (!page.equals(loaded) && page.getRef() == null) {
 428             scrollRectToVisible(new Rectangle(0,0,1,1));
 429         }
 430         boolean reloaded = false;
 431         Object postData = getPostData();


 501                 doc.putProperty(key, pageProperties.get(key));
 502             }
 503             pageProperties.clear();
 504         }
 505         if (doc.getProperty(Document.StreamDescriptionProperty) == null) {
 506             doc.putProperty(Document.StreamDescriptionProperty, page);
 507         }
 508         return doc;
 509     }
 510 
 511     /**
 512      * Return load priority for the document or -1 if priority not supported.
 513      */
 514     private int getAsynchronousLoadPriority(Document doc) {
 515         return (doc instanceof AbstractDocument ?
 516             ((AbstractDocument) doc).getAsynchronousLoadPriority() : -1);
 517     }
 518 
 519     /**
 520      * This method initializes from a stream.  If the kit is
 521      * set to be of type <code>HTMLEditorKit</code>, and the
 522      * <code>desc</code> parameter is an <code>HTMLDocument</code>,
 523      * then it invokes the <code>HTMLEditorKit</code> to initiate
 524      * the read. Otherwise it calls the superclass
 525      * method which loads the model as plain text.
 526      *
 527      * @param in the stream from which to read
 528      * @param desc an object describing the stream
 529      * @exception IOException as thrown by the stream being
 530      *          used to initialize
 531      * @see JTextComponent#read
 532      * @see #setDocument
 533      */
 534     public void read(InputStream in, Object desc) throws IOException {
 535 
 536         if (desc instanceof HTMLDocument &&
 537             kit instanceof HTMLEditorKit) {
 538             HTMLDocument hdoc = (HTMLDocument) desc;
 539             setDocument(hdoc);
 540             read(in, hdoc);
 541         } else {
 542             String charset = (String) getClientProperty("charset");
 543             Reader r = (charset != null) ? new InputStreamReader(in, charset) :
 544                 new InputStreamReader(in);
 545             super.read(r, desc);
 546         }
 547     }
 548 
 549 
 550     /**
 551      * This method invokes the <code>EditorKit</code> to initiate a
 552      * read.  In the case where a <code>ChangedCharSetException</code>
 553      * is thrown this exception will contain the new CharSet.
 554      * Therefore the <code>read</code> operation
 555      * is then restarted after building a new Reader with the new charset.
 556      *
 557      * @param in the inputstream to use
 558      * @param doc the document to load
 559      *
 560      */
 561     void read(InputStream in, Document doc) throws IOException {
 562         if (! Boolean.TRUE.equals(doc.getProperty("IgnoreCharsetDirective"))) {
 563             final int READ_LIMIT = 1024 * 10;
 564             in = new BufferedInputStream(in, READ_LIMIT);
 565             in.mark(READ_LIMIT);
 566         }
 567         try {
 568             String charset = (String) getClientProperty("charset");
 569             Reader r = (charset != null) ? new InputStreamReader(in, charset) :
 570                 new InputStreamReader(in);
 571             kit.read(r, doc, 0);
 572         } catch (BadLocationException e) {
 573             throw new IOException(e.getMessage());
 574         } catch (ChangedCharSetException changedCharSetException) {


 693         /**
 694          * URL of the old page that was replaced (for the property change event)
 695          */
 696         URL old;
 697 
 698         /**
 699          * URL of the page being loaded (for the property change event)
 700          */
 701         URL page;
 702 
 703         /**
 704          * The Document instance to load into. This is cached in case a
 705          * new Document is created between the time the thread this is created
 706          * and run.
 707          */
 708         Document doc;
 709     }
 710 
 711     /**
 712      * Fetches a stream for the given URL, which is about to
 713      * be loaded by the <code>setPage</code> method.  By
 714      * default, this simply opens the URL and returns the
 715      * stream.  This can be reimplemented to do useful things
 716      * like fetch the stream from a cache, monitor the progress
 717      * of the stream, etc.
 718      * <p>
 719      * This method is expected to have the side effect of
 720      * establishing the content type, and therefore setting the
 721      * appropriate <code>EditorKit</code> to use for loading the stream.
 722      * <p>
 723      * If this the stream was an http connection, redirects
 724      * will be followed and the resulting URL will be set as
 725      * the <code>Document.StreamDescriptionProperty</code> so that relative
 726      * URL's can be properly resolved.
 727      *
 728      * @param page  the URL of the page
 729      * @return a stream for the URL which is about to be loaded
 730      * @throws IOException if an I/O problem occurs
 731      */
 732     protected InputStream getStream(URL page) throws IOException {
 733         final URLConnection conn = page.openConnection();
 734         if (conn instanceof HttpURLConnection) {
 735             HttpURLConnection hconn = (HttpURLConnection) conn;
 736             hconn.setInstanceFollowRedirects(false);
 737             Object postData = getPostData();
 738             if (postData != null) {
 739                 handlePostData(hconn, postData);
 740             }
 741             int response = hconn.getResponseCode();
 742             boolean redirect = (response >= 300 && response <= 399);
 743 
 744             /*
 745              * In the case of a redirect, we want to actually change the URL


 801 
 802     private void handlePostData(HttpURLConnection conn, Object postData)
 803                                                             throws IOException {
 804         conn.setDoOutput(true);
 805         DataOutputStream os = null;
 806         try {
 807             conn.setRequestProperty("Content-Type",
 808                     "application/x-www-form-urlencoded");
 809             os = new DataOutputStream(conn.getOutputStream());
 810             os.writeBytes((String) postData);
 811         } finally {
 812             if (os != null) {
 813                 os.close();
 814             }
 815         }
 816     }
 817 
 818 
 819     /**
 820      * Scrolls the view to the given reference location
 821      * (that is, the value returned by the <code>UL.getRef</code>
 822      * method for the URL being displayed).  By default, this
 823      * method only knows how to locate a reference in an
 824      * HTMLDocument.  The implementation calls the
 825      * <code>scrollRectToVisible</code> method to
 826      * accomplish the actual scrolling.  If scrolling to a
 827      * reference location is needed for document types other
 828      * than HTML, this method should be reimplemented.
 829      * This method will have no effect if the component
 830      * is not visible.
 831      *
 832      * @param reference the named location to scroll to
 833      */
 834     public void scrollToReference(String reference) {
 835         Document d = getDocument();
 836         if (d instanceof HTMLDocument) {
 837             HTMLDocument doc = (HTMLDocument) d;
 838             HTMLDocument.Iterator iter = doc.getIterator(HTML.Tag.A);
 839             for (; iter.isValid(); iter.next()) {
 840                 AttributeSet a = iter.getAttributes();
 841                 String nm = (String) a.getAttribute(HTML.Attribute.NAME);
 842                 if ((nm != null) && nm.equals(reference)) {
 843                     // found a matching reference in the document.
 844                     try {
 845                         int pos = iter.getStartOffset();


 847                         if (r != null) {
 848                             // the view is visible, scroll it to the
 849                             // center of the current visible area.
 850                             Rectangle vis = getVisibleRect();
 851                             //r.y -= (vis.height / 2);
 852                             r.height = vis.height;
 853                             scrollRectToVisible(r);
 854                             setCaretPosition(pos);
 855                         }
 856                     } catch (BadLocationException ble) {
 857                         UIManager.getLookAndFeel().provideErrorFeedback(JEditorPane.this);
 858                     }
 859                 }
 860             }
 861         }
 862     }
 863 
 864     /**
 865      * Gets the current URL being displayed.  If a URL was
 866      * not specified in the creation of the document, this
 867      * will return <code>null</code>, and relative URL's will not be
 868      * resolved.
 869      *
 870      * @return the URL, or <code>null</code> if none
 871      */
 872     public URL getPage() {
 873         return (URL) getDocument().getProperty(Document.StreamDescriptionProperty);
 874     }
 875 
 876     /**
 877      * Sets the current URL being displayed.
 878      *
 879      * @param url the URL for display
 880      * @exception IOException for a <code>null</code> or invalid URL
 881      *          specification
 882      */
 883     public void setPage(String url) throws IOException {
 884         if (url == null) {
 885             throw new IOException("invalid url");
 886         }
 887         URL page = new URL(url);
 888         setPage(page);
 889     }
 890 
 891     /**
 892      * Gets the class ID for the UI.
 893      *
 894      * @return the string "EditorPaneUI"
 895      * @see JComponent#getUIClassID
 896      * @see UIDefaults#getUI
 897      */
 898     public String getUIClassID() {
 899         return uiClassID;
 900     }
 901 
 902     /**
 903      * Creates the default editor kit (<code>PlainEditorKit</code>) for when
 904      * the component is first created.
 905      *
 906      * @return the editor kit
 907      */
 908     protected EditorKit createDefaultEditorKit() {
 909         return new PlainEditorKit();
 910     }
 911 
 912     /**
 913      * Fetches the currently installed kit for handling content.
 914      * <code>createDefaultEditorKit</code> is called to set up a default
 915      * if necessary.
 916      *
 917      * @return the editor kit
 918      */
 919     public EditorKit getEditorKit() {
 920         if (kit == null) {
 921             kit = createDefaultEditorKit();
 922             isUserSetEditorKit = false;
 923         }
 924         return kit;
 925     }
 926 
 927     /**
 928      * Gets the type of content that this editor
 929      * is currently set to deal with.  This is
 930      * defined to be the type associated with the
 931      * currently installed <code>EditorKit</code>.
 932      *
 933      * @return the content type, <code>null</code> if no editor kit set
 934      */
 935     public final String getContentType() {
 936         return (kit != null) ? kit.getContentType() : null;
 937     }
 938 
 939     /**
 940      * Sets the type of content that this editor
 941      * handles.  This calls <code>getEditorKitForContentType</code>,
 942      * and then <code>setEditorKit</code> if an editor kit can
 943      * be successfully located.  This is mostly convenience method
 944      * that can be used as an alternative to calling
 945      * <code>setEditorKit</code> directly.
 946      * <p>
 947      * If there is a charset definition specified as a parameter
 948      * of the content type specification, it will be used when
 949      * loading input streams using the associated <code>EditorKit</code>.
 950      * For example if the type is specified as
 951      * <code>text/html; charset=EUC-JP</code> the content
 952      * will be loaded using the <code>EditorKit</code> registered for
 953      * <code>text/html</code> and the Reader provided to
 954      * the <code>EditorKit</code> to load unicode into the document will
 955      * use the <code>EUC-JP</code> charset for translating
 956      * to unicode.  If the type is not recognized, the content
 957      * will be loaded using the <code>EditorKit</code> registered
 958      * for plain text, <code>text/plain</code>.
 959      *
 960      * @param type the non-<code>null</code> mime type for the content editing
 961      *   support
 962      * @see #getContentType
 963      * @beaninfo
 964      *  description: the type of content
 965      * @throws NullPointerException if the <code>type</code> parameter
 966      *          is <code>null</code>
 967      */
 968     public final void setContentType(String type) {
 969         // The type could have optional info is part of it,
 970         // for example some charset info.  We need to strip that
 971         // of and save it.
 972         int parm = type.indexOf(';');
 973         if (parm > -1) {
 974             // Save the paramList.
 975             String paramList = type.substring(parm);
 976             // update the content type string.
 977             type = type.substring(0, parm).trim();
 978             if (type.toLowerCase().startsWith("text/")) {
 979                 setCharsetFromContentTypeParameters(paramList);
 980             }
 981         }
 982         if ((kit == null) || (! type.equals(kit.getContentType()))
 983                 || !isUserSetEditorKit) {
 984             EditorKit k = getEditorKitForContentType(type);
 985             if (k != null && k != kit) {
 986                 setEditorKit(k);


1015         }
1016         catch (IndexOutOfBoundsException e) {
1017             // malformed parameter list, use charset we have
1018         }
1019         catch (NullPointerException e) {
1020             // malformed parameter list, use charset we have
1021         }
1022         catch (Exception e) {
1023             // malformed parameter list, use charset we have; but complain
1024             System.err.println("JEditorPane.getCharsetFromContentTypeParameters failed on: " + paramlist);
1025             e.printStackTrace();
1026         }
1027     }
1028 
1029 
1030     /**
1031      * Sets the currently installed kit for handling
1032      * content.  This is the bound property that
1033      * establishes the content type of the editor.
1034      * Any old kit is first deinstalled, then if kit is
1035      * non-<code>null</code>,
1036      * the new kit is installed, and a default document created for it.
1037      * A <code>PropertyChange</code> event ("editorKit") is always fired when
1038      * <code>setEditorKit</code> is called.
1039      * <p>
1040      * <em>NOTE: This has the side effect of changing the model,
1041      * because the <code>EditorKit</code> is the source of how a
1042      * particular type
1043      * of content is modeled.  This method will cause <code>setDocument</code>
1044      * to be called on behalf of the caller to ensure integrity
1045      * of the internal state.</em>
1046      *
1047      * @param kit the desired editor behavior
1048      * @see #getEditorKit
1049      * @beaninfo
1050      *  description: the currently installed kit for handling content
1051      *        bound: true
1052      *       expert: true
1053      */
1054     public void setEditorKit(EditorKit kit) {
1055         EditorKit old = this.kit;
1056         isUserSetEditorKit = true;
1057         if (old != null) {
1058             old.deinstall(this);
1059         }
1060         this.kit = kit;
1061         if (this.kit != null) {
1062             this.kit.install(this);
1063             setDocument(this.kit.createDefaultDocument());
1064         }
1065         firePropertyChange("editorKit", old, kit);
1066     }
1067 
1068     /**
1069      * Fetches the editor kit to use for the given type
1070      * of content.  This is called when a type is requested
1071      * that doesn't match the currently installed type.
1072      * If the component doesn't have an <code>EditorKit</code> registered
1073      * for the given type, it will try to create an
1074      * <code>EditorKit</code> from the default <code>EditorKit</code> registry.
1075      * If that fails, a <code>PlainEditorKit</code> is used on the
1076      * assumption that all text documents can be represented
1077      * as plain text.
1078      * <p>
1079      * This method can be reimplemented to use some
1080      * other kind of type registry.  This can
1081      * be reimplemented to use the Java Activation
1082      * Framework, for example.
1083      *
1084      * @param type the non-<code>null</code> content type
1085      * @return the editor kit
1086      */
1087     public EditorKit getEditorKitForContentType(String type) {
1088         if (typeHandlers == null) {
1089             typeHandlers = new Hashtable<String, EditorKit>(3);
1090         }
1091         EditorKit k = typeHandlers.get(type);
1092         if (k == null) {
1093             k = createEditorKitForContentType(type);
1094             if (k != null) {
1095                 setEditorKitForContentType(type, k);
1096             }
1097         }
1098         if (k == null) {
1099             k = createDefaultEditorKit();
1100         }
1101         return k;
1102     }
1103 
1104     /**
1105      * Directly sets the editor kit to use for the given type.  A
1106      * look-and-feel implementation might use this in conjunction
1107      * with <code>createEditorKitForContentType</code> to install handlers for
1108      * content types with a look-and-feel bias.
1109      *
1110      * @param type the non-<code>null</code> content type
1111      * @param k the editor kit to be set
1112      */
1113     public void setEditorKitForContentType(String type, EditorKit k) {
1114         if (typeHandlers == null) {
1115             typeHandlers = new Hashtable<String, EditorKit>(3);
1116         }
1117         typeHandlers.put(type, k);
1118     }
1119 
1120     /**
1121      * Replaces the currently selected content with new content
1122      * represented by the given string.  If there is no selection
1123      * this amounts to an insert of the given text.  If there
1124      * is no replacement text (i.e. the content string is empty
1125      * or <code>null</code>) this amounts to a removal of the
1126      * current selection.  The replacement text will have the
1127      * attributes currently defined for input.  If the component is not
1128      * editable, beep and return.
1129      *
1130      * @param content  the content to replace the selection with.  This
1131      *   value can be <code>null</code>
1132      */
1133     @Override
1134     public void replaceSelection(String content) {
1135         if (! isEditable()) {
1136             UIManager.getLookAndFeel().provideErrorFeedback(JEditorPane.this);
1137             return;
1138         }
1139         EditorKit kit = getEditorKit();
1140         if(kit instanceof StyledEditorKit) {
1141             try {
1142                 Document doc = getDocument();
1143                 Caret caret = getCaret();
1144                 boolean composedTextSaved = saveComposedText(caret.getDot());
1145                 int p0 = Math.min(caret.getDot(), caret.getMark());
1146                 int p1 = Math.max(caret.getDot(), caret.getMark());
1147                 if (doc instanceof AbstractDocument) {
1148                     ((AbstractDocument)doc).replace(p0, p1 - p0, content,
1149                               ((StyledEditorKit)kit).getInputAttributes());
1150                 }
1151                 else {


1157                                          getInputAttributes());
1158                     }
1159                 }
1160                 if (composedTextSaved) {
1161                     restoreComposedText();
1162                 }
1163             } catch (BadLocationException e) {
1164                 UIManager.getLookAndFeel().provideErrorFeedback(JEditorPane.this);
1165             }
1166         }
1167         else {
1168             super.replaceSelection(content);
1169         }
1170     }
1171 
1172     /**
1173      * Creates a handler for the given type from the default registry
1174      * of editor kits.  The registry is created if necessary.  If the
1175      * registered class has not yet been loaded, an attempt
1176      * is made to dynamically load the prototype of the kit for the
1177      * given type.  If the type was registered with a <code>ClassLoader</code>,
1178      * that <code>ClassLoader</code> will be used to load the prototype.
1179      * If there was no registered <code>ClassLoader</code>,
1180      * <code>Class.forName</code> will be used to load the prototype.
1181      * <p>
1182      * Once a prototype <code>EditorKit</code> instance is successfully
1183      * located, it is cloned and the clone is returned.
1184      *
1185      * @param type the content type
1186      * @return the editor kit, or <code>null</code> if there is nothing
1187      *   registered for the given type
1188      */
1189     public static EditorKit createEditorKitForContentType(String type) {
1190         Hashtable<String, EditorKit> kitRegistry = getKitRegisty();
1191         EditorKit k = kitRegistry.get(type);
1192         if (k == null) {
1193             // try to dynamically load the support
1194             String classname = getKitTypeRegistry().get(type);
1195             ClassLoader loader = getKitLoaderRegistry().get(type);
1196             try {
1197                 Class<?> c;
1198                 if (loader != null) {
1199                     ReflectUtil.checkPackageAccess(classname);
1200                     c = loader.loadClass(classname);
1201                 } else {
1202                     // Will only happen if developer has invoked
1203                     // registerEditorKitForContentType(type, class, null).
1204                     c = SwingUtilities.loadSystemClass(classname);
1205                 }
1206                 k = (EditorKit) c.newInstance();
1207                 kitRegistry.put(type, k);
1208             } catch (Throwable e) {
1209                 k = null;
1210             }
1211         }
1212 
1213         // create a copy of the prototype or null if there
1214         // is no prototype.
1215         if (k != null) {
1216             return (EditorKit) k.clone();
1217         }
1218         return null;
1219     }
1220 
1221     /**
1222      * Establishes the default bindings of <code>type</code> to
1223      * <code>classname</code>.
1224      * The class will be dynamically loaded later when actually
1225      * needed, and can be safely changed before attempted uses
1226      * to avoid loading unwanted classes.  The prototype
1227      * <code>EditorKit</code> will be loaded with <code>Class.forName</code>
1228      * when registered with this method.
1229      *
1230      * @param type the non-<code>null</code> content type
1231      * @param classname the class to load later
1232      */
1233     public static void registerEditorKitForContentType(String type, String classname) {
1234         registerEditorKitForContentType(type, classname,Thread.currentThread().
1235                                         getContextClassLoader());
1236     }
1237 
1238     /**
1239      * Establishes the default bindings of <code>type</code> to
1240      * <code>classname</code>.
1241      * The class will be dynamically loaded later when actually
1242      * needed using the given <code>ClassLoader</code>,
1243      * and can be safely changed
1244      * before attempted uses to avoid loading unwanted classes.
1245      *
1246      * @param type the non-<code>null</code> content type
1247      * @param classname the class to load later
1248      * @param loader the <code>ClassLoader</code> to use to load the name
1249      */
1250     public static void registerEditorKitForContentType(String type, String classname, ClassLoader loader) {
1251         getKitTypeRegistry().put(type, classname);
1252         getKitLoaderRegistry().put(type, loader);
1253         getKitRegisty().remove(type);
1254     }
1255 
1256     /**
1257      * Returns the currently registered {@code EditorKit} class name for the
1258      * type {@code type}.
1259      *
1260      * @param type  the non-{@code null} content type
1261      * @return a {@code String} containing the {@code EditorKit} class name
1262      *         for {@code type}
1263      * @since 1.3
1264      */
1265     public static String getEditorKitClassNameForContentType(String type) {
1266         return getKitTypeRegistry().get(type);
1267     }
1268 


1310                     defaultEditorKitMap.put("text/rtf",
1311                                             "javax.swing.text.rtf.RTFEditorKit");
1312                     defaultEditorKitMap.put("application/rtf",
1313                                             "javax.swing.text.rtf.RTFEditorKit");
1314                 }
1315             }
1316             Hashtable<Object, Object> ht = new Hashtable<>();
1317             SwingUtilities.appContextPut(kitTypeRegistryKey, ht);
1318             ht = new Hashtable<>();
1319             SwingUtilities.appContextPut(kitLoaderRegistryKey, ht);
1320             for (String key : defaultEditorKitMap.keySet()) {
1321                 registerEditorKitForContentType(key,defaultEditorKitMap.get(key));
1322             }
1323 
1324         }
1325     }
1326 
1327     // --- java.awt.Component methods --------------------------
1328 
1329     /**
1330      * Returns the preferred size for the <code>JEditorPane</code>.
1331      * The preferred size for <code>JEditorPane</code> is slightly altered
1332      * from the preferred size of the superclass.  If the size
1333      * of the viewport has become smaller than the minimum size
1334      * of the component, the scrollable definition for tracking
1335      * width or height will turn to false.  The default viewport
1336      * layout will give the preferred size, and that is not desired
1337      * in the case where the scrollable is tracking.  In that case
1338      * the <em>normal</em> preferred size is adjusted to the
1339      * minimum size.  This allows things like HTML tables to
1340      * shrink down to their minimum size and then be laid out at
1341      * their minimum size, refusing to shrink any further.
1342      *
1343      * @return a <code>Dimension</code> containing the preferred size
1344      */
1345     public Dimension getPreferredSize() {
1346         Dimension d = super.getPreferredSize();
1347         Container parent = SwingUtilities.getUnwrappedParent(this);
1348         if (parent instanceof JViewport) {
1349             JViewport port = (JViewport) parent;
1350             TextUI ui = getUI();
1351             int prefWidth = d.width;
1352             int prefHeight = d.height;
1353             if (! getScrollableTracksViewportWidth()) {
1354                 int w = port.getWidth();
1355                 Dimension min = ui.getMinimumSize(this);
1356                 if (w != 0 && w < min.width) {
1357                     // Only adjust to min if we have a valid size
1358                     prefWidth = min.width;
1359                 }
1360             }
1361             if (! getScrollableTracksViewportHeight()) {
1362                 int h = port.getHeight();
1363                 Dimension min = ui.getMinimumSize(this);
1364                 if (h != 0 && h < min.height) {
1365                     // Only adjust to min if we have a valid size
1366                     prefHeight = min.height;
1367                 }
1368             }
1369             if (prefWidth != d.width || prefHeight != d.height) {
1370                 d = new Dimension(prefWidth, prefHeight);
1371             }
1372         }
1373         return d;
1374     }
1375 
1376     // --- JTextComponent methods -----------------------------
1377 
1378     /**
1379      * Sets the text of this <code>TextComponent</code> to the specified
1380      * content,
1381      * which is expected to be in the format of the content type of
1382      * this editor.  For example, if the type is set to <code>text/html</code>
1383      * the string should be specified in terms of HTML.
1384      * <p>
1385      * This is implemented to remove the contents of the current document,
1386      * and replace them by parsing the given string using the current
1387      * <code>EditorKit</code>.  This gives the semantics of the
1388      * superclass by not changing
1389      * out the model, while supporting the content type currently set on
1390      * this component.  The assumption is that the previous content is
1391      * relatively
1392      * small, and that the previous content doesn't have side effects.
1393      * Both of those assumptions can be violated and cause undesirable results.
1394      * To avoid this, create a new document,
1395      * <code>getEditorKit().createDefaultDocument()</code>, and replace the
1396      * existing <code>Document</code> with the new one. You are then assured the
1397      * previous <code>Document</code> won't have any lingering state.
1398      * <ol>
1399      * <li>
1400      * Leaving the existing model in place means that the old view will be
1401      * torn down, and a new view created, where replacing the document would
1402      * avoid the tear down of the old view.
1403      * <li>
1404      * Some formats (such as HTML) can install things into the document that
1405      * can influence future contents.  HTML can have style information embedded
1406      * that would influence the next content installed unexpectedly.
1407      * </ol>
1408      * <p>
1409      * An alternative way to load this component with a string would be to
1410      * create a StringReader and call the read method.  In this case the model
1411      * would be replaced after it was initialized with the contents of the
1412      * string.
1413      *
1414      * @param t the new text to be set; if <code>null</code> the old
1415      *    text will be deleted
1416      * @see #getText
1417      * @beaninfo
1418      * description: the text of this component
1419      */
1420     public void setText(String t) {
1421         try {
1422             Document doc = getDocument();
1423             doc.remove(0, doc.getLength());
1424             if (t == null || t.equals("")) {
1425                 return;
1426             }
1427             Reader r = new StringReader(t);
1428             EditorKit kit = getEditorKit();
1429             kit.read(r, doc, 0);
1430         } catch (IOException ioe) {
1431             UIManager.getLookAndFeel().provideErrorFeedback(JEditorPane.this);
1432         } catch (BadLocationException ble) {
1433             UIManager.getLookAndFeel().provideErrorFeedback(JEditorPane.this);
1434         }
1435     }
1436 
1437     /**
1438      * Returns the text contained in this <code>TextComponent</code>
1439      * in terms of the
1440      * content type of this editor.  If an exception is thrown while
1441      * attempting to retrieve the text, <code>null</code> will be returned.
1442      * This is implemented to call <code>JTextComponent.write</code> with
1443      * a <code>StringWriter</code>.
1444      *
1445      * @return the text
1446      * @see #setText
1447      */
1448     public String getText() {
1449         String txt;
1450         try {
1451             StringWriter buf = new StringWriter();
1452             write(buf);
1453             txt = buf.toString();
1454         } catch (IOException ioe) {
1455             txt = null;
1456         }
1457         return txt;
1458     }
1459 
1460     // --- Scrollable  ----------------------------------------
1461 
1462     /**
1463      * Returns true if a viewport should always force the width of this
1464      * <code>Scrollable</code> to match the width of the viewport.
1465      *
1466      * @return true if a viewport should force the Scrollables width to
1467      * match its own, false otherwise
1468      */
1469     public boolean getScrollableTracksViewportWidth() {
1470         Container parent = SwingUtilities.getUnwrappedParent(this);
1471         if (parent instanceof JViewport) {
1472             JViewport port = (JViewport) parent;
1473             TextUI ui = getUI();
1474             int w = port.getWidth();
1475             Dimension min = ui.getMinimumSize(this);
1476             Dimension max = ui.getMaximumSize(this);
1477             if ((w >= min.width) && (w <= max.width)) {
1478                 return true;
1479             }
1480         }
1481         return false;
1482     }
1483 
1484     /**
1485      * Returns true if a viewport should always force the height of this
1486      * <code>Scrollable</code> to match the height of the viewport.
1487      *
1488      * @return true if a viewport should force the
1489      *          <code>Scrollable</code>'s height to match its own,
1490      *          false otherwise
1491      */
1492     public boolean getScrollableTracksViewportHeight() {
1493         Container parent = SwingUtilities.getUnwrappedParent(this);
1494         if (parent instanceof JViewport) {
1495             JViewport port = (JViewport) parent;
1496             TextUI ui = getUI();
1497             int h = port.getHeight();
1498             Dimension min = ui.getMinimumSize(this);
1499             if (h >= min.height) {
1500                 Dimension max = ui.getMaximumSize(this);
1501                 if (h <= max.height) {
1502                     return true;
1503                 }
1504             }
1505         }
1506         return false;
1507     }
1508 
1509     // --- Serialization ------------------------------------
1510 
1511     /**
1512      * See <code>readObject</code> and <code>writeObject</code> in
1513      * <code>JComponent</code> for more
1514      * information about serialization in Swing.
1515      */
1516     private void writeObject(ObjectOutputStream s) throws IOException {
1517         s.defaultWriteObject();
1518         if (getUIClassID().equals(uiClassID)) {
1519             byte count = JComponent.getWriteObjCounter(this);
1520             JComponent.setWriteObjCounter(this, --count);
1521             if (count == 0 && ui != null) {
1522                 ui.installUI(this);
1523             }
1524         }
1525     }
1526 
1527     // --- variables ---------------------------------------
1528 
1529     private SwingWorker<URL, Object> pageLoader;
1530 
1531     /**
1532      * Current content binding of the editor.
1533      */


1551         new StringBuffer("JEditorPane.kitRegistry");
1552     private static final Object kitTypeRegistryKey =
1553         new StringBuffer("JEditorPane.kitTypeRegistry");
1554     private static final Object kitLoaderRegistryKey =
1555         new StringBuffer("JEditorPane.kitLoaderRegistry");
1556 
1557     /**
1558      * @see #getUIClassID
1559      * @see #readObject
1560      */
1561     private static final String uiClassID = "EditorPaneUI";
1562 
1563 
1564     /**
1565      * Key for a client property used to indicate whether
1566      * <a href="http://www.w3.org/TR/CSS21/syndata.html#length-units">
1567      * w3c compliant</a> length units are used for html rendering.
1568      * <p>
1569      * By default this is not enabled; to enable
1570      * it set the client {@link #putClientProperty property} with this name
1571      * to <code>Boolean.TRUE</code>.
1572      *
1573      * @since 1.5
1574      */
1575     public static final String W3C_LENGTH_UNITS = "JEditorPane.w3cLengthUnits";
1576 
1577     /**
1578      * Key for a client property used to indicate whether
1579      * the default font and foreground color from the component are
1580      * used if a font or foreground color is not specified in the styled
1581      * text.
1582      * <p>
1583      * The default varies based on the look and feel;
1584      * to enable it set the client {@link #putClientProperty property} with
1585      * this name to <code>Boolean.TRUE</code>.
1586      *
1587      * @since 1.5
1588      */
1589     public static final String HONOR_DISPLAY_PROPERTIES = "JEditorPane.honorDisplayProperties";
1590 
1591     static final Map<String, String> defaultEditorKitMap = new HashMap<String, String>(0);
1592 
1593     /**
1594      * Returns a string representation of this <code>JEditorPane</code>.
1595      * This method
1596      * is intended to be used only for debugging purposes, and the
1597      * content and format of the returned string may vary between
1598      * implementations. The returned string may be empty but may not
1599      * be <code>null</code>.
1600      *
1601      * @return  a string representation of this <code>JEditorPane</code>
1602      */
1603     protected String paramString() {
1604         String kitString = (kit != null ?
1605                             kit.toString() : "");
1606         String typeHandlersString = (typeHandlers != null ?
1607                                      typeHandlers.toString() : "");
1608 
1609         return super.paramString() +
1610         ",kit=" + kitString +
1611         ",typeHandlers=" + typeHandlersString;
1612     }
1613 
1614 
1615 /////////////////
1616 // Accessibility support
1617 ////////////////
1618 
1619 
1620     /**
1621      * Gets the AccessibleContext associated with this JEditorPane.


1624      * A new AccessibleJEditorPane instance is created if necessary.
1625      *
1626      * @return an AccessibleJEditorPane that serves as the
1627      *         AccessibleContext of this JEditorPane
1628      */
1629     public AccessibleContext getAccessibleContext() {
1630         if (getEditorKit() instanceof HTMLEditorKit) {
1631             if (accessibleContext == null || accessibleContext.getClass() !=
1632                     AccessibleJEditorPaneHTML.class) {
1633                 accessibleContext = new AccessibleJEditorPaneHTML();
1634             }
1635         } else if (accessibleContext == null || accessibleContext.getClass() !=
1636                        AccessibleJEditorPane.class) {
1637             accessibleContext = new AccessibleJEditorPane();
1638         }
1639         return accessibleContext;
1640     }
1641 
1642     /**
1643      * This class implements accessibility support for the
1644      * <code>JEditorPane</code> class.  It provides an implementation of the
1645      * Java Accessibility API appropriate to editor pane user-interface
1646      * elements.
1647      * <p>
1648      * <strong>Warning:</strong>
1649      * Serialized objects of this class will not be compatible with
1650      * future Swing releases. The current serialization support is
1651      * appropriate for short term storage or RMI between applications running
1652      * the same version of Swing.  As of 1.4, support for long term storage
1653      * of all JavaBeans&trade;
1654      * has been added to the <code>java.beans</code> package.
1655      * Please see {@link java.beans.XMLEncoder}.
1656      */
1657     @SuppressWarnings("serial") // Same-version serialization only
1658     protected class AccessibleJEditorPane extends AccessibleJTextComponent {
1659 
1660         /**
1661          * Gets the accessibleDescription property of this object.  If this
1662          * property isn't set, returns the content type of this
1663          * <code>JEditorPane</code> instead (e.g. "plain/text", "html/text").
1664          *
1665          * @return the localized description of the object; <code>null</code>
1666          *      if this object does not have a description
1667          *
1668          * @see #setAccessibleName
1669          */
1670         public String getAccessibleDescription() {
1671             String description = accessibleDescription;
1672 
1673             // fallback to client property
1674             if (description == null) {
1675                 description = (String)getClientProperty(AccessibleContext.ACCESSIBLE_DESCRIPTION_PROPERTY);
1676             }
1677             if (description == null) {
1678                 description = JEditorPane.this.getContentType();
1679             }
1680             return description;
1681         }
1682 
1683         /**
1684          * Gets the state set of this object.
1685          *
1686          * @return an instance of AccessibleStateSet describing the states
1687          * of the object
1688          * @see AccessibleStateSet
1689          */
1690         public AccessibleStateSet getAccessibleStateSet() {
1691             AccessibleStateSet states = super.getAccessibleStateSet();
1692             states.add(AccessibleState.MULTI_LINE);
1693             return states;
1694         }
1695     }
1696 
1697     /**
1698      * This class provides support for <code>AccessibleHypertext</code>,
1699      * and is used in instances where the <code>EditorKit</code>
1700      * installed in this <code>JEditorPane</code> is an instance of
1701      * <code>HTMLEditorKit</code>.
1702      * <p>
1703      * <strong>Warning:</strong>
1704      * Serialized objects of this class will not be compatible with
1705      * future Swing releases. The current serialization support is
1706      * appropriate for short term storage or RMI between applications running
1707      * the same version of Swing.  As of 1.4, support for long term storage
1708      * of all JavaBeans&trade;
1709      * has been added to the <code>java.beans</code> package.
1710      * Please see {@link java.beans.XMLEncoder}.
1711      */
1712     @SuppressWarnings("serial") // Same-version serialization only
1713     protected class AccessibleJEditorPaneHTML extends AccessibleJEditorPane {
1714 
1715         private AccessibleContext accessibleContext;
1716 
1717         /**
1718          * Returns the accessible text.
1719          * @return the accessible text
1720          */
1721         public AccessibleText getAccessibleText() {
1722             return new JEditorPaneAccessibleHypertextSupport();
1723         }
1724 
1725         /**
1726          * Constructs an {@code AccessibleJEditorPaneHTML}.
1727          */
1728         protected AccessibleJEditorPaneHTML () {
1729             HTMLEditorKit kit = (HTMLEditorKit)JEditorPane.this.getEditorKit();


1773             if (accessibleContext != null && p != null) {
1774                 try {
1775                     AccessibleComponent acomp =
1776                         accessibleContext.getAccessibleComponent();
1777                     if (acomp != null) {
1778                         return acomp.getAccessibleAt(p);
1779                     } else {
1780                         return null;
1781                     }
1782                 } catch (IllegalComponentStateException e) {
1783                     return null;
1784                 }
1785             } else {
1786                 return null;
1787             }
1788         }
1789     }
1790 
1791     /**
1792      * What's returned by
1793      * <code>AccessibleJEditorPaneHTML.getAccessibleText</code>.
1794      *
1795      * Provides support for <code>AccessibleHypertext</code> in case
1796      * there is an HTML document being displayed in this
1797      * <code>JEditorPane</code>.
1798      *
1799      */
1800     protected class JEditorPaneAccessibleHypertextSupport
1801     extends AccessibleJEditorPane implements AccessibleHypertext {
1802 
1803         /**
1804          * An HTML link.
1805          */
1806         public class HTMLLink extends AccessibleHyperlink {
1807             Element element;
1808 
1809             /**
1810              * Constructs a {@code HTMLLink}.
1811              * @param e the element
1812              */
1813             public HTMLLink(Element e) {
1814                 element = e;
1815             }
1816 
1817             /**




  32 import java.net.*;
  33 import java.util.*;
  34 import java.io.*;
  35 import java.util.*;
  36 
  37 import javax.swing.plaf.*;
  38 import javax.swing.text.*;
  39 import javax.swing.event.*;
  40 import javax.swing.text.html.*;
  41 import javax.accessibility.*;
  42 import sun.reflect.misc.ReflectUtil;
  43 
  44 /**
  45  * A text component to edit various kinds of content.
  46  * You can find how-to information and examples of using editor panes in
  47  * <a href="http://docs.oracle.com/javase/tutorial/uiswing/components/text.html">Using Text Components</a>,
  48  * a section in <em>The Java Tutorial.</em>
  49  *
  50  * <p>
  51  * This component uses implementations of the
  52  * {@code EditorKit} to accomplish its behavior. It effectively
  53  * morphs into the proper kind of text editor for the kind
  54  * of content it is given.  The content type that editor is bound
  55  * to at any given time is determined by the {@code EditorKit} currently
  56  * installed.  If the content is set to a new URL, its type is used
  57  * to determine the {@code EditorKit} that should be used to
  58  * load the content.
  59  * <p>
  60  * By default, the following types of content are known:
  61  * <dl>
  62  * <dt><b>text/plain</b>
  63  * <dd>Plain text, which is the default the type given isn't
  64  * recognized.  The kit used in this case is an extension of
  65  * {@code DefaultEditorKit} that produces a wrapped plain text view.
  66  * <dt><b>text/html</b>
  67  * <dd>HTML text.  The kit used in this case is the class
  68  * {@code javax.swing.text.html.HTMLEditorKit}
  69  * which provides HTML 3.2 support.
  70  * <dt><b>text/rtf</b>
  71  * <dd>RTF text.  The kit used in this case is the class
  72  * {@code javax.swing.text.rtf.RTFEditorKit}
  73  * which provides a limited support of the Rich Text Format.
  74  * </dl>
  75  * <p>
  76  * There are several ways to load content into this component.
  77  * <ol>
  78  * <li>
  79  * The {@link #setText setText} method can be used to initialize
  80  * the component from a string.  In this case the current
  81  * {@code EditorKit} will be used, and the content type will be
  82  * expected to be of this type.
  83  * <li>
  84  * The {@link #read read} method can be used to initialize the
  85  * component from a {@code Reader}.  Note that if the content type is HTML,
  86  * relative references (e.g. for things like images) can't be resolved
  87  * unless the &lt;base&gt; tag is used or the <em>Base</em> property
  88  * on {@code HTMLDocument} is set.
  89  * In this case the current {@code EditorKit} will be used,
  90  * and the content type will be expected to be of this type.
  91  * <li>
  92  * The {@link #setPage setPage} method can be used to initialize
  93  * the component from a URL.  In this case, the content type will be
  94  * determined from the URL, and the registered {@code EditorKit}
  95  * for that content type will be set.
  96  * </ol>
  97  * <p>
  98  * Some kinds of content may provide hyperlink support by generating
  99  * hyperlink events.  The HTML {@code EditorKit} will generate
 100  * hyperlink events if the {@code JEditorPane} is <em>not editable</em>
 101  * ({@code JEditorPane.setEditable(false);} has been called).
 102  * If HTML frames are embedded in the document, the typical response would be
 103  * to change a portion of the current document.  The following code
 104  * fragment is a possible hyperlink listener implementation, that treats
 105  * HTML frame events specially, and simply displays any other activated
 106  * hyperlinks.
 107  * <pre>
 108 
 109 &nbsp;    class Hyperactive implements HyperlinkListener {
 110 &nbsp;
 111 &nbsp;        public void hyperlinkUpdate(HyperlinkEvent e) {
 112 &nbsp;            if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
 113 &nbsp;                JEditorPane pane = (JEditorPane) e.getSource();
 114 &nbsp;                if (e instanceof HTMLFrameHyperlinkEvent) {
 115 &nbsp;                    HTMLFrameHyperlinkEvent  evt = (HTMLFrameHyperlinkEvent)e;
 116 &nbsp;                    HTMLDocument doc = (HTMLDocument)pane.getDocument();
 117 &nbsp;                    doc.processHTMLFrameHyperlinkEvent(evt);
 118 &nbsp;                } else {
 119 &nbsp;                    try {
 120 &nbsp;                        pane.setPage(e.getURL());
 121 &nbsp;                    } catch (Throwable t) {
 122 &nbsp;                        t.printStackTrace();
 123 &nbsp;                    }
 124 &nbsp;                }
 125 &nbsp;            }
 126 &nbsp;        }
 127 &nbsp;    }
 128 
 129  * </pre>
 130  * <p>
 131  * For information on customizing how <b>text/html</b> is rendered please see
 132  * {@link #W3C_LENGTH_UNITS} and {@link #HONOR_DISPLAY_PROPERTIES}
 133  * <p>
 134  * Culturally dependent information in some documents is handled through
 135  * a mechanism called character encoding.  Character encoding is an
 136  * unambiguous mapping of the members of a character set (letters, ideographs,
 137  * digits, symbols, or control functions) to specific numeric code values. It
 138  * represents the way the file is stored. Example character encodings are
 139  * ISO-8859-1, ISO-8859-5, Shift-jis, Euc-jp, and UTF-8. When the file is
 140  * passed to an user agent ({@code JEditorPane}) it is converted to
 141  * the document character set (ISO-10646 aka Unicode).
 142  * <p>
 143  * There are multiple ways to get a character set mapping to happen
 144  * with {@code JEditorPane}.
 145  * <ol>
 146  * <li>
 147  * One way is to specify the character set as a parameter of the MIME
 148  * type.  This will be established by a call to the
 149  * {@link #setContentType setContentType} method.  If the content
 150  * is loaded by the {@link #setPage setPage} method the content
 151  * type will have been set according to the specification of the URL.
 152  * It the file is loaded directly, the content type would be expected to
 153  * have been set prior to loading.
 154  * <li>
 155  * Another way the character set can be specified is in the document itself.
 156  * This requires reading the document prior to determining the character set
 157  * that is desired.  To handle this, it is expected that the
 158  * {@code EditorKit}.read operation throw a
 159  * {@code ChangedCharSetException} which will
 160  * be caught.  The read is then restarted with a new Reader that uses
 161  * the character set specified in the {@code ChangedCharSetException}
 162  * (which is an {@code IOException}).
 163  * </ol>
 164  *
 165  * <dl>
 166  * <dt><b>Newlines</b>
 167  * <dd>
 168  * For a discussion on how newlines are handled, see
 169  * <a href="text/DefaultEditorKit.html">DefaultEditorKit</a>.
 170  * </dl>
 171  *
 172  * <p>
 173  * <strong>Warning:</strong> Swing is not thread safe. For more
 174  * information see <a
 175  * href="package-summary.html#threading">Swing's Threading
 176  * Policy</a>.
 177  * <p>
 178  * <strong>Warning:</strong>
 179  * Serialized objects of this class will not be compatible with
 180  * future Swing releases. The current serialization support is
 181  * appropriate for short term storage or RMI between applications running
 182  * the same version of Swing.  As of 1.4, support for long term storage
 183  * of all JavaBeans&trade;
 184  * has been added to the {@code java.beans} package.
 185  * Please see {@link java.beans.XMLEncoder}.
 186  *
 187  * @beaninfo
 188  *   attribute: isContainer false
 189  * description: A text component to edit various types of content.
 190  *
 191  * @author  Timothy Prinzing
 192  * @since 1.2
 193  */
 194 @SuppressWarnings("serial") // Same-version serialization only
 195 public class JEditorPane extends JTextComponent {
 196 
 197     /**
 198      * Creates a new {@code JEditorPane}.
 199      * The document model is set to {@code null}.
 200      */
 201     public JEditorPane() {
 202         super();
 203         setFocusCycleRoot(true);
 204         setFocusTraversalPolicy(new LayoutFocusTraversalPolicy() {
 205                 public Component getComponentAfter(Container focusCycleRoot,
 206                                                    Component aComponent) {
 207                     if (focusCycleRoot != JEditorPane.this ||
 208                         (!isEditable() && getComponentCount() > 0)) {
 209                         return super.getComponentAfter(focusCycleRoot,
 210                                                        aComponent);
 211                     } else {
 212                         Container rootAncestor = getFocusCycleRootAncestor();
 213                         return (rootAncestor != null)
 214                             ? rootAncestor.getFocusTraversalPolicy().
 215                                   getComponentAfter(rootAncestor,
 216                                                     JEditorPane.this)
 217                             : null;
 218                     }
 219                 }


 239                         ? super.getDefaultComponent(focusCycleRoot)
 240                         : null;
 241                 }
 242                 protected boolean accept(Component aComponent) {
 243                     return (aComponent != JEditorPane.this)
 244                         ? super.accept(aComponent)
 245                         : false;
 246                 }
 247             });
 248         LookAndFeel.installProperty(this,
 249                                     "focusTraversalKeysForward",
 250                                     JComponent.
 251                                     getManagingFocusForwardTraversalKeys());
 252         LookAndFeel.installProperty(this,
 253                                     "focusTraversalKeysBackward",
 254                                     JComponent.
 255                                     getManagingFocusBackwardTraversalKeys());
 256     }
 257 
 258     /**
 259      * Creates a {@code JEditorPane} based on a specified URL for input.
 260      *
 261      * @param initialPage the URL
 262      * @exception IOException if the URL is {@code null}
 263      *          or cannot be accessed
 264      */
 265     public JEditorPane(URL initialPage) throws IOException {
 266         this();
 267         setPage(initialPage);
 268     }
 269 
 270     /**
 271      * Creates a {@code JEditorPane} based on a string containing
 272      * a URL specification.
 273      *
 274      * @param url the URL
 275      * @exception IOException if the URL is {@code null} or
 276      *          cannot be accessed
 277      */
 278     public JEditorPane(String url) throws IOException {
 279         this();
 280         setPage(url);
 281     }
 282 
 283     /**
 284      * Creates a {@code JEditorPane} that has been initialized
 285      * to the given text.  This is a convenience constructor that calls the
 286      * {@code setContentType} and {@code setText} methods.
 287      *
 288      * @param type mime type of the given text
 289      * @param text the text to initialize with; may be {@code null}
 290      * @exception NullPointerException if the {@code type} parameter
 291      *          is {@code null}
 292      */
 293     public JEditorPane(String type, String text) {
 294         this();
 295         setContentType(type);
 296         setText(text);
 297     }
 298 
 299     /**
 300      * Adds a hyperlink listener for notification of any changes, for example
 301      * when a link is selected and entered.
 302      *
 303      * @param listener the listener
 304      */
 305     public synchronized void addHyperlinkListener(HyperlinkListener listener) {
 306         listenerList.add(HyperlinkListener.class, listener);
 307     }
 308 
 309     /**
 310      * Removes a hyperlink listener.
 311      *
 312      * @param listener the listener
 313      */
 314     public synchronized void removeHyperlinkListener(HyperlinkListener listener) {
 315         listenerList.remove(HyperlinkListener.class, listener);
 316     }
 317 
 318     /**
 319      * Returns an array of all the {@code HyperLinkListener}s added
 320      * to this JEditorPane with addHyperlinkListener().
 321      *
 322      * @return all of the {@code HyperLinkListener}s added or an empty
 323      *         array if no listeners have been added
 324      * @since 1.4
 325      */
 326     public synchronized HyperlinkListener[] getHyperlinkListeners() {
 327         return listenerList.getListeners(javax.swing.event.HyperlinkListener.class);
 328     }
 329 
 330     /**
 331      * Notifies all listeners that have registered interest for
 332      * notification on this event type.  This is normally called
 333      * by the currently installed {@code EditorKit} if a content type
 334      * that supports hyperlinks is currently active and there
 335      * was activity with a link.  The listener list is processed
 336      * last to first.
 337      *
 338      * @param e the event
 339      * @see EventListenerList
 340      */
 341     public void fireHyperlinkUpdate(HyperlinkEvent e) {
 342         // Guaranteed to return a non-null array
 343         Object[] listeners = listenerList.getListenerList();
 344         // Process the listeners last to first, notifying
 345         // those that are interested in this event
 346         for (int i = listeners.length-2; i>=0; i-=2) {
 347             if (listeners[i]==HyperlinkListener.class) {
 348                 ((HyperlinkListener)listeners[i+1]).hyperlinkUpdate(e);
 349             }
 350         }
 351     }
 352 
 353 
 354     /**
 355      * Sets the current URL being displayed.  The content type of the
 356      * pane is set, and if the editor kit for the pane is
 357      * non-{@code null}, then
 358      * a new default document is created and the URL is read into it.
 359      * If the URL contains and reference location, the location will
 360      * be scrolled to by calling the {@code scrollToReference}
 361      * method. If the desired URL is the one currently being displayed,
 362      * the document will not be reloaded. To force a document
 363      * reload it is necessary to clear the stream description property
 364      * of the document. The following code shows how this can be done:
 365      *
 366      * <pre>
 367      *   Document doc = jEditorPane.getDocument();
 368      *   doc.putProperty(Document.StreamDescriptionProperty, null);
 369      * </pre>
 370      *
 371      * If the desired URL is not the one currently being
 372      * displayed, the {@code getStream} method is called to
 373      * give subclasses control over the stream provided.
 374      * <p>
 375      * This may load either synchronously or asynchronously
 376      * depending upon the document returned by the {@code EditorKit}.
 377      * If the {@code Document} is of type
 378      * {@code AbstractDocument} and has a value returned by
 379      * {@code AbstractDocument.getAsynchronousLoadPriority}
 380      * that is greater than or equal to zero, the page will be
 381      * loaded on a separate thread using that priority.
 382      * <p>
 383      * If the document is loaded synchronously, it will be
 384      * filled in with the stream prior to being installed into
 385      * the editor with a call to {@code setDocument}, which
 386      * is bound and will fire a property change event.  If an
 387      * {@code IOException} is thrown the partially loaded
 388      * document will
 389      * be discarded and neither the document or page property
 390      * change events will be fired.  If the document is
 391      * successfully loaded and installed, a view will be
 392      * built for it by the UI which will then be scrolled if
 393      * necessary, and then the page property change event
 394      * will be fired.
 395      * <p>
 396      * If the document is loaded asynchronously, the document
 397      * will be installed into the editor immediately using a
 398      * call to {@code setDocument} which will fire a
 399      * document property change event, then a thread will be
 400      * created which will begin doing the actual loading.
 401      * In this case, the page property change event will not be
 402      * fired by the call to this method directly, but rather will be
 403      * fired when the thread doing the loading has finished.
 404      * It will also be fired on the event-dispatch thread.
 405      * Since the calling thread can not throw an {@code IOException}
 406      * in the event of failure on the other thread, the page
 407      * property change event will be fired when the other
 408      * thread is done whether the load was successful or not.
 409      *
 410      * @param page the URL of the page
 411      * @exception IOException for a {@code null} or invalid
 412      *          page specification, or exception from the stream being read
 413      * @see #getPage
 414      * @beaninfo
 415      *  description: the URL used to set content
 416      *        bound: true
 417      *       expert: true
 418      */
 419     public void setPage(URL page) throws IOException {
 420         if (page == null) {
 421             throw new IOException("invalid url");
 422         }
 423         URL loaded = getPage();
 424 
 425 
 426         // reset scrollbar
 427         if (!page.equals(loaded) && page.getRef() == null) {
 428             scrollRectToVisible(new Rectangle(0,0,1,1));
 429         }
 430         boolean reloaded = false;
 431         Object postData = getPostData();


 501                 doc.putProperty(key, pageProperties.get(key));
 502             }
 503             pageProperties.clear();
 504         }
 505         if (doc.getProperty(Document.StreamDescriptionProperty) == null) {
 506             doc.putProperty(Document.StreamDescriptionProperty, page);
 507         }
 508         return doc;
 509     }
 510 
 511     /**
 512      * Return load priority for the document or -1 if priority not supported.
 513      */
 514     private int getAsynchronousLoadPriority(Document doc) {
 515         return (doc instanceof AbstractDocument ?
 516             ((AbstractDocument) doc).getAsynchronousLoadPriority() : -1);
 517     }
 518 
 519     /**
 520      * This method initializes from a stream.  If the kit is
 521      * set to be of type {@code HTMLEditorKit}, and the
 522      * {@code desc} parameter is an {@code HTMLDocument},
 523      * then it invokes the {@code HTMLEditorKit} to initiate
 524      * the read. Otherwise it calls the superclass
 525      * method which loads the model as plain text.
 526      *
 527      * @param in the stream from which to read
 528      * @param desc an object describing the stream
 529      * @exception IOException as thrown by the stream being
 530      *          used to initialize
 531      * @see JTextComponent#read
 532      * @see #setDocument
 533      */
 534     public void read(InputStream in, Object desc) throws IOException {
 535 
 536         if (desc instanceof HTMLDocument &&
 537             kit instanceof HTMLEditorKit) {
 538             HTMLDocument hdoc = (HTMLDocument) desc;
 539             setDocument(hdoc);
 540             read(in, hdoc);
 541         } else {
 542             String charset = (String) getClientProperty("charset");
 543             Reader r = (charset != null) ? new InputStreamReader(in, charset) :
 544                 new InputStreamReader(in);
 545             super.read(r, desc);
 546         }
 547     }
 548 
 549 
 550     /**
 551      * This method invokes the {@code EditorKit} to initiate a
 552      * read.  In the case where a {@code ChangedCharSetException}
 553      * is thrown this exception will contain the new CharSet.
 554      * Therefore the {@code read} operation
 555      * is then restarted after building a new Reader with the new charset.
 556      *
 557      * @param in the inputstream to use
 558      * @param doc the document to load
 559      *
 560      */
 561     void read(InputStream in, Document doc) throws IOException {
 562         if (! Boolean.TRUE.equals(doc.getProperty("IgnoreCharsetDirective"))) {
 563             final int READ_LIMIT = 1024 * 10;
 564             in = new BufferedInputStream(in, READ_LIMIT);
 565             in.mark(READ_LIMIT);
 566         }
 567         try {
 568             String charset = (String) getClientProperty("charset");
 569             Reader r = (charset != null) ? new InputStreamReader(in, charset) :
 570                 new InputStreamReader(in);
 571             kit.read(r, doc, 0);
 572         } catch (BadLocationException e) {
 573             throw new IOException(e.getMessage());
 574         } catch (ChangedCharSetException changedCharSetException) {


 693         /**
 694          * URL of the old page that was replaced (for the property change event)
 695          */
 696         URL old;
 697 
 698         /**
 699          * URL of the page being loaded (for the property change event)
 700          */
 701         URL page;
 702 
 703         /**
 704          * The Document instance to load into. This is cached in case a
 705          * new Document is created between the time the thread this is created
 706          * and run.
 707          */
 708         Document doc;
 709     }
 710 
 711     /**
 712      * Fetches a stream for the given URL, which is about to
 713      * be loaded by the {@code setPage} method.  By
 714      * default, this simply opens the URL and returns the
 715      * stream.  This can be reimplemented to do useful things
 716      * like fetch the stream from a cache, monitor the progress
 717      * of the stream, etc.
 718      * <p>
 719      * This method is expected to have the side effect of
 720      * establishing the content type, and therefore setting the
 721      * appropriate {@code EditorKit} to use for loading the stream.
 722      * <p>
 723      * If this the stream was an http connection, redirects
 724      * will be followed and the resulting URL will be set as
 725      * the {@code Document.StreamDescriptionProperty} so that relative
 726      * URL's can be properly resolved.
 727      *
 728      * @param page  the URL of the page
 729      * @return a stream for the URL which is about to be loaded
 730      * @throws IOException if an I/O problem occurs
 731      */
 732     protected InputStream getStream(URL page) throws IOException {
 733         final URLConnection conn = page.openConnection();
 734         if (conn instanceof HttpURLConnection) {
 735             HttpURLConnection hconn = (HttpURLConnection) conn;
 736             hconn.setInstanceFollowRedirects(false);
 737             Object postData = getPostData();
 738             if (postData != null) {
 739                 handlePostData(hconn, postData);
 740             }
 741             int response = hconn.getResponseCode();
 742             boolean redirect = (response >= 300 && response <= 399);
 743 
 744             /*
 745              * In the case of a redirect, we want to actually change the URL


 801 
 802     private void handlePostData(HttpURLConnection conn, Object postData)
 803                                                             throws IOException {
 804         conn.setDoOutput(true);
 805         DataOutputStream os = null;
 806         try {
 807             conn.setRequestProperty("Content-Type",
 808                     "application/x-www-form-urlencoded");
 809             os = new DataOutputStream(conn.getOutputStream());
 810             os.writeBytes((String) postData);
 811         } finally {
 812             if (os != null) {
 813                 os.close();
 814             }
 815         }
 816     }
 817 
 818 
 819     /**
 820      * Scrolls the view to the given reference location
 821      * (that is, the value returned by the {@code UL.getRef}
 822      * method for the URL being displayed).  By default, this
 823      * method only knows how to locate a reference in an
 824      * HTMLDocument.  The implementation calls the
 825      * {@code scrollRectToVisible} method to
 826      * accomplish the actual scrolling.  If scrolling to a
 827      * reference location is needed for document types other
 828      * than HTML, this method should be reimplemented.
 829      * This method will have no effect if the component
 830      * is not visible.
 831      *
 832      * @param reference the named location to scroll to
 833      */
 834     public void scrollToReference(String reference) {
 835         Document d = getDocument();
 836         if (d instanceof HTMLDocument) {
 837             HTMLDocument doc = (HTMLDocument) d;
 838             HTMLDocument.Iterator iter = doc.getIterator(HTML.Tag.A);
 839             for (; iter.isValid(); iter.next()) {
 840                 AttributeSet a = iter.getAttributes();
 841                 String nm = (String) a.getAttribute(HTML.Attribute.NAME);
 842                 if ((nm != null) && nm.equals(reference)) {
 843                     // found a matching reference in the document.
 844                     try {
 845                         int pos = iter.getStartOffset();


 847                         if (r != null) {
 848                             // the view is visible, scroll it to the
 849                             // center of the current visible area.
 850                             Rectangle vis = getVisibleRect();
 851                             //r.y -= (vis.height / 2);
 852                             r.height = vis.height;
 853                             scrollRectToVisible(r);
 854                             setCaretPosition(pos);
 855                         }
 856                     } catch (BadLocationException ble) {
 857                         UIManager.getLookAndFeel().provideErrorFeedback(JEditorPane.this);
 858                     }
 859                 }
 860             }
 861         }
 862     }
 863 
 864     /**
 865      * Gets the current URL being displayed.  If a URL was
 866      * not specified in the creation of the document, this
 867      * will return {@code null}, and relative URL's will not be
 868      * resolved.
 869      *
 870      * @return the URL, or {@code null} if none
 871      */
 872     public URL getPage() {
 873         return (URL) getDocument().getProperty(Document.StreamDescriptionProperty);
 874     }
 875 
 876     /**
 877      * Sets the current URL being displayed.
 878      *
 879      * @param url the URL for display
 880      * @exception IOException for a {@code null} or invalid URL
 881      *          specification
 882      */
 883     public void setPage(String url) throws IOException {
 884         if (url == null) {
 885             throw new IOException("invalid url");
 886         }
 887         URL page = new URL(url);
 888         setPage(page);
 889     }
 890 
 891     /**
 892      * Gets the class ID for the UI.
 893      *
 894      * @return the string "EditorPaneUI"
 895      * @see JComponent#getUIClassID
 896      * @see UIDefaults#getUI
 897      */
 898     public String getUIClassID() {
 899         return uiClassID;
 900     }
 901 
 902     /**
 903      * Creates the default editor kit ({@code PlainEditorKit}) for when
 904      * the component is first created.
 905      *
 906      * @return the editor kit
 907      */
 908     protected EditorKit createDefaultEditorKit() {
 909         return new PlainEditorKit();
 910     }
 911 
 912     /**
 913      * Fetches the currently installed kit for handling content.
 914      * {@code createDefaultEditorKit} is called to set up a default
 915      * if necessary.
 916      *
 917      * @return the editor kit
 918      */
 919     public EditorKit getEditorKit() {
 920         if (kit == null) {
 921             kit = createDefaultEditorKit();
 922             isUserSetEditorKit = false;
 923         }
 924         return kit;
 925     }
 926 
 927     /**
 928      * Gets the type of content that this editor
 929      * is currently set to deal with.  This is
 930      * defined to be the type associated with the
 931      * currently installed {@code EditorKit}.
 932      *
 933      * @return the content type, {@code null} if no editor kit set
 934      */
 935     public final String getContentType() {
 936         return (kit != null) ? kit.getContentType() : null;
 937     }
 938 
 939     /**
 940      * Sets the type of content that this editor
 941      * handles.  This calls {@code getEditorKitForContentType},
 942      * and then {@code setEditorKit} if an editor kit can
 943      * be successfully located.  This is mostly convenience method
 944      * that can be used as an alternative to calling
 945      * {@code setEditorKit} directly.
 946      * <p>
 947      * If there is a charset definition specified as a parameter
 948      * of the content type specification, it will be used when
 949      * loading input streams using the associated {@code EditorKit}.
 950      * For example if the type is specified as
 951      * {@code text/html; charset=EUC-JP} the content
 952      * will be loaded using the {@code EditorKit} registered for
 953      * {@code text/html} and the Reader provided to
 954      * the {@code EditorKit} to load unicode into the document will
 955      * use the {@code EUC-JP} charset for translating
 956      * to unicode.  If the type is not recognized, the content
 957      * will be loaded using the {@code EditorKit} registered
 958      * for plain text, {@code text/plain}.
 959      *
 960      * @param type the non-{@code null} mime type for the content editing
 961      *   support
 962      * @see #getContentType
 963      * @beaninfo
 964      *  description: the type of content
 965      * @throws NullPointerException if the {@code type} parameter
 966      *          is {@code null}
 967      */
 968     public final void setContentType(String type) {
 969         // The type could have optional info is part of it,
 970         // for example some charset info.  We need to strip that
 971         // of and save it.
 972         int parm = type.indexOf(';');
 973         if (parm > -1) {
 974             // Save the paramList.
 975             String paramList = type.substring(parm);
 976             // update the content type string.
 977             type = type.substring(0, parm).trim();
 978             if (type.toLowerCase().startsWith("text/")) {
 979                 setCharsetFromContentTypeParameters(paramList);
 980             }
 981         }
 982         if ((kit == null) || (! type.equals(kit.getContentType()))
 983                 || !isUserSetEditorKit) {
 984             EditorKit k = getEditorKitForContentType(type);
 985             if (k != null && k != kit) {
 986                 setEditorKit(k);


1015         }
1016         catch (IndexOutOfBoundsException e) {
1017             // malformed parameter list, use charset we have
1018         }
1019         catch (NullPointerException e) {
1020             // malformed parameter list, use charset we have
1021         }
1022         catch (Exception e) {
1023             // malformed parameter list, use charset we have; but complain
1024             System.err.println("JEditorPane.getCharsetFromContentTypeParameters failed on: " + paramlist);
1025             e.printStackTrace();
1026         }
1027     }
1028 
1029 
1030     /**
1031      * Sets the currently installed kit for handling
1032      * content.  This is the bound property that
1033      * establishes the content type of the editor.
1034      * Any old kit is first deinstalled, then if kit is
1035      * non-{@code null},
1036      * the new kit is installed, and a default document created for it.
1037      * A {@code PropertyChange} event ("editorKit") is always fired when
1038      * {@code setEditorKit} is called.
1039      * <p>
1040      * <em>NOTE: This has the side effect of changing the model,
1041      * because the {@code EditorKit} is the source of how a
1042      * particular type
1043      * of content is modeled.  This method will cause {@code setDocument}
1044      * to be called on behalf of the caller to ensure integrity
1045      * of the internal state.</em>
1046      *
1047      * @param kit the desired editor behavior
1048      * @see #getEditorKit
1049      * @beaninfo
1050      *  description: the currently installed kit for handling content
1051      *        bound: true
1052      *       expert: true
1053      */
1054     public void setEditorKit(EditorKit kit) {
1055         EditorKit old = this.kit;
1056         isUserSetEditorKit = true;
1057         if (old != null) {
1058             old.deinstall(this);
1059         }
1060         this.kit = kit;
1061         if (this.kit != null) {
1062             this.kit.install(this);
1063             setDocument(this.kit.createDefaultDocument());
1064         }
1065         firePropertyChange("editorKit", old, kit);
1066     }
1067 
1068     /**
1069      * Fetches the editor kit to use for the given type
1070      * of content.  This is called when a type is requested
1071      * that doesn't match the currently installed type.
1072      * If the component doesn't have an {@code EditorKit} registered
1073      * for the given type, it will try to create an
1074      * {@code EditorKit} from the default {@code EditorKit} registry.
1075      * If that fails, a {@code PlainEditorKit} is used on the
1076      * assumption that all text documents can be represented
1077      * as plain text.
1078      * <p>
1079      * This method can be reimplemented to use some
1080      * other kind of type registry.  This can
1081      * be reimplemented to use the Java Activation
1082      * Framework, for example.
1083      *
1084      * @param type the non-{@code null} content type
1085      * @return the editor kit
1086      */
1087     public EditorKit getEditorKitForContentType(String type) {
1088         if (typeHandlers == null) {
1089             typeHandlers = new Hashtable<String, EditorKit>(3);
1090         }
1091         EditorKit k = typeHandlers.get(type);
1092         if (k == null) {
1093             k = createEditorKitForContentType(type);
1094             if (k != null) {
1095                 setEditorKitForContentType(type, k);
1096             }
1097         }
1098         if (k == null) {
1099             k = createDefaultEditorKit();
1100         }
1101         return k;
1102     }
1103 
1104     /**
1105      * Directly sets the editor kit to use for the given type.  A
1106      * look-and-feel implementation might use this in conjunction
1107      * with {@code createEditorKitForContentType} to install handlers for
1108      * content types with a look-and-feel bias.
1109      *
1110      * @param type the non-{@code null} content type
1111      * @param k the editor kit to be set
1112      */
1113     public void setEditorKitForContentType(String type, EditorKit k) {
1114         if (typeHandlers == null) {
1115             typeHandlers = new Hashtable<String, EditorKit>(3);
1116         }
1117         typeHandlers.put(type, k);
1118     }
1119 
1120     /**
1121      * Replaces the currently selected content with new content
1122      * represented by the given string.  If there is no selection
1123      * this amounts to an insert of the given text.  If there
1124      * is no replacement text (i.e. the content string is empty
1125      * or {@code null}) this amounts to a removal of the
1126      * current selection.  The replacement text will have the
1127      * attributes currently defined for input.  If the component is not
1128      * editable, beep and return.
1129      *
1130      * @param content  the content to replace the selection with.  This
1131      *   value can be {@code null}
1132      */
1133     @Override
1134     public void replaceSelection(String content) {
1135         if (! isEditable()) {
1136             UIManager.getLookAndFeel().provideErrorFeedback(JEditorPane.this);
1137             return;
1138         }
1139         EditorKit kit = getEditorKit();
1140         if(kit instanceof StyledEditorKit) {
1141             try {
1142                 Document doc = getDocument();
1143                 Caret caret = getCaret();
1144                 boolean composedTextSaved = saveComposedText(caret.getDot());
1145                 int p0 = Math.min(caret.getDot(), caret.getMark());
1146                 int p1 = Math.max(caret.getDot(), caret.getMark());
1147                 if (doc instanceof AbstractDocument) {
1148                     ((AbstractDocument)doc).replace(p0, p1 - p0, content,
1149                               ((StyledEditorKit)kit).getInputAttributes());
1150                 }
1151                 else {


1157                                          getInputAttributes());
1158                     }
1159                 }
1160                 if (composedTextSaved) {
1161                     restoreComposedText();
1162                 }
1163             } catch (BadLocationException e) {
1164                 UIManager.getLookAndFeel().provideErrorFeedback(JEditorPane.this);
1165             }
1166         }
1167         else {
1168             super.replaceSelection(content);
1169         }
1170     }
1171 
1172     /**
1173      * Creates a handler for the given type from the default registry
1174      * of editor kits.  The registry is created if necessary.  If the
1175      * registered class has not yet been loaded, an attempt
1176      * is made to dynamically load the prototype of the kit for the
1177      * given type.  If the type was registered with a {@code ClassLoader},
1178      * that {@code ClassLoader} will be used to load the prototype.
1179      * If there was no registered {@code ClassLoader},
1180      * {@code Class.forName} will be used to load the prototype.
1181      * <p>
1182      * Once a prototype {@code EditorKit} instance is successfully
1183      * located, it is cloned and the clone is returned.
1184      *
1185      * @param type the content type
1186      * @return the editor kit, or {@code null} if there is nothing
1187      *   registered for the given type
1188      */
1189     public static EditorKit createEditorKitForContentType(String type) {
1190         Hashtable<String, EditorKit> kitRegistry = getKitRegisty();
1191         EditorKit k = kitRegistry.get(type);
1192         if (k == null) {
1193             // try to dynamically load the support
1194             String classname = getKitTypeRegistry().get(type);
1195             ClassLoader loader = getKitLoaderRegistry().get(type);
1196             try {
1197                 Class<?> c;
1198                 if (loader != null) {
1199                     ReflectUtil.checkPackageAccess(classname);
1200                     c = loader.loadClass(classname);
1201                 } else {
1202                     // Will only happen if developer has invoked
1203                     // registerEditorKitForContentType(type, class, null).
1204                     c = SwingUtilities.loadSystemClass(classname);
1205                 }
1206                 k = (EditorKit) c.newInstance();
1207                 kitRegistry.put(type, k);
1208             } catch (Throwable e) {
1209                 k = null;
1210             }
1211         }
1212 
1213         // create a copy of the prototype or null if there
1214         // is no prototype.
1215         if (k != null) {
1216             return (EditorKit) k.clone();
1217         }
1218         return null;
1219     }
1220 
1221     /**
1222      * Establishes the default bindings of {@code type} to
1223      * {@code classname}.
1224      * The class will be dynamically loaded later when actually
1225      * needed, and can be safely changed before attempted uses
1226      * to avoid loading unwanted classes.  The prototype
1227      * {@code EditorKit} will be loaded with {@code Class.forName}
1228      * when registered with this method.
1229      *
1230      * @param type the non-{@code null} content type
1231      * @param classname the class to load later
1232      */
1233     public static void registerEditorKitForContentType(String type, String classname) {
1234         registerEditorKitForContentType(type, classname,Thread.currentThread().
1235                                         getContextClassLoader());
1236     }
1237 
1238     /**
1239      * Establishes the default bindings of {@code type} to
1240      * {@code classname}.
1241      * The class will be dynamically loaded later when actually
1242      * needed using the given {@code ClassLoader},
1243      * and can be safely changed
1244      * before attempted uses to avoid loading unwanted classes.
1245      *
1246      * @param type the non-{@code null} content type
1247      * @param classname the class to load later
1248      * @param loader the {@code ClassLoader} to use to load the name
1249      */
1250     public static void registerEditorKitForContentType(String type, String classname, ClassLoader loader) {
1251         getKitTypeRegistry().put(type, classname);
1252         getKitLoaderRegistry().put(type, loader);
1253         getKitRegisty().remove(type);
1254     }
1255 
1256     /**
1257      * Returns the currently registered {@code EditorKit} class name for the
1258      * type {@code type}.
1259      *
1260      * @param type  the non-{@code null} content type
1261      * @return a {@code String} containing the {@code EditorKit} class name
1262      *         for {@code type}
1263      * @since 1.3
1264      */
1265     public static String getEditorKitClassNameForContentType(String type) {
1266         return getKitTypeRegistry().get(type);
1267     }
1268 


1310                     defaultEditorKitMap.put("text/rtf",
1311                                             "javax.swing.text.rtf.RTFEditorKit");
1312                     defaultEditorKitMap.put("application/rtf",
1313                                             "javax.swing.text.rtf.RTFEditorKit");
1314                 }
1315             }
1316             Hashtable<Object, Object> ht = new Hashtable<>();
1317             SwingUtilities.appContextPut(kitTypeRegistryKey, ht);
1318             ht = new Hashtable<>();
1319             SwingUtilities.appContextPut(kitLoaderRegistryKey, ht);
1320             for (String key : defaultEditorKitMap.keySet()) {
1321                 registerEditorKitForContentType(key,defaultEditorKitMap.get(key));
1322             }
1323 
1324         }
1325     }
1326 
1327     // --- java.awt.Component methods --------------------------
1328 
1329     /**
1330      * Returns the preferred size for the {@code JEditorPane}.
1331      * The preferred size for {@code JEditorPane} is slightly altered
1332      * from the preferred size of the superclass.  If the size
1333      * of the viewport has become smaller than the minimum size
1334      * of the component, the scrollable definition for tracking
1335      * width or height will turn to false.  The default viewport
1336      * layout will give the preferred size, and that is not desired
1337      * in the case where the scrollable is tracking.  In that case
1338      * the <em>normal</em> preferred size is adjusted to the
1339      * minimum size.  This allows things like HTML tables to
1340      * shrink down to their minimum size and then be laid out at
1341      * their minimum size, refusing to shrink any further.
1342      *
1343      * @return a {@code Dimension} containing the preferred size
1344      */
1345     public Dimension getPreferredSize() {
1346         Dimension d = super.getPreferredSize();
1347         Container parent = SwingUtilities.getUnwrappedParent(this);
1348         if (parent instanceof JViewport) {
1349             JViewport port = (JViewport) parent;
1350             TextUI ui = getUI();
1351             int prefWidth = d.width;
1352             int prefHeight = d.height;
1353             if (! getScrollableTracksViewportWidth()) {
1354                 int w = port.getWidth();
1355                 Dimension min = ui.getMinimumSize(this);
1356                 if (w != 0 && w < min.width) {
1357                     // Only adjust to min if we have a valid size
1358                     prefWidth = min.width;
1359                 }
1360             }
1361             if (! getScrollableTracksViewportHeight()) {
1362                 int h = port.getHeight();
1363                 Dimension min = ui.getMinimumSize(this);
1364                 if (h != 0 && h < min.height) {
1365                     // Only adjust to min if we have a valid size
1366                     prefHeight = min.height;
1367                 }
1368             }
1369             if (prefWidth != d.width || prefHeight != d.height) {
1370                 d = new Dimension(prefWidth, prefHeight);
1371             }
1372         }
1373         return d;
1374     }
1375 
1376     // --- JTextComponent methods -----------------------------
1377 
1378     /**
1379      * Sets the text of this {@code TextComponent} to the specified
1380      * content,
1381      * which is expected to be in the format of the content type of
1382      * this editor.  For example, if the type is set to {@code text/html}
1383      * the string should be specified in terms of HTML.
1384      * <p>
1385      * This is implemented to remove the contents of the current document,
1386      * and replace them by parsing the given string using the current
1387      * {@code EditorKit}.  This gives the semantics of the
1388      * superclass by not changing
1389      * out the model, while supporting the content type currently set on
1390      * this component.  The assumption is that the previous content is
1391      * relatively
1392      * small, and that the previous content doesn't have side effects.
1393      * Both of those assumptions can be violated and cause undesirable results.
1394      * To avoid this, create a new document,
1395      * {@code getEditorKit().createDefaultDocument()}, and replace the
1396      * existing {@code Document} with the new one. You are then assured the
1397      * previous {@code Document} won't have any lingering state.
1398      * <ol>
1399      * <li>
1400      * Leaving the existing model in place means that the old view will be
1401      * torn down, and a new view created, where replacing the document would
1402      * avoid the tear down of the old view.
1403      * <li>
1404      * Some formats (such as HTML) can install things into the document that
1405      * can influence future contents.  HTML can have style information embedded
1406      * that would influence the next content installed unexpectedly.
1407      * </ol>
1408      * <p>
1409      * An alternative way to load this component with a string would be to
1410      * create a StringReader and call the read method.  In this case the model
1411      * would be replaced after it was initialized with the contents of the
1412      * string.
1413      *
1414      * @param t the new text to be set; if {@code null} the old
1415      *    text will be deleted
1416      * @see #getText
1417      * @beaninfo
1418      * description: the text of this component
1419      */
1420     public void setText(String t) {
1421         try {
1422             Document doc = getDocument();
1423             doc.remove(0, doc.getLength());
1424             if (t == null || t.equals("")) {
1425                 return;
1426             }
1427             Reader r = new StringReader(t);
1428             EditorKit kit = getEditorKit();
1429             kit.read(r, doc, 0);
1430         } catch (IOException ioe) {
1431             UIManager.getLookAndFeel().provideErrorFeedback(JEditorPane.this);
1432         } catch (BadLocationException ble) {
1433             UIManager.getLookAndFeel().provideErrorFeedback(JEditorPane.this);
1434         }
1435     }
1436 
1437     /**
1438      * Returns the text contained in this {@code TextComponent}
1439      * in terms of the
1440      * content type of this editor.  If an exception is thrown while
1441      * attempting to retrieve the text, {@code null} will be returned.
1442      * This is implemented to call {@code JTextComponent.write} with
1443      * a {@code StringWriter}.
1444      *
1445      * @return the text
1446      * @see #setText
1447      */
1448     public String getText() {
1449         String txt;
1450         try {
1451             StringWriter buf = new StringWriter();
1452             write(buf);
1453             txt = buf.toString();
1454         } catch (IOException ioe) {
1455             txt = null;
1456         }
1457         return txt;
1458     }
1459 
1460     // --- Scrollable  ----------------------------------------
1461 
1462     /**
1463      * Returns true if a viewport should always force the width of this
1464      * {@code Scrollable} to match the width of the viewport.
1465      *
1466      * @return true if a viewport should force the Scrollables width to
1467      * match its own, false otherwise
1468      */
1469     public boolean getScrollableTracksViewportWidth() {
1470         Container parent = SwingUtilities.getUnwrappedParent(this);
1471         if (parent instanceof JViewport) {
1472             JViewport port = (JViewport) parent;
1473             TextUI ui = getUI();
1474             int w = port.getWidth();
1475             Dimension min = ui.getMinimumSize(this);
1476             Dimension max = ui.getMaximumSize(this);
1477             if ((w >= min.width) && (w <= max.width)) {
1478                 return true;
1479             }
1480         }
1481         return false;
1482     }
1483 
1484     /**
1485      * Returns true if a viewport should always force the height of this
1486      * {@code Scrollable} to match the height of the viewport.
1487      *
1488      * @return true if a viewport should force the
1489      *          {@code Scrollable}'s height to match its own,
1490      *          false otherwise
1491      */
1492     public boolean getScrollableTracksViewportHeight() {
1493         Container parent = SwingUtilities.getUnwrappedParent(this);
1494         if (parent instanceof JViewport) {
1495             JViewport port = (JViewport) parent;
1496             TextUI ui = getUI();
1497             int h = port.getHeight();
1498             Dimension min = ui.getMinimumSize(this);
1499             if (h >= min.height) {
1500                 Dimension max = ui.getMaximumSize(this);
1501                 if (h <= max.height) {
1502                     return true;
1503                 }
1504             }
1505         }
1506         return false;
1507     }
1508 
1509     // --- Serialization ------------------------------------
1510 
1511     /**
1512      * See {@code readObject} and {@code writeObject} in
1513      * {@code JComponent} for more
1514      * information about serialization in Swing.
1515      */
1516     private void writeObject(ObjectOutputStream s) throws IOException {
1517         s.defaultWriteObject();
1518         if (getUIClassID().equals(uiClassID)) {
1519             byte count = JComponent.getWriteObjCounter(this);
1520             JComponent.setWriteObjCounter(this, --count);
1521             if (count == 0 && ui != null) {
1522                 ui.installUI(this);
1523             }
1524         }
1525     }
1526 
1527     // --- variables ---------------------------------------
1528 
1529     private SwingWorker<URL, Object> pageLoader;
1530 
1531     /**
1532      * Current content binding of the editor.
1533      */


1551         new StringBuffer("JEditorPane.kitRegistry");
1552     private static final Object kitTypeRegistryKey =
1553         new StringBuffer("JEditorPane.kitTypeRegistry");
1554     private static final Object kitLoaderRegistryKey =
1555         new StringBuffer("JEditorPane.kitLoaderRegistry");
1556 
1557     /**
1558      * @see #getUIClassID
1559      * @see #readObject
1560      */
1561     private static final String uiClassID = "EditorPaneUI";
1562 
1563 
1564     /**
1565      * Key for a client property used to indicate whether
1566      * <a href="http://www.w3.org/TR/CSS21/syndata.html#length-units">
1567      * w3c compliant</a> length units are used for html rendering.
1568      * <p>
1569      * By default this is not enabled; to enable
1570      * it set the client {@link #putClientProperty property} with this name
1571      * to {@code Boolean.TRUE}.
1572      *
1573      * @since 1.5
1574      */
1575     public static final String W3C_LENGTH_UNITS = "JEditorPane.w3cLengthUnits";
1576 
1577     /**
1578      * Key for a client property used to indicate whether
1579      * the default font and foreground color from the component are
1580      * used if a font or foreground color is not specified in the styled
1581      * text.
1582      * <p>
1583      * The default varies based on the look and feel;
1584      * to enable it set the client {@link #putClientProperty property} with
1585      * this name to {@code Boolean.TRUE}.
1586      *
1587      * @since 1.5
1588      */
1589     public static final String HONOR_DISPLAY_PROPERTIES = "JEditorPane.honorDisplayProperties";
1590 
1591     static final Map<String, String> defaultEditorKitMap = new HashMap<String, String>(0);
1592 
1593     /**
1594      * Returns a string representation of this {@code JEditorPane}.
1595      * This method
1596      * is intended to be used only for debugging purposes, and the
1597      * content and format of the returned string may vary between
1598      * implementations. The returned string may be empty but may not
1599      * be {@code null}.
1600      *
1601      * @return  a string representation of this {@code JEditorPane}
1602      */
1603     protected String paramString() {
1604         String kitString = (kit != null ?
1605                             kit.toString() : "");
1606         String typeHandlersString = (typeHandlers != null ?
1607                                      typeHandlers.toString() : "");
1608 
1609         return super.paramString() +
1610         ",kit=" + kitString +
1611         ",typeHandlers=" + typeHandlersString;
1612     }
1613 
1614 
1615 /////////////////
1616 // Accessibility support
1617 ////////////////
1618 
1619 
1620     /**
1621      * Gets the AccessibleContext associated with this JEditorPane.


1624      * A new AccessibleJEditorPane instance is created if necessary.
1625      *
1626      * @return an AccessibleJEditorPane that serves as the
1627      *         AccessibleContext of this JEditorPane
1628      */
1629     public AccessibleContext getAccessibleContext() {
1630         if (getEditorKit() instanceof HTMLEditorKit) {
1631             if (accessibleContext == null || accessibleContext.getClass() !=
1632                     AccessibleJEditorPaneHTML.class) {
1633                 accessibleContext = new AccessibleJEditorPaneHTML();
1634             }
1635         } else if (accessibleContext == null || accessibleContext.getClass() !=
1636                        AccessibleJEditorPane.class) {
1637             accessibleContext = new AccessibleJEditorPane();
1638         }
1639         return accessibleContext;
1640     }
1641 
1642     /**
1643      * This class implements accessibility support for the
1644      * {@code JEditorPane} class.  It provides an implementation of the
1645      * Java Accessibility API appropriate to editor pane user-interface
1646      * elements.
1647      * <p>
1648      * <strong>Warning:</strong>
1649      * Serialized objects of this class will not be compatible with
1650      * future Swing releases. The current serialization support is
1651      * appropriate for short term storage or RMI between applications running
1652      * the same version of Swing.  As of 1.4, support for long term storage
1653      * of all JavaBeans&trade;
1654      * has been added to the {@code java.beans} package.
1655      * Please see {@link java.beans.XMLEncoder}.
1656      */
1657     @SuppressWarnings("serial") // Same-version serialization only
1658     protected class AccessibleJEditorPane extends AccessibleJTextComponent {
1659 
1660         /**
1661          * Gets the accessibleDescription property of this object.  If this
1662          * property isn't set, returns the content type of this
1663          * {@code JEditorPane} instead (e.g. "plain/text", "html/text").
1664          *
1665          * @return the localized description of the object; {@code null}
1666          *      if this object does not have a description
1667          *
1668          * @see #setAccessibleName
1669          */
1670         public String getAccessibleDescription() {
1671             String description = accessibleDescription;
1672 
1673             // fallback to client property
1674             if (description == null) {
1675                 description = (String)getClientProperty(AccessibleContext.ACCESSIBLE_DESCRIPTION_PROPERTY);
1676             }
1677             if (description == null) {
1678                 description = JEditorPane.this.getContentType();
1679             }
1680             return description;
1681         }
1682 
1683         /**
1684          * Gets the state set of this object.
1685          *
1686          * @return an instance of AccessibleStateSet describing the states
1687          * of the object
1688          * @see AccessibleStateSet
1689          */
1690         public AccessibleStateSet getAccessibleStateSet() {
1691             AccessibleStateSet states = super.getAccessibleStateSet();
1692             states.add(AccessibleState.MULTI_LINE);
1693             return states;
1694         }
1695     }
1696 
1697     /**
1698      * This class provides support for {@code AccessibleHypertext},
1699      * and is used in instances where the {@code EditorKit}
1700      * installed in this {@code JEditorPane} is an instance of
1701      * {@code HTMLEditorKit}.
1702      * <p>
1703      * <strong>Warning:</strong>
1704      * Serialized objects of this class will not be compatible with
1705      * future Swing releases. The current serialization support is
1706      * appropriate for short term storage or RMI between applications running
1707      * the same version of Swing.  As of 1.4, support for long term storage
1708      * of all JavaBeans&trade;
1709      * has been added to the {@code java.beans} package.
1710      * Please see {@link java.beans.XMLEncoder}.
1711      */
1712     @SuppressWarnings("serial") // Same-version serialization only
1713     protected class AccessibleJEditorPaneHTML extends AccessibleJEditorPane {
1714 
1715         private AccessibleContext accessibleContext;
1716 
1717         /**
1718          * Returns the accessible text.
1719          * @return the accessible text
1720          */
1721         public AccessibleText getAccessibleText() {
1722             return new JEditorPaneAccessibleHypertextSupport();
1723         }
1724 
1725         /**
1726          * Constructs an {@code AccessibleJEditorPaneHTML}.
1727          */
1728         protected AccessibleJEditorPaneHTML () {
1729             HTMLEditorKit kit = (HTMLEditorKit)JEditorPane.this.getEditorKit();


1773             if (accessibleContext != null && p != null) {
1774                 try {
1775                     AccessibleComponent acomp =
1776                         accessibleContext.getAccessibleComponent();
1777                     if (acomp != null) {
1778                         return acomp.getAccessibleAt(p);
1779                     } else {
1780                         return null;
1781                     }
1782                 } catch (IllegalComponentStateException e) {
1783                     return null;
1784                 }
1785             } else {
1786                 return null;
1787             }
1788         }
1789     }
1790 
1791     /**
1792      * What's returned by
1793      * {@code AccessibleJEditorPaneHTML.getAccessibleText}.
1794      *
1795      * Provides support for {@code AccessibleHypertext} in case
1796      * there is an HTML document being displayed in this
1797      * {@code JEditorPane}.
1798      *
1799      */
1800     protected class JEditorPaneAccessibleHypertextSupport
1801     extends AccessibleJEditorPane implements AccessibleHypertext {
1802 
1803         /**
1804          * An HTML link.
1805          */
1806         public class HTMLLink extends AccessibleHyperlink {
1807             Element element;
1808 
1809             /**
1810              * Constructs a {@code HTMLLink}.
1811              * @param e the element
1812              */
1813             public HTMLLink(Element e) {
1814                 element = e;
1815             }
1816 
1817             /**


< prev index next >