Source Code Cross Referenced for JEditorPane.java in  » 6.0-JDK-Core » swing » javax » swing » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Home
Java Source Code / Java Documentation
1.6.0 JDK Core
2.6.0 JDK Modules
3.6.0 JDK Modules com.sun
4.6.0 JDK Modules com.sun.java
5.6.0 JDK Modules sun
6.6.0 JDK Platform
7.Ajax
8.Apache Harmony Java SE
9.Aspect oriented
10.Authentication Authorization
11.Blogger System
12.Build
13.Byte Code
14.Cache
15.Chart
16.Chat
17.Code Analyzer
18.Collaboration
19.Content Management System
20.Database Client
21.Database DBMS
22.Database JDBC Connection Pool
23.Database ORM
24.Development
25.EJB Server
26.ERP CRM Financial
27.ESB
28.Forum
29.Game
30.GIS
31.Graphic 3D
32.Graphic Library
33.Groupware
34.HTML Parser
35.IDE
36.IDE Eclipse
37.IDE Netbeans
38.Installer
39.Internationalization Localization
40.Inversion of Control
41.Issue Tracking
42.J2EE
43.J2ME
44.JBoss
45.JMS
46.JMX
47.Library
48.Mail Clients
49.Music
50.Net
51.Parser
52.PDF
53.Portal
54.Profiler
55.Project Management
56.Report
57.RSS RDF
58.Rule Engine
59.Science
60.Scripting
61.Search Engine
62.Security
63.Sevlet Container
64.Source Control
65.Swing Library
66.Template Engine
67.Test Coverage
68.Testing
69.UML
70.Web Crawler
71.Web Framework
72.Web Mail
73.Web Server
74.Web Services
75.Web Services apache cxf 2.2.6
76.Web Services AXIS2
77.Wiki Engine
78.Workflow Engines
79.XML
80.XML UI
Java Source Code / Java Documentation » 6.0 JDK Core » swing » javax.swing 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001        /*
0002         * Copyright 1997-2006 Sun Microsystems, Inc.  All Rights Reserved.
0003         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004         *
0005         * This code is free software; you can redistribute it and/or modify it
0006         * under the terms of the GNU General Public License version 2 only, as
0007         * published by the Free Software Foundation.  Sun designates this
0008         * particular file as subject to the "Classpath" exception as provided
0009         * by Sun in the LICENSE file that accompanied this code.
0010         *
0011         * This code is distributed in the hope that it will be useful, but WITHOUT
0012         * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013         * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
0014         * version 2 for more details (a copy is included in the LICENSE file that
0015         * accompanied this code).
0016         *
0017         * You should have received a copy of the GNU General Public License version
0018         * 2 along with this work; if not, write to the Free Software Foundation,
0019         * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020         *
0021         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022         * CA 95054 USA or visit www.sun.com if you need additional information or
0023         * have any questions.
0024         */
0025        package javax.swing;
0026
0027        import java.awt.*;
0028        import java.awt.event.*;
0029        import java.lang.reflect.*;
0030        import java.net.*;
0031        import java.util.*;
0032        import java.io.*;
0033        import java.util.*;
0034
0035        import javax.swing.plaf.*;
0036        import javax.swing.text.*;
0037        import javax.swing.event.*;
0038        import javax.swing.text.html.*;
0039        import javax.accessibility.*;
0040
0041        /**
0042         * A text component to edit various kinds of content.
0043         * You can find how-to information and examples of using editor panes in
0044         * <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/text.html">Using Text Components</a>,
0045         * a section in <em>The Java Tutorial.</em>
0046         *
0047         * <p>
0048         * This component uses implementations of the
0049         * <code>EditorKit</code> to accomplish its behavior. It effectively
0050         * morphs into the proper kind of text editor for the kind
0051         * of content it is given.  The content type that editor is bound
0052         * to at any given time is determined by the <code>EditorKit</code> currently
0053         * installed.  If the content is set to a new URL, its type is used
0054         * to determine the <code>EditorKit</code> that should be used to
0055         * load the content.
0056         * <p>
0057         * By default, the following types of content are known:
0058         * <dl>
0059         * <dt><b>text/plain</b>
0060         * <dd>Plain text, which is the default the type given isn't
0061         * recognized.  The kit used in this case is an extension of
0062         * <code>DefaultEditorKit</code> that produces a wrapped plain text view.
0063         * <dt><b>text/html</b>
0064         * <dd>HTML text.  The kit used in this case is the class
0065         * <code>javax.swing.text.html.HTMLEditorKit</code>
0066         * which provides HTML 3.2 support.
0067         * <dt><b>text/rtf</b>
0068         * <dd>RTF text.  The kit used in this case is the class
0069         * <code>javax.swing.text.rtf.RTFEditorKit</code>
0070         * which provides a limited support of the Rich Text Format.
0071         * </dl>
0072         * <p>
0073         * There are several ways to load content into this component.
0074         * <ol>
0075         * <li>
0076         * The {@link #setText setText} method can be used to initialize
0077         * the component from a string.  In this case the current
0078         * <code>EditorKit</code> will be used, and the content type will be
0079         * expected to be of this type.
0080         * <li>
0081         * The {@link #read read} method can be used to initialize the 
0082         * component from a <code>Reader</code>.  Note that if the content type is HTML,
0083         * relative references (e.g. for things like images) can't be resolved 
0084         * unless the &lt;base&gt; tag is used or the <em>Base</em> property
0085         * on <code>HTMLDocument</code> is set.
0086         * In this case the current <code>EditorKit</code> will be used,
0087         * and the content type will be expected to be of this type.
0088         * <li>
0089         * The {@link #setPage setPage} method can be used to initialize
0090         * the component from a URL.  In this case, the content type will be
0091         * determined from the URL, and the registered <code>EditorKit</code>
0092         * for that content type will be set.
0093         * </ol>
0094         * <p>
0095         * Some kinds of content may provide hyperlink support by generating
0096         * hyperlink events.  The HTML <code>EditorKit</code> will generate
0097         * hyperlink events if the <code>JEditorPane</code> is <em>not editable</em> 
0098         * (<code>JEditorPane.setEditable(false);</code> has been called).
0099         * If HTML frames are embedded in the document, the typical response would be
0100         * to change a portion of the current document.  The following code
0101         * fragment is a possible hyperlink listener implementation, that treats 
0102         * HTML frame events specially, and simply displays any other activated
0103         * hyperlinks.
0104         * <code><pre>
0105
0106         &nbsp;    class Hyperactive implements HyperlinkListener {
0107         &nbsp;
0108         &nbsp;        public void hyperlinkUpdate(HyperlinkEvent e) {
0109         &nbsp;	          if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
0110         &nbsp;		      JEditorPane pane = (JEditorPane) e.getSource();
0111         &nbsp;		      if (e instanceof HTMLFrameHyperlinkEvent) {
0112         &nbsp;		          HTMLFrameHyperlinkEvent  evt = (HTMLFrameHyperlinkEvent)e;
0113         &nbsp;		          HTMLDocument doc = (HTMLDocument)pane.getDocument();
0114         &nbsp;		          doc.processHTMLFrameHyperlinkEvent(evt);
0115         &nbsp;		      } else {
0116         &nbsp;		          try {
0117         &nbsp;			      pane.setPage(e.getURL());
0118         &nbsp;		          } catch (Throwable t) {
0119         &nbsp;			      t.printStackTrace();
0120         &nbsp;		          }
0121         &nbsp;		      }
0122         &nbsp;	          }
0123         &nbsp;	      }
0124         &nbsp;    }
0125
0126         * </pre></code>
0127         * <p>
0128         * For information on customizing how <b>text/html</b> is rendered please see
0129         * {@link #W3C_LENGTH_UNITS} and {@link #HONOR_DISPLAY_PROPERTIES}
0130         * <p>
0131         * Culturally dependent information in some documents is handled through
0132         * a mechanism called character encoding.  Character encoding is an
0133         * unambiguous mapping of the members of a character set (letters, ideographs,
0134         * digits, symbols, or control functions) to specific numeric code values. It
0135         * represents the way the file is stored. Example character encodings are
0136         * ISO-8859-1, ISO-8859-5, Shift-jis, Euc-jp, and UTF-8. When the file is 
0137         * passed to an user agent (<code>JEditorPane</code>) it is converted to
0138         * the document character set (ISO-10646 aka Unicode).
0139         * <p>
0140         * There are multiple ways to get a character set mapping to happen 
0141         * with <code>JEditorPane</code>.  
0142         * <ol>
0143         * <li>
0144         * One way is to specify the character set as a parameter of the MIME 
0145         * type.  This will be established by a call to the 
0146         * <a href="#setContentType">setContentType</a> method.  If the content
0147         * is loaded by the <a href="#setPage">setPage</a> method the content
0148         * type will have been set according to the specification of the URL.
0149         * It the file is loaded directly, the content type would be expected to
0150         * have been set prior to loading.
0151         * <li>
0152         * Another way the character set can be specified is in the document itself.
0153         * This requires reading the document prior to determining the character set
0154         * that is desired.  To handle this, it is expected that the 
0155         * <code>EditorKit</code>.read operation throw a
0156         * <code>ChangedCharSetException</code> which will
0157         * be caught.  The read is then restarted with a new Reader that uses
0158         * the character set specified in the <code>ChangedCharSetException</code>
0159         * (which is an <code>IOException</code>).
0160         * </ol>
0161         * <p>
0162         * <dl>
0163         * <dt><b><font size=+1>Newlines</font></b>
0164         * <dd>
0165         * For a discussion on how newlines are handled, see
0166         * <a href="text/DefaultEditorKit.html">DefaultEditorKit</a>.
0167         * </dl>
0168         *
0169         * <p>
0170         * <strong>Warning:</strong> Swing is not thread safe. For more
0171         * information see <a
0172         * href="package-summary.html#threading">Swing's Threading
0173         * Policy</a>.
0174         * <p>
0175         * <strong>Warning:</strong>
0176         * Serialized objects of this class will not be compatible with
0177         * future Swing releases. The current serialization support is
0178         * appropriate for short term storage or RMI between applications running
0179         * the same version of Swing.  As of 1.4, support for long term storage
0180         * of all JavaBeans<sup><font size="-2">TM</font></sup>
0181         * has been added to the <code>java.beans</code> package.
0182         * Please see {@link java.beans.XMLEncoder}.
0183         *
0184         * @beaninfo
0185         *   attribute: isContainer false
0186         * description: A text component to edit various types of content.
0187         *
0188         * @author  Timothy Prinzing
0189         * @version 1.145 05/05/07
0190         */
0191        public class JEditorPane extends JTextComponent {
0192
0193            /**
0194             * Creates a new <code>JEditorPane</code>.
0195             * The document model is set to <code>null</code>.
0196             */
0197            public JEditorPane() {
0198                super ();
0199                setFocusCycleRoot(true);
0200                setFocusTraversalPolicy(new LayoutFocusTraversalPolicy() {
0201                    public Component getComponentAfter(
0202                            Container focusCycleRoot, Component aComponent) {
0203                        if (focusCycleRoot != JEditorPane.this 
0204                                || (!isEditable() && getComponentCount() > 0)) {
0205                            return super .getComponentAfter(focusCycleRoot,
0206                                    aComponent);
0207                        } else {
0208                            Container rootAncestor = getFocusCycleRootAncestor();
0209                            return (rootAncestor != null) ? rootAncestor
0210                                    .getFocusTraversalPolicy()
0211                                    .getComponentAfter(rootAncestor,
0212                                            JEditorPane.this ) : null;
0213                        }
0214                    }
0215
0216                    public Component getComponentBefore(
0217                            Container focusCycleRoot, Component aComponent) {
0218                        if (focusCycleRoot != JEditorPane.this 
0219                                || (!isEditable() && getComponentCount() > 0)) {
0220                            return super .getComponentBefore(focusCycleRoot,
0221                                    aComponent);
0222                        } else {
0223                            Container rootAncestor = getFocusCycleRootAncestor();
0224                            return (rootAncestor != null) ? rootAncestor
0225                                    .getFocusTraversalPolicy()
0226                                    .getComponentBefore(rootAncestor,
0227                                            JEditorPane.this ) : null;
0228                        }
0229                    }
0230
0231                    public Component getDefaultComponent(
0232                            Container focusCycleRoot) {
0233                        return (focusCycleRoot != JEditorPane.this  || (!isEditable() && getComponentCount() > 0)) ? super 
0234                                .getDefaultComponent(focusCycleRoot)
0235                                : null;
0236                    }
0237
0238                    protected boolean accept(Component aComponent) {
0239                        return (aComponent != JEditorPane.this ) ? super 
0240                                .accept(aComponent) : false;
0241                    }
0242                });
0243                LookAndFeel.installProperty(this , "focusTraversalKeysForward",
0244                        JComponent.getManagingFocusForwardTraversalKeys());
0245                LookAndFeel.installProperty(this , "focusTraversalKeysBackward",
0246                        JComponent.getManagingFocusBackwardTraversalKeys());
0247            }
0248
0249            /**
0250             * Creates a <code>JEditorPane</code> based on a specified URL for input.
0251             *
0252             * @param initialPage the URL
0253             * @exception IOException if the URL is <code>null</code>
0254             *		or cannot be accessed
0255             */
0256            public JEditorPane(URL initialPage) throws IOException {
0257                this ();
0258                setPage(initialPage);
0259            }
0260
0261            /**
0262             * Creates a <code>JEditorPane</code> based on a string containing
0263             * a URL specification.
0264             *
0265             * @param url the URL
0266             * @exception IOException if the URL is <code>null</code> or
0267             *		cannot be accessed
0268             */
0269            public JEditorPane(String url) throws IOException {
0270                this ();
0271                setPage(url);
0272            }
0273
0274            /**
0275             * Creates a <code>JEditorPane</code> that has been initialized
0276             * to the given text.  This is a convenience constructor that calls the
0277             * <code>setContentType</code> and <code>setText</code> methods.
0278             *
0279             * @param type mime type of the given text
0280             * @param text the text to initialize with; may be <code>null</code>
0281             * @exception NullPointerException if the <code>type</code> parameter
0282             *		is <code>null</code>
0283             */
0284            public JEditorPane(String type, String text) {
0285                this ();
0286                setContentType(type);
0287                setText(text);
0288            }
0289
0290            /**
0291             * Adds a hyperlink listener for notification of any changes, for example
0292             * when a link is selected and entered.
0293             *
0294             * @param listener the listener
0295             */
0296            public synchronized void addHyperlinkListener(
0297                    HyperlinkListener listener) {
0298                listenerList.add(HyperlinkListener.class, listener);
0299            }
0300
0301            /**
0302             * Removes a hyperlink listener.
0303             *
0304             * @param listener the listener
0305             */
0306            public synchronized void removeHyperlinkListener(
0307                    HyperlinkListener listener) {
0308                listenerList.remove(HyperlinkListener.class, listener);
0309            }
0310
0311            /**
0312             * Returns an array of all the <code>HyperLinkListener</code>s added
0313             * to this JEditorPane with addHyperlinkListener().
0314             *
0315             * @return all of the <code>HyperLinkListener</code>s added or an empty
0316             *         array if no listeners have been added
0317             * @since 1.4
0318             */
0319            public synchronized HyperlinkListener[] getHyperlinkListeners() {
0320                return (HyperlinkListener[]) listenerList
0321                        .getListeners(HyperlinkListener.class);
0322            }
0323
0324            /**
0325             * Notifies all listeners that have registered interest for
0326             * notification on this event type.  This is normally called
0327             * by the currently installed <code>EditorKit</code> if a content type
0328             * that supports hyperlinks is currently active and there
0329             * was activity with a link.  The listener list is processed
0330             * last to first.
0331             *
0332             * @param e the event
0333             * @see EventListenerList
0334             */
0335            public void fireHyperlinkUpdate(HyperlinkEvent e) {
0336                // Guaranteed to return a non-null array
0337                Object[] listeners = listenerList.getListenerList();
0338                // Process the listeners last to first, notifying
0339                // those that are interested in this event
0340                for (int i = listeners.length - 2; i >= 0; i -= 2) {
0341                    if (listeners[i] == HyperlinkListener.class) {
0342                        ((HyperlinkListener) listeners[i + 1])
0343                                .hyperlinkUpdate(e);
0344                    }
0345                }
0346            }
0347
0348            /**
0349             * Sets the current URL being displayed.  The content type of the
0350             * pane is set, and if the editor kit for the pane is
0351             * non-<code>null</code>, then
0352             * a new default document is created and the URL is read into it.
0353             * If the URL contains and reference location, the location will
0354             * be scrolled to by calling the <code>scrollToReference</code> 
0355             * method. If the desired URL is the one currently being displayed, 
0356             * the document will not be reloaded. To force a document
0357             * reload it is necessary to clear the stream description property 
0358             * of the document. The following code shows how this can be done:
0359             *
0360             * <pre>
0361             *   Document doc = jEditorPane.getDocument();
0362             *   doc.putProperty(Document.StreamDescriptionProperty, null);
0363             * </pre>
0364             * 
0365             * If the desired URL is not the one currently being
0366             * displayed, the <code>getStream</code> method is called to
0367             * give subclasses control over the stream provided.
0368             * <p>
0369             * This may load either synchronously or asynchronously
0370             * depending upon the document returned by the <code>EditorKit</code>.
0371             * If the <code>Document</code> is of type
0372             * <code>AbstractDocument</code> and has a value returned by 
0373             * <code>AbstractDocument.getAsynchronousLoadPriority</code>
0374             * that is greater than or equal to zero, the page will be
0375             * loaded on a separate thread using that priority.
0376             * <p>
0377             * If the document is loaded synchronously, it will be
0378             * filled in with the stream prior to being installed into
0379             * the editor with a call to <code>setDocument</code>, which
0380             * is bound and will fire a property change event.  If an
0381             * <code>IOException</code> is thrown the partially loaded
0382             * document will
0383             * be discarded and neither the document or page property
0384             * change events will be fired.  If the document is 
0385             * successfully loaded and installed, a view will be
0386             * built for it by the UI which will then be scrolled if 
0387             * necessary, and then the page property change event
0388             * will be fired.
0389             * <p>
0390             * If the document is loaded asynchronously, the document
0391             * will be installed into the editor immediately using a
0392             * call to <code>setDocument</code> which will fire a 
0393             * document property change event, then a thread will be
0394             * created which will begin doing the actual loading.  
0395             * In this case, the page property change event will not be 
0396             * fired by the call to this method directly, but rather will be 
0397             * fired when the thread doing the loading has finished.
0398             * It will also be fired on the event-dispatch thread.
0399             * Since the calling thread can not throw an <code>IOException</code>
0400             * in the event of failure on the other thread, the page 
0401             * property change event will be fired when the other 
0402             * thread is done whether the load was successful or not.
0403             * 
0404             * @param page the URL of the page
0405             * @exception IOException for a <code>null</code> or invalid
0406             *		page specification, or exception from the stream being read
0407             * @see #getPage
0408             * @beaninfo
0409             *  description: the URL used to set content
0410             *        bound: true
0411             *       expert: true
0412             */
0413            public void setPage(URL page) throws IOException {
0414                if (page == null) {
0415                    throw new IOException("invalid url");
0416                }
0417                URL loaded = getPage();
0418
0419                // reset scrollbar
0420                if (!page.equals(loaded) && page.getRef() == null) {
0421                    scrollRectToVisible(new Rectangle(0, 0, 1, 1));
0422                }
0423                boolean reloaded = false;
0424                Object postData = getPostData();
0425                if ((loaded == null) || !loaded.sameFile(page)
0426                        || (postData != null)) {
0427                    // different url or POST method, load the new content
0428
0429                    int p = getAsynchronousLoadPriority(getDocument());
0430                    if ((postData == null) || (p < 0)) {
0431                        // Either we do not have POST data, or should submit the data
0432                        // synchronously.
0433                        InputStream in = getStream(page);
0434                        if (kit != null) {
0435                            Document doc = initializeModel(kit, page);
0436
0437                            // At this point, one could either load up the model with no
0438                            // view notifications slowing it down (i.e. best synchronous
0439                            // behavior) or set the model and start to feed it on a separate
0440                            // thread (best asynchronous behavior).
0441                            synchronized (this ) {
0442                                if (loading != null) {
0443                                    // we are loading asynchronously, so we need to cancel
0444                                    // the old stream.
0445                                    loading.cancel();
0446                                    loading = null;
0447                                }
0448                            }
0449                            p = getAsynchronousLoadPriority(doc);
0450                            if (p >= 0) {
0451                                // load asynchronously
0452                                setDocument(doc);
0453                                synchronized (this ) {
0454                                    loading = new PageStream(in);
0455                                    Thread pl = new PageLoader(doc, loading, p,
0456                                            loaded, page);
0457                                    pl.start();
0458                                }
0459                                return;
0460                            }
0461                            read(in, doc);
0462                            setDocument(doc);
0463                            reloaded = true;
0464                        }
0465                    } else {
0466                        // We have POST data and should send it asynchronously.
0467                        // Send (and subsequentally read) data in separate thread.
0468                        // Model initialization is deferred to that thread, too.
0469                        Thread pl = new PageLoader(null, null, p, loaded, page);
0470                        pl.start();
0471                        return;
0472                    }
0473                }
0474                final String reference = page.getRef();
0475                if (reference != null) {
0476                    if (!reloaded) {
0477                        scrollToReference(reference);
0478                    } else {
0479                        // Have to scroll after painted.
0480                        SwingUtilities.invokeLater(new Runnable() {
0481                            public void run() {
0482                                scrollToReference(reference);
0483                            }
0484                        });
0485                    }
0486                    getDocument().putProperty(
0487                            Document.StreamDescriptionProperty, page);
0488                }
0489                firePropertyChange("page", loaded, page);
0490            }
0491
0492            /**
0493             * Create model and initialize document properties from page properties.
0494             */
0495            private Document initializeModel(EditorKit kit, URL page) {
0496                Document doc = kit.createDefaultDocument();
0497                if (pageProperties != null) {
0498                    // transfer properties discovered in stream to the
0499                    // document property collection.
0500                    for (Enumeration e = pageProperties.keys(); e
0501                            .hasMoreElements();) {
0502                        Object key = e.nextElement();
0503                        doc.putProperty(key, pageProperties.get(key));
0504                    }
0505                    pageProperties.clear();
0506                }
0507                if (doc.getProperty(Document.StreamDescriptionProperty) == null) {
0508                    doc.putProperty(Document.StreamDescriptionProperty, page);
0509                }
0510                return doc;
0511            }
0512
0513            /**
0514             * Return load priority for the document or -1 if priority not supported.
0515             */
0516            private int getAsynchronousLoadPriority(Document doc) {
0517                return (doc instanceof  AbstractDocument ? ((AbstractDocument) doc)
0518                        .getAsynchronousLoadPriority()
0519                        : -1);
0520            }
0521
0522            /**
0523             * This method initializes from a stream.  If the kit is
0524             * set to be of type <code>HTMLEditorKit</code>, and the
0525             * <code>desc</code> parameter is an <code>HTMLDocument</code>,
0526             * then it invokes the <code>HTMLEditorKit</code> to initiate
0527             * the read. Otherwise it calls the superclass
0528             * method which loads the model as plain text.
0529             *
0530             * @param in the stream from which to read
0531             * @param desc an object describing the stream
0532             * @exception IOException as thrown by the stream being
0533             * 		used to initialize
0534             * @see JTextComponent#read
0535             * @see #setDocument
0536             */
0537            public void read(InputStream in, Object desc) throws IOException {
0538
0539                if (desc instanceof  HTMLDocument
0540                        && kit instanceof  HTMLEditorKit) {
0541                    HTMLDocument hdoc = (HTMLDocument) desc;
0542                    setDocument(hdoc);
0543                    read(in, hdoc);
0544                } else {
0545                    String charset = (String) getClientProperty("charset");
0546                    Reader r = (charset != null) ? new InputStreamReader(in,
0547                            charset) : new InputStreamReader(in);
0548                    super .read(r, desc);
0549                }
0550            }
0551
0552            /**
0553             * This method invokes the <code>EditorKit</code> to initiate a
0554             * read.  In the case where a <code>ChangedCharSetException</code>
0555             * is thrown this exception will contain the new CharSet.
0556             * Therefore the <code>read</code> operation
0557             * is then restarted after building a new Reader with the new charset.
0558             *
0559             * @param in the inputstream to use
0560             * @param doc the document to load
0561             *
0562             */
0563            void read(InputStream in, Document doc) throws IOException {
0564                if (!Boolean.TRUE.equals(doc
0565                        .getProperty("IgnoreCharsetDirective"))) {
0566                    final int READ_LIMIT = 1024 * 10;
0567                    in = new BufferedInputStream(in, READ_LIMIT);
0568                    in.mark(READ_LIMIT);
0569                }
0570                try {
0571                    String charset = (String) getClientProperty("charset");
0572                    Reader r = (charset != null) ? new InputStreamReader(in,
0573                            charset) : new InputStreamReader(in);
0574                    kit.read(r, doc, 0);
0575                } catch (BadLocationException e) {
0576                    throw new IOException(e.getMessage());
0577                } catch (ChangedCharSetException changedCharSetException) {
0578                    String charSetSpec = changedCharSetException
0579                            .getCharSetSpec();
0580                    if (changedCharSetException.keyEqualsCharSet()) {
0581                        putClientProperty("charset", charSetSpec);
0582                    } else {
0583                        setCharsetFromContentTypeParameters(charSetSpec);
0584                    }
0585                    try {
0586                        in.reset();
0587                    } catch (IOException exception) {
0588                        //mark was invalidated
0589                        in.close();
0590                        URL url = (URL) doc
0591                                .getProperty(Document.StreamDescriptionProperty);
0592                        if (url != null) {
0593                            URLConnection conn = url.openConnection();
0594                            in = conn.getInputStream();
0595                        } else {
0596                            //there is nothing we can do to recover stream
0597                            throw changedCharSetException;
0598                        }
0599                    }
0600                    try {
0601                        doc.remove(0, doc.getLength());
0602                    } catch (BadLocationException e) {
0603                    }
0604                    doc.putProperty("IgnoreCharsetDirective", Boolean
0605                            .valueOf(true));
0606                    read(in, doc);
0607                }
0608            }
0609
0610            /**
0611             * Thread to load a stream into the text document model.
0612             */
0613            class PageLoader extends Thread {
0614
0615                /**
0616                 * Construct an asynchronous page loader.
0617                 */
0618                PageLoader(Document doc, InputStream in, int priority, URL old,
0619                        URL page) {
0620                    setPriority(priority);
0621                    this .in = in;
0622                    this .old = old;
0623                    this .page = page;
0624                    this .doc = doc;
0625                }
0626
0627                boolean pageLoaded = false;
0628
0629                /**
0630                 * Try to load the document, then scroll the view
0631                 * to the reference (if specified).  When done, fire
0632                 * a page property change event.
0633                 */
0634                public void run() {
0635                    try {
0636                        if (in == null) {
0637                            in = getStream(page);
0638                            if (kit == null) {
0639                                // We received document of unknown content type.
0640                                UIManager.getLookAndFeel()
0641                                        .provideErrorFeedback(JEditorPane.this );
0642                                return;
0643                            }
0644                            // Access to <code>loading</code> should be synchronized.
0645                            synchronized (JEditorPane.this ) {
0646                                in = loading = new PageStream(in);
0647                            }
0648                        }
0649                        if (doc == null) {
0650                            try {
0651                                SwingUtilities.invokeAndWait(new Runnable() {
0652                                    public void run() {
0653                                        doc = initializeModel(kit, page);
0654                                        setDocument(doc);
0655                                    }
0656                                });
0657                            } catch (InvocationTargetException ex) {
0658                                UIManager.getLookAndFeel()
0659                                        .provideErrorFeedback(JEditorPane.this );
0660                                return;
0661                            } catch (InterruptedException ex) {
0662                                UIManager.getLookAndFeel()
0663                                        .provideErrorFeedback(JEditorPane.this );
0664                                return;
0665                            }
0666                        }
0667
0668                        read(in, doc);
0669                        URL page = (URL) doc
0670                                .getProperty(Document.StreamDescriptionProperty);
0671                        String reference = page.getRef();
0672                        if (reference != null) {
0673                            // scroll the page if necessary, but do it on the
0674                            // event thread... that is the only guarantee that 
0675                            // modelToView can be safely called.
0676                            Runnable callScrollToReference = new Runnable() {
0677                                public void run() {
0678                                    URL u = (URL) getDocument().getProperty(
0679                                            Document.StreamDescriptionProperty);
0680                                    String ref = u.getRef();
0681                                    scrollToReference(ref);
0682                                }
0683                            };
0684                            SwingUtilities.invokeLater(callScrollToReference);
0685                        }
0686                        pageLoaded = true;
0687                    } catch (IOException ioe) {
0688                        UIManager.getLookAndFeel().provideErrorFeedback(
0689                                JEditorPane.this );
0690                    } finally {
0691                        synchronized (JEditorPane.this ) {
0692                            loading = null;
0693                        }
0694                        SwingUtilities.invokeLater(new Runnable() {
0695                            public void run() {
0696                                if (pageLoaded) {
0697                                    firePropertyChange("page", old, page);
0698                                }
0699                            }
0700                        });
0701                    }
0702                }
0703
0704                /**
0705                 * The stream to load the document with
0706                 */
0707                InputStream in;
0708
0709                /**
0710                 * URL of the old page that was replaced (for the property change event)
0711                 */
0712                URL old;
0713
0714                /**
0715                 * URL of the page being loaded (for the property change event)
0716                 */
0717                URL page;
0718
0719                /**
0720                 * The Document instance to load into. This is cached in case a
0721                 * new Document is created between the time the thread this is created
0722                 * and run.
0723                 */
0724                Document doc;
0725            }
0726
0727            static class PageStream extends FilterInputStream {
0728
0729                boolean canceled;
0730
0731                public PageStream(InputStream i) {
0732                    super (i);
0733                    canceled = false;
0734                }
0735
0736                /**
0737                 * Cancel the loading of the stream by throwing
0738                 * an IOException on the next request.
0739                 */
0740                public synchronized void cancel() {
0741                    canceled = true;
0742                }
0743
0744                protected synchronized void checkCanceled() throws IOException {
0745                    if (canceled) {
0746                        throw new IOException("page canceled");
0747                    }
0748                }
0749
0750                public int read() throws IOException {
0751                    checkCanceled();
0752                    return super .read();
0753                }
0754
0755                public long skip(long n) throws IOException {
0756                    checkCanceled();
0757                    return super .skip(n);
0758                }
0759
0760                public int available() throws IOException {
0761                    checkCanceled();
0762                    return super .available();
0763                }
0764
0765                public void reset() throws IOException {
0766                    checkCanceled();
0767                    super .reset();
0768                }
0769
0770            }
0771
0772            /**
0773             * Fetches a stream for the given URL, which is about to 
0774             * be loaded by the <code>setPage</code> method.  By
0775             * default, this simply opens the URL and returns the
0776             * stream.  This can be reimplemented to do useful things
0777             * like fetch the stream from a cache, monitor the progress
0778             * of the stream, etc.
0779             * <p>
0780             * This method is expected to have the the side effect of
0781             * establishing the content type, and therefore setting the
0782             * appropriate <code>EditorKit</code> to use for loading the stream.
0783             * <p>
0784             * If this the stream was an http connection, redirects
0785             * will be followed and the resulting URL will be set as
0786             * the <code>Document.StreamDescriptionProperty</code> so that relative
0787             * URL's can be properly resolved.
0788             *
0789             * @param page  the URL of the page
0790             */
0791            protected InputStream getStream(URL page) throws IOException {
0792                final URLConnection conn = page.openConnection();
0793                if (conn instanceof  HttpURLConnection) {
0794                    HttpURLConnection hconn = (HttpURLConnection) conn;
0795                    hconn.setInstanceFollowRedirects(false);
0796                    Object postData = getPostData();
0797                    if (postData != null) {
0798                        handlePostData(hconn, postData);
0799                    }
0800                    int response = hconn.getResponseCode();
0801                    boolean redirect = (response >= 300 && response <= 399);
0802
0803                    /*
0804                     * In the case of a redirect, we want to actually change the URL
0805                     * that was input to the new, redirected URL
0806                     */
0807                    if (redirect) {
0808                        String loc = conn.getHeaderField("Location");
0809                        if (loc.startsWith("http", 0)) {
0810                            page = new URL(loc);
0811                        } else {
0812                            page = new URL(page, loc);
0813                        }
0814                        return getStream(page);
0815                    }
0816                }
0817
0818                // Connection properties handler should be forced to run on EDT,
0819                // as it instantiates the EditorKit.
0820                if (SwingUtilities.isEventDispatchThread()) {
0821                    handleConnectionProperties(conn);
0822                } else {
0823                    try {
0824                        SwingUtilities.invokeAndWait(new Runnable() {
0825                            public void run() {
0826                                handleConnectionProperties(conn);
0827                            }
0828                        });
0829                    } catch (InterruptedException e) {
0830                        throw new RuntimeException(e);
0831                    } catch (InvocationTargetException e) {
0832                        throw new RuntimeException(e);
0833                    }
0834                }
0835                return conn.getInputStream();
0836            }
0837
0838            /**
0839             * Handle URL connection properties (most notably, content type).
0840             */
0841            private void handleConnectionProperties(URLConnection conn) {
0842                if (pageProperties == null) {
0843                    pageProperties = new Hashtable();
0844                }
0845                String type = conn.getContentType();
0846                if (type != null) {
0847                    setContentType(type);
0848                    pageProperties.put("content-type", type);
0849                }
0850                pageProperties.put(Document.StreamDescriptionProperty, conn
0851                        .getURL());
0852                String enc = conn.getContentEncoding();
0853                if (enc != null) {
0854                    pageProperties.put("content-encoding", enc);
0855                }
0856            }
0857
0858            private Object getPostData() {
0859                return getDocument().getProperty(PostDataProperty);
0860            }
0861
0862            private void handlePostData(HttpURLConnection conn, Object postData)
0863                    throws IOException {
0864                conn.setDoOutput(true);
0865                DataOutputStream os = null;
0866                try {
0867                    conn.setRequestProperty("Content-Type",
0868                            "application/x-www-form-urlencoded");
0869                    os = new DataOutputStream(conn.getOutputStream());
0870                    os.writeBytes((String) postData);
0871                } finally {
0872                    if (os != null) {
0873                        os.close();
0874                    }
0875                }
0876            }
0877
0878            /**
0879             * Scrolls the view to the given reference location
0880             * (that is, the value returned by the <code>UL.getRef</code>
0881             * method for the URL being displayed).  By default, this
0882             * method only knows how to locate a reference in an
0883             * HTMLDocument.  The implementation calls the
0884             * <code>scrollRectToVisible</code> method to
0885             * accomplish the actual scrolling.  If scrolling to a
0886             * reference location is needed for document types other
0887             * than HTML, this method should be reimplemented.
0888             * This method will have no effect if the component
0889             * is not visible.
0890             * 
0891             * @param reference the named location to scroll to
0892             */
0893            public void scrollToReference(String reference) {
0894                Document d = getDocument();
0895                if (d instanceof  HTMLDocument) {
0896                    HTMLDocument doc = (HTMLDocument) d;
0897                    HTMLDocument.Iterator iter = doc.getIterator(HTML.Tag.A);
0898                    for (; iter.isValid(); iter.next()) {
0899                        AttributeSet a = iter.getAttributes();
0900                        String nm = (String) a
0901                                .getAttribute(HTML.Attribute.NAME);
0902                        if ((nm != null) && nm.equals(reference)) {
0903                            // found a matching reference in the document.
0904                            try {
0905                                Rectangle r = modelToView(iter.getStartOffset());
0906                                if (r != null) {
0907                                    // the view is visible, scroll it to the 
0908                                    // center of the current visible area.
0909                                    Rectangle vis = getVisibleRect();
0910                                    //r.y -= (vis.height / 2);
0911                                    r.height = vis.height;
0912                                    scrollRectToVisible(r);
0913                                }
0914                            } catch (BadLocationException ble) {
0915                                UIManager.getLookAndFeel()
0916                                        .provideErrorFeedback(JEditorPane.this );
0917                            }
0918                        }
0919                    }
0920                }
0921            }
0922
0923            /**
0924             * Gets the current URL being displayed.  If a URL was 
0925             * not specified in the creation of the document, this
0926             * will return <code>null</code>, and relative URL's will not be 
0927             * resolved.
0928             *
0929             * @return the URL, or <code>null</code> if none
0930             */
0931            public URL getPage() {
0932                return (URL) getDocument().getProperty(
0933                        Document.StreamDescriptionProperty);
0934            }
0935
0936            /**
0937             * Sets the current URL being displayed.
0938             *
0939             * @param url the URL for display
0940             * @exception IOException for a <code>null</code> or invalid URL
0941             *		specification
0942             */
0943            public void setPage(String url) throws IOException {
0944                if (url == null) {
0945                    throw new IOException("invalid url");
0946                }
0947                URL page = new URL(url);
0948                setPage(page);
0949            }
0950
0951            /**
0952             * Gets the class ID for the UI.
0953             *
0954             * @return the string "EditorPaneUI"
0955             * @see JComponent#getUIClassID
0956             * @see UIDefaults#getUI
0957             */
0958            public String getUIClassID() {
0959                return uiClassID;
0960            }
0961
0962            /**
0963             * Creates the default editor kit (<code>PlainEditorKit</code>) for when
0964             * the component is first created.
0965             *
0966             * @return the editor kit
0967             */
0968            protected EditorKit createDefaultEditorKit() {
0969                return new PlainEditorKit();
0970            }
0971
0972            /**
0973             * Fetches the currently installed kit for handling content.
0974             * <code>createDefaultEditorKit</code> is called to set up a default
0975             * if necessary.
0976             *
0977             * @return the editor kit
0978             */
0979            public EditorKit getEditorKit() {
0980                if (kit == null) {
0981                    kit = createDefaultEditorKit();
0982                    isUserSetEditorKit = false;
0983                }
0984                return kit;
0985            }
0986
0987            /**
0988             * Gets the type of content that this editor 
0989             * is currently set to deal with.  This is 
0990             * defined to be the type associated with the
0991             * currently installed <code>EditorKit</code>.
0992             *
0993             * @return the content type, <code>null</code> if no editor kit set
0994             */
0995            public final String getContentType() {
0996                return (kit != null) ? kit.getContentType() : null;
0997            }
0998
0999            /**
1000             * Sets the type of content that this editor
1001             * handles.  This calls <code>getEditorKitForContentType</code>,
1002             * and then <code>setEditorKit</code> if an editor kit can
1003             * be successfully located.  This is mostly convenience method
1004             * that can be used as an alternative to calling 
1005             * <code>setEditorKit</code> directly.
1006             * <p>
1007             * If there is a charset definition specified as a parameter
1008             * of the content type specification, it will be used when
1009             * loading input streams using the associated <code>EditorKit</code>.
1010             * For example if the type is specified as 
1011             * <code>text/html; charset=EUC-JP</code> the content
1012             * will be loaded using the <code>EditorKit</code> registered for
1013             * <code>text/html</code> and the Reader provided to
1014             * the <code>EditorKit</code> to load unicode into the document will
1015             * use the <code>EUC-JP</code> charset for translating
1016             * to unicode.  If the type is not recognized, the content
1017             * will be loaded using the <code>EditorKit</code> registered
1018             * for plain text, <code>text/plain</code>.
1019             * 
1020             * @param type the non-<code>null</code> mime type for the content editing
1021             *   support
1022             * @see #getContentType
1023             * @beaninfo
1024             *  description: the type of content
1025             * @throws NullPointerException if the <code>type</code> parameter
1026             *		is <code>null</code>
1027             */
1028            public final void setContentType(String type) {
1029                // The type could have optional info is part of it,
1030                // for example some charset info.  We need to strip that
1031                // of and save it.
1032                int parm = type.indexOf(";");
1033                if (parm > -1) {
1034                    // Save the paramList.
1035                    String paramList = type.substring(parm);
1036                    // update the content type string.
1037                    type = type.substring(0, parm).trim();
1038                    if (type.toLowerCase().startsWith("text/")) {
1039                        setCharsetFromContentTypeParameters(paramList);
1040                    }
1041                }
1042                if ((kit == null) || (!type.equals(kit.getContentType()))
1043                        || !isUserSetEditorKit) {
1044                    EditorKit k = getEditorKitForContentType(type);
1045                    if (k != null && k != kit) {
1046                        setEditorKit(k);
1047                        isUserSetEditorKit = false;
1048                    }
1049                }
1050
1051            }
1052
1053            /**
1054             * This method gets the charset information specified as part
1055             * of the content type in the http header information.
1056             */
1057            private void setCharsetFromContentTypeParameters(String paramlist) {
1058                String charset = null;
1059                try {
1060                    // paramlist is handed to us with a leading ';', strip it.
1061                    int semi = paramlist.indexOf(';');
1062                    if (semi > -1 && semi < paramlist.length() - 1) {
1063                        paramlist = paramlist.substring(semi + 1);
1064                    }
1065
1066                    if (paramlist.length() > 0) {
1067                        // parse the paramlist into attr-value pairs & get the
1068                        // charset pair's value
1069                        HeaderParser hdrParser = new HeaderParser(paramlist);
1070                        charset = hdrParser.findValue("charset");
1071                        if (charset != null) {
1072                            putClientProperty("charset", charset);
1073                        }
1074                    }
1075                } catch (IndexOutOfBoundsException e) {
1076                    // malformed parameter list, use charset we have
1077                } catch (NullPointerException e) {
1078                    // malformed parameter list, use charset we have
1079                } catch (Exception e) {
1080                    // malformed parameter list, use charset we have; but complain
1081                    System.err
1082                            .println("JEditorPane.getCharsetFromContentTypeParameters failed on: "
1083                                    + paramlist);
1084                    e.printStackTrace();
1085                }
1086            }
1087
1088            /**
1089             * Sets the currently installed kit for handling
1090             * content.  This is the bound property that
1091             * establishes the content type of the editor.
1092             * Any old kit is first deinstalled, then if kit is
1093             * non-<code>null</code>,
1094             * the new kit is installed, and a default document created for it.
1095             * A <code>PropertyChange</code> event ("editorKit") is always fired when
1096             * <code>setEditorKit</code> is called.
1097             * <p>
1098             * <em>NOTE: This has the side effect of changing the model,
1099             * because the <code>EditorKit</code> is the source of how a
1100             * particular type
1101             * of content is modeled.  This method will cause <code>setDocument</code>
1102             * to be called on behalf of the caller to ensure integrity
1103             * of the internal state.</em>
1104             * 
1105             * @param kit the desired editor behavior
1106             * @see #getEditorKit
1107             * @beaninfo
1108             *  description: the currently installed kit for handling content
1109             *        bound: true
1110             *       expert: true
1111             */
1112            public void setEditorKit(EditorKit kit) {
1113                EditorKit old = this .kit;
1114                isUserSetEditorKit = true;
1115                if (old != null) {
1116                    old.deinstall(this );
1117                }
1118                this .kit = kit;
1119                if (this .kit != null) {
1120                    this .kit.install(this );
1121                    setDocument(this .kit.createDefaultDocument());
1122                }
1123                firePropertyChange("editorKit", old, kit);
1124            }
1125
1126            /**
1127             * Fetches the editor kit to use for the given type
1128             * of content.  This is called when a type is requested
1129             * that doesn't match the currently installed type.
1130             * If the component doesn't have an <code>EditorKit</code> registered
1131             * for the given type, it will try to create an 
1132             * <code>EditorKit</code> from the default <code>EditorKit</code> registry.
1133             * If that fails, a <code>PlainEditorKit</code> is used on the
1134             * assumption that all text documents can be represented
1135             * as plain text.
1136             * <p>
1137             * This method can be reimplemented to use some
1138             * other kind of type registry.  This can
1139             * be reimplemented to use the Java Activation
1140             * Framework, for example.
1141             *
1142             * @param type the non-<code>null</code> content type
1143             * @return the editor kit
1144             */
1145            public EditorKit getEditorKitForContentType(String type) {
1146                if (typeHandlers == null) {
1147                    typeHandlers = new Hashtable(3);
1148                }
1149                EditorKit k = (EditorKit) typeHandlers.get(type);
1150                if (k == null) {
1151                    k = createEditorKitForContentType(type);
1152                    if (k != null) {
1153                        setEditorKitForContentType(type, k);
1154                    }
1155                }
1156                if (k == null) {
1157                    k = createDefaultEditorKit();
1158                }
1159                return k;
1160            }
1161
1162            /**
1163             * Directly sets the editor kit to use for the given type.  A 
1164             * look-and-feel implementation might use this in conjunction
1165             * with <code>createEditorKitForContentType</code> to install handlers for
1166             * content types with a look-and-feel bias.
1167             *
1168             * @param type the non-<code>null</code> content type
1169             * @param k the editor kit to be set
1170             */
1171            public void setEditorKitForContentType(String type, EditorKit k) {
1172                if (typeHandlers == null) {
1173                    typeHandlers = new Hashtable(3);
1174                }
1175                typeHandlers.put(type, k);
1176            }
1177
1178            /**
1179             * Replaces the currently selected content with new content
1180             * represented by the given string.  If there is no selection
1181             * this amounts to an insert of the given text.  If there
1182             * is no replacement text (i.e. the content string is empty
1183             * or <code>null</code>) this amounts to a removal of the
1184             * current selection.  The replacement text will have the
1185             * attributes currently defined for input.  If the component is not
1186             * editable, beep and return.  
1187             * <p>
1188             * This method is thread safe, although most Swing methods
1189             * are not. Please see 
1190             * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How
1191             * to Use Threads</A> for more information.     
1192             *
1193             * @param content  the content to replace the selection with.  This
1194             *   value can be <code>null</code>
1195             */
1196            public void replaceSelection(String content) {
1197                if (!isEditable()) {
1198                    UIManager.getLookAndFeel().provideErrorFeedback(
1199                            JEditorPane.this );
1200                    return;
1201                }
1202                EditorKit kit = getEditorKit();
1203                if (kit instanceof  StyledEditorKit) {
1204                    try {
1205                        Document doc = getDocument();
1206                        Caret caret = getCaret();
1207                        int p0 = Math.min(caret.getDot(), caret.getMark());
1208                        int p1 = Math.max(caret.getDot(), caret.getMark());
1209                        if (doc instanceof  AbstractDocument) {
1210                            ((AbstractDocument) doc).replace(p0, p1 - p0,
1211                                    content, ((StyledEditorKit) kit)
1212                                            .getInputAttributes());
1213                        } else {
1214                            if (p0 != p1) {
1215                                doc.remove(p0, p1 - p0);
1216                            }
1217                            if (content != null && content.length() > 0) {
1218                                doc.insertString(p0, content,
1219                                        ((StyledEditorKit) kit)
1220                                                .getInputAttributes());
1221                            }
1222                        }
1223                    } catch (BadLocationException e) {
1224                        UIManager.getLookAndFeel().provideErrorFeedback(
1225                                JEditorPane.this );
1226                    }
1227                } else {
1228                    super .replaceSelection(content);
1229                }
1230            }
1231
1232            /**
1233             * Creates a handler for the given type from the default registry
1234             * of editor kits.  The registry is created if necessary.  If the
1235             * registered class has not yet been loaded, an attempt
1236             * is made to dynamically load the prototype of the kit for the
1237             * given type.  If the type was registered with a <code>ClassLoader</code>,
1238             * that <code>ClassLoader</code> will be used to load the prototype.
1239             * If there was no registered <code>ClassLoader</code>,
1240             * <code>Class.forName</code> will be used to load the prototype.
1241             * <p>
1242             * Once a prototype <code>EditorKit</code> instance is successfully
1243             * located, it is cloned and the clone is returned.  
1244             *
1245             * @param type the content type
1246             * @return the editor kit, or <code>null</code> if there is nothing
1247             *   registered for the given type
1248             */
1249            public static EditorKit createEditorKitForContentType(String type) {
1250                EditorKit k = null;
1251                Hashtable kitRegistry = getKitRegisty();
1252                k = (EditorKit) kitRegistry.get(type);
1253                if (k == null) {
1254                    // try to dynamically load the support 
1255                    String classname = (String) getKitTypeRegistry().get(type);
1256                    ClassLoader loader = (ClassLoader) getKitLoaderRegistry()
1257                            .get(type);
1258                    try {
1259                        Class c;
1260                        if (loader != null) {
1261                            c = loader.loadClass(classname);
1262                        } else {
1263                            // Will only happen if developer has invoked 
1264                            // registerEditorKitForContentType(type, class, null).
1265                            c = Class.forName(classname, true, Thread
1266                                    .currentThread().getContextClassLoader());
1267                        }
1268                        k = (EditorKit) c.newInstance();
1269                        kitRegistry.put(type, k);
1270                    } catch (Throwable e) {
1271                        k = null;
1272                    }
1273                }
1274
1275                // create a copy of the prototype or null if there
1276                // is no prototype.
1277                if (k != null) {
1278                    return (EditorKit) k.clone();
1279                }
1280                return null;
1281            }
1282
1283            /**
1284             * Establishes the default bindings of <code>type</code> to
1285             * <code>classname</code>.  
1286             * The class will be dynamically loaded later when actually
1287             * needed, and can be safely changed before attempted uses
1288             * to avoid loading unwanted classes.  The prototype 
1289             * <code>EditorKit</code> will be loaded with <code>Class.forName</code>
1290             * when registered with this method.
1291             *
1292             * @param type the non-<code>null</code> content type
1293             * @param classname the class to load later
1294             */
1295            public static void registerEditorKitForContentType(String type,
1296                    String classname) {
1297                registerEditorKitForContentType(type, classname, Thread
1298                        .currentThread().getContextClassLoader());
1299            }
1300
1301            /**
1302             * Establishes the default bindings of <code>type</code> to
1303             * <code>classname</code>.  
1304             * The class will be dynamically loaded later when actually
1305             * needed using the given <code>ClassLoader</code>,
1306             * and can be safely changed 
1307             * before attempted uses to avoid loading unwanted classes.
1308             *
1309             * @param type the non-<code>null</code> content type
1310             * @param classname the class to load later
1311             * @param loader the <code>ClassLoader</code> to use to load the name
1312             */
1313            public static void registerEditorKitForContentType(String type,
1314                    String classname, ClassLoader loader) {
1315                getKitTypeRegistry().put(type, classname);
1316                getKitLoaderRegistry().put(type, loader);
1317                getKitRegisty().remove(type);
1318            }
1319
1320            /**
1321             * Returns the currently registered <code>EditorKit</code>
1322             * class name for the type <code>type</code>.
1323             *
1324             * @param type  the non-<code>null</code> content type
1325             *
1326             * @since 1.3
1327             */
1328            public static String getEditorKitClassNameForContentType(String type) {
1329                return (String) getKitTypeRegistry().get(type);
1330            }
1331
1332            private static Hashtable getKitTypeRegistry() {
1333                loadDefaultKitsIfNecessary();
1334                return (Hashtable) SwingUtilities
1335                        .appContextGet(kitTypeRegistryKey);
1336            }
1337
1338            private static Hashtable getKitLoaderRegistry() {
1339                loadDefaultKitsIfNecessary();
1340                return (Hashtable) SwingUtilities
1341                        .appContextGet(kitLoaderRegistryKey);
1342            }
1343
1344            private static Hashtable getKitRegisty() {
1345                Hashtable ht = (Hashtable) SwingUtilities
1346                        .appContextGet(kitRegistryKey);
1347                if (ht == null) {
1348                    ht = new Hashtable(3);
1349                    SwingUtilities.appContextPut(kitRegistryKey, ht);
1350                }
1351                return ht;
1352            }
1353
1354            /**
1355             * This is invoked every time the registries are accessed. Loading
1356             * is done this way instead of via a static as the static is only
1357             * called once when running in plugin resulting in the entries only
1358             * appearing in the first applet.
1359             */
1360            private static void loadDefaultKitsIfNecessary() {
1361                if (SwingUtilities.appContextGet(kitTypeRegistryKey) == null) {
1362                    synchronized (defaultEditorKitMap) {
1363                        if (defaultEditorKitMap.size() == 0) {
1364                            defaultEditorKitMap.put("text/plain",
1365                                    "javax.swing.JEditorPane$PlainEditorKit");
1366                            defaultEditorKitMap.put("text/html",
1367                                    "javax.swing.text.html.HTMLEditorKit");
1368                            defaultEditorKitMap.put("text/rtf",
1369                                    "javax.swing.text.rtf.RTFEditorKit");
1370                            defaultEditorKitMap.put("application/rtf",
1371                                    "javax.swing.text.rtf.RTFEditorKit");
1372                        }
1373                    }
1374                    Hashtable ht = new Hashtable();
1375                    SwingUtilities.appContextPut(kitTypeRegistryKey, ht);
1376                    ht = new Hashtable();
1377                    SwingUtilities.appContextPut(kitLoaderRegistryKey, ht);
1378                    for (String key : defaultEditorKitMap.keySet()) {
1379                        registerEditorKitForContentType(key,
1380                                defaultEditorKitMap.get(key));
1381                    }
1382
1383                }
1384            }
1385
1386            // --- java.awt.Component methods --------------------------
1387
1388            /**
1389             * Returns the preferred size for the <code>JEditorPane</code>.
1390             * The preferred size for <code>JEditorPane</code> is slightly altered
1391             * from the preferred size of the superclass.  If the size
1392             * of the viewport has become smaller than the minimum size
1393             * of the component, the scrollable definition for tracking
1394             * width or height will turn to false.  The default viewport
1395             * layout will give the preferred size, and that is not desired
1396             * in the case where the scrollable is tracking.  In that case
1397             * the <em>normal</em> preferred size is adjusted to the
1398             * minimum size.  This allows things like HTML tables to
1399             * shrink down to their minimum size and then be laid out at
1400             * their minimum size, refusing to shrink any further.
1401             *
1402             * @return a <code>Dimension</code> containing the preferred size
1403             */
1404            public Dimension getPreferredSize() {
1405                Dimension d = super .getPreferredSize();
1406                if (getParent() instanceof  JViewport) {
1407                    JViewport port = (JViewport) getParent();
1408                    TextUI ui = getUI();
1409                    int prefWidth = d.width;
1410                    int prefHeight = d.height;
1411                    if (!getScrollableTracksViewportWidth()) {
1412                        int w = port.getWidth();
1413                        Dimension min = ui.getMinimumSize(this );
1414                        if (w != 0 && w < min.width) {
1415                            // Only adjust to min if we have a valid size
1416                            prefWidth = min.width;
1417                        }
1418                    }
1419                    if (!getScrollableTracksViewportHeight()) {
1420                        int h = port.getHeight();
1421                        Dimension min = ui.getMinimumSize(this );
1422                        if (h != 0 && h < min.height) {
1423                            // Only adjust to min if we have a valid size
1424                            prefHeight = min.height;
1425                        }
1426                    }
1427                    if (prefWidth != d.width || prefHeight != d.height) {
1428                        d = new Dimension(prefWidth, prefHeight);
1429                    }
1430                }
1431                return d;
1432            }
1433
1434            // --- JTextComponent methods -----------------------------
1435
1436            /**
1437             * Sets the text of this <code>TextComponent</code> to the specified
1438             * content,
1439             * which is expected to be in the format of the content type of
1440             * this editor.  For example, if the type is set to <code>text/html</code>
1441             * the string should be specified in terms of HTML.  
1442             * <p>
1443             * This is implemented to remove the contents of the current document,
1444             * and replace them by parsing the given string using the current
1445             * <code>EditorKit</code>.  This gives the semantics of the
1446             * superclass by not changing
1447             * out the model, while supporting the content type currently set on
1448             * this component.  The assumption is that the previous content is
1449             * relatively
1450             * small, and that the previous content doesn't have side effects.
1451             * Both of those assumptions can be violated and cause undesirable results.
1452             * To avoid this, create a new document,
1453             * <code>getEditorKit().createDefaultDocument()</code>, and replace the
1454             * existing <code>Document</code> with the new one. You are then assured the
1455             * previous <code>Document</code> won't have any lingering state.
1456             * <ol>
1457             * <li>
1458             * Leaving the existing model in place means that the old view will be
1459             * torn down, and a new view created, where replacing the document would
1460             * avoid the tear down of the old view.
1461             * <li>
1462             * Some formats (such as HTML) can install things into the document that
1463             * can influence future contents.  HTML can have style information embedded
1464             * that would influence the next content installed unexpectedly.
1465             * </ol>
1466             * <p>
1467             * An alternative way to load this component with a string would be to
1468             * create a StringReader and call the read method.  In this case the model
1469             * would be replaced after it was initialized with the contents of the
1470             * string.
1471             * <p>
1472             * This method is thread safe, although most Swing methods
1473             * are not. Please see 
1474             * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How
1475             * to Use Threads</A> for more information.     
1476             *
1477             * @param t the new text to be set; if <code>null</code> the old
1478             *    text will be deleted
1479             * @see #getText
1480             * @beaninfo
1481             * description: the text of this component
1482             */
1483            public void setText(String t) {
1484                try {
1485                    Document doc = getDocument();
1486                    doc.remove(0, doc.getLength());
1487                    if (t == null || t.equals("")) {
1488                        return;
1489                    }
1490                    Reader r = new StringReader(t);
1491                    EditorKit kit = getEditorKit();
1492                    kit.read(r, doc, 0);
1493                } catch (IOException ioe) {
1494                    UIManager.getLookAndFeel().provideErrorFeedback(
1495                            JEditorPane.this );
1496                } catch (BadLocationException ble) {
1497                    UIManager.getLookAndFeel().provideErrorFeedback(
1498                            JEditorPane.this );
1499                }
1500            }
1501
1502            /**
1503             * Returns the text contained in this <code>TextComponent</code>
1504             * in terms of the
1505             * content type of this editor.  If an exception is thrown while
1506             * attempting to retrieve the text, <code>null</code> will be returned.
1507             * This is implemented to call <code>JTextComponent.write</code> with
1508             * a <code>StringWriter</code>.
1509             *
1510             * @return the text
1511             * @see #setText
1512             */
1513            public String getText() {
1514                String txt;
1515                try {
1516                    StringWriter buf = new StringWriter();
1517                    write(buf);
1518                    txt = buf.toString();
1519                } catch (IOException ioe) {
1520                    txt = null;
1521                }
1522                return txt;
1523            }
1524
1525            // --- Scrollable  ----------------------------------------
1526
1527            /**
1528             * Returns true if a viewport should always force the width of this 
1529             * <code>Scrollable</code> to match the width of the viewport.  
1530             * 
1531             * @return true if a viewport should force the Scrollables width to
1532             * match its own, false otherwise
1533             */
1534            public boolean getScrollableTracksViewportWidth() {
1535                if (getParent() instanceof  JViewport) {
1536                    JViewport port = (JViewport) getParent();
1537                    TextUI ui = getUI();
1538                    int w = port.getWidth();
1539                    Dimension min = ui.getMinimumSize(this );
1540                    Dimension max = ui.getMaximumSize(this );
1541                    if ((w >= min.width) && (w <= max.width)) {
1542                        return true;
1543                    }
1544                }
1545                return false;
1546            }
1547
1548            /**
1549             * Returns true if a viewport should always force the height of this 
1550             * <code>Scrollable</code> to match the height of the viewport.  
1551             * 
1552             * @return true if a viewport should force the
1553             *		<code>Scrollable</code>'s height to match its own,
1554             *		false otherwise
1555             */
1556            public boolean getScrollableTracksViewportHeight() {
1557                if (getParent() instanceof  JViewport) {
1558                    JViewport port = (JViewport) getParent();
1559                    TextUI ui = getUI();
1560                    int h = port.getHeight();
1561                    Dimension min = ui.getMinimumSize(this );
1562                    if (h >= min.height) {
1563                        Dimension max = ui.getMaximumSize(this );
1564                        if (h <= max.height) {
1565                            return true;
1566                        }
1567                    }
1568                }
1569                return false;
1570            }
1571
1572            // --- Serialization ------------------------------------
1573
1574            /** 
1575             * See <code>readObject</code> and <code>writeObject</code> in
1576             * <code>JComponent</code> for more 
1577             * information about serialization in Swing.
1578             */
1579            private void writeObject(ObjectOutputStream s) throws IOException {
1580                s.defaultWriteObject();
1581                if (getUIClassID().equals(uiClassID)) {
1582                    byte count = JComponent.getWriteObjCounter(this );
1583                    JComponent.setWriteObjCounter(this , --count);
1584                    if (count == 0 && ui != null) {
1585                        ui.installUI(this );
1586                    }
1587                }
1588            }
1589
1590            // --- variables ---------------------------------------
1591
1592            /**
1593             * Stream currently loading asynchronously (potentially cancelable).
1594             * Access to this variable should be synchronized.
1595             */
1596            PageStream loading;
1597
1598            /**
1599             * Current content binding of the editor.
1600             */
1601            private EditorKit kit;
1602            private boolean isUserSetEditorKit;
1603
1604            private Hashtable pageProperties;
1605
1606            /** Should be kept in sync with javax.swing.text.html.FormView counterpart. */
1607            final static String PostDataProperty = "javax.swing.JEditorPane.postdata";
1608
1609            /**
1610             * Table of registered type handlers for this editor.
1611             */
1612            private Hashtable typeHandlers;
1613
1614            /*
1615             * Private AppContext keys for this class's static variables.
1616             */
1617            private static final Object kitRegistryKey = new StringBuffer(
1618                    "JEditorPane.kitRegistry");
1619            private static final Object kitTypeRegistryKey = new StringBuffer(
1620                    "JEditorPane.kitTypeRegistry");
1621            private static final Object kitLoaderRegistryKey = new StringBuffer(
1622                    "JEditorPane.kitLoaderRegistry");
1623
1624            /**
1625             * @see #getUIClassID
1626             * @see #readObject
1627             */
1628            private static final String uiClassID = "EditorPaneUI";
1629
1630            /**
1631             * Key for a client property used to indicate whether
1632             * <a href="http://www.w3.org/TR/CSS21/syndata.html#length-units">
1633             * w3c compliant</a> length units are used for html rendering.
1634             * <p>
1635             * By default this is not enabled; to enable
1636             * it set the client {@link #putClientProperty property} with this name
1637             * to <code>Boolean.TRUE</code>.
1638             *
1639             * @since 1.5
1640             */
1641            public static final String W3C_LENGTH_UNITS = "JEditorPane.w3cLengthUnits";
1642
1643            /**
1644             * Key for a client property used to indicate whether 
1645             * the default font and foreground color from the component are
1646             * used if a font or foreground color is not specified in the styled
1647             * text. 
1648             * <p>
1649             * The default varies based on the look and feel;
1650             * to enable it set the client {@link #putClientProperty property} with
1651             * this name to <code>Boolean.TRUE</code>.
1652             *
1653             * @since 1.5
1654             */
1655            public static final String HONOR_DISPLAY_PROPERTIES = "JEditorPane.honorDisplayProperties";
1656
1657            static final Map<String, String> defaultEditorKitMap = new HashMap<String, String>(
1658                    0);
1659
1660            /**
1661             * Returns a string representation of this <code>JEditorPane</code>.
1662             * This method 
1663             * is intended to be used only for debugging purposes, and the 
1664             * content and format of the returned string may vary between      
1665             * implementations. The returned string may be empty but may not 
1666             * be <code>null</code>.
1667             * 
1668             * @return  a string representation of this <code>JEditorPane</code>
1669             */
1670            protected String paramString() {
1671                String kitString = (kit != null ? kit.toString() : "");
1672                String typeHandlersString = (typeHandlers != null ? typeHandlers
1673                        .toString()
1674                        : "");
1675
1676                return super .paramString() + ",kit=" + kitString
1677                        + ",typeHandlers=" + typeHandlersString;
1678            }
1679
1680            /////////////////
1681            // Accessibility support
1682            ////////////////
1683
1684            /**
1685             * Gets the AccessibleContext associated with this JEditorPane. 
1686             * For editor panes, the AccessibleContext takes the form of an 
1687             * AccessibleJEditorPane. 
1688             * A new AccessibleJEditorPane instance is created if necessary.
1689             *
1690             * @return an AccessibleJEditorPane that serves as the 
1691             *         AccessibleContext of this JEditorPane
1692             */
1693            public AccessibleContext getAccessibleContext() {
1694                if (getEditorKit() instanceof  HTMLEditorKit) {
1695                    if (accessibleContext == null
1696                            || accessibleContext.getClass() != AccessibleJEditorPaneHTML.class) {
1697                        accessibleContext = new AccessibleJEditorPaneHTML();
1698                    }
1699                } else if (accessibleContext == null
1700                        || accessibleContext.getClass() != AccessibleJEditorPane.class) {
1701                    accessibleContext = new AccessibleJEditorPane();
1702                }
1703                return accessibleContext;
1704            }
1705
1706            /**
1707             * This class implements accessibility support for the 
1708             * <code>JEditorPane</code> class.  It provides an implementation of the 
1709             * Java Accessibility API appropriate to editor pane user-interface 
1710             * elements.
1711             * <p>
1712             * <strong>Warning:</strong>
1713             * Serialized objects of this class will not be compatible with
1714             * future Swing releases. The current serialization support is
1715             * appropriate for short term storage or RMI between applications running
1716             * the same version of Swing.  As of 1.4, support for long term storage
1717             * of all JavaBeans<sup><font size="-2">TM</font></sup>
1718             * has been added to the <code>java.beans</code> package.
1719             * Please see {@link java.beans.XMLEncoder}.
1720             */
1721            protected class AccessibleJEditorPane extends
1722                    AccessibleJTextComponent {
1723
1724                /**
1725                 * Gets the accessibleDescription property of this object.  If this
1726                 * property isn't set, returns the content type of this
1727                 * <code>JEditorPane</code> instead (e.g. "plain/text", "html/text").
1728                 *
1729                 * @return the localized description of the object; <code>null</code>
1730                 * 	if this object does not have a description
1731                 *
1732                 * @see #setAccessibleName
1733                 */
1734                public String getAccessibleDescription() {
1735                    String description = accessibleDescription;
1736
1737                    // fallback to client property
1738                    if (description == null) {
1739                        description = (String) getClientProperty(AccessibleContext.ACCESSIBLE_DESCRIPTION_PROPERTY);
1740                    }
1741                    if (description == null) {
1742                        description = JEditorPane.this .getContentType();
1743                    }
1744                    return description;
1745                }
1746
1747                /**
1748                 * Gets the state set of this object.
1749                 *
1750                 * @return an instance of AccessibleStateSet describing the states
1751                 * of the object
1752                 * @see AccessibleStateSet
1753                 */
1754                public AccessibleStateSet getAccessibleStateSet() {
1755                    AccessibleStateSet states = super .getAccessibleStateSet();
1756                    states.add(AccessibleState.MULTI_LINE);
1757                    return states;
1758                }
1759            }
1760
1761            /**
1762             * This class provides support for <code>AccessibleHypertext</code>,
1763             * and is used in instances where the <code>EditorKit</code>
1764             * installed in this <code>JEditorPane</code> is an instance of
1765             * <code>HTMLEditorKit</code>.
1766             * <p>
1767             * <strong>Warning:</strong>
1768             * Serialized objects of this class will not be compatible with
1769             * future Swing releases. The current serialization support is
1770             * appropriate for short term storage or RMI between applications running
1771             * the same version of Swing.  As of 1.4, support for long term storage
1772             * of all JavaBeans<sup><font size="-2">TM</font></sup>
1773             * has been added to the <code>java.beans</code> package.
1774             * Please see {@link java.beans.XMLEncoder}.
1775             */
1776            protected class AccessibleJEditorPaneHTML extends
1777                    AccessibleJEditorPane {
1778
1779                private AccessibleContext accessibleContext;
1780
1781                public AccessibleText getAccessibleText() {
1782                    return new JEditorPaneAccessibleHypertextSupport();
1783                }
1784
1785                protected AccessibleJEditorPaneHTML() {
1786                    HTMLEditorKit kit = (HTMLEditorKit) JEditorPane.this 
1787                            .getEditorKit();
1788                    accessibleContext = kit.getAccessibleContext();
1789                }
1790
1791                /**
1792                 * Returns the number of accessible children of the object.
1793                 *
1794                 * @return the number of accessible children of the object.
1795                 */
1796                public int getAccessibleChildrenCount() {
1797                    if (accessibleContext != null) {
1798                        return accessibleContext.getAccessibleChildrenCount();
1799                    } else {
1800                        return 0;
1801                    }
1802                }
1803
1804                /**
1805                 * Returns the specified Accessible child of the object.  The Accessible
1806                 * children of an Accessible object are zero-based, so the first child 
1807                 * of an Accessible child is at index 0, the second child is at index 1,
1808                 * and so on.
1809                 *
1810                 * @param i zero-based index of child
1811                 * @return the Accessible child of the object
1812                 * @see #getAccessibleChildrenCount
1813                 */
1814                public Accessible getAccessibleChild(int i) {
1815                    if (accessibleContext != null) {
1816                        return accessibleContext.getAccessibleChild(i);
1817                    } else {
1818                        return null;
1819                    }
1820                }
1821
1822                /**
1823                 * Returns the Accessible child, if one exists, contained at the local 
1824                 * coordinate Point.
1825                 *
1826                 * @param p The point relative to the coordinate system of this object.
1827                 * @return the Accessible, if it exists, at the specified location; 
1828                 * otherwise null
1829                 */
1830                public Accessible getAccessibleAt(Point p) {
1831                    if (accessibleContext != null && p != null) {
1832                        try {
1833                            AccessibleComponent acomp = accessibleContext
1834                                    .getAccessibleComponent();
1835                            if (acomp != null) {
1836                                return acomp.getAccessibleAt(p);
1837                            } else {
1838                                return null;
1839                            }
1840                        } catch (IllegalComponentStateException e) {
1841                            return null;
1842                        }
1843                    } else {
1844                        return null;
1845                    }
1846                }
1847            }
1848
1849            /**
1850             * What's returned by
1851             * <code>AccessibleJEditorPaneHTML.getAccessibleText</code>.
1852             *
1853             * Provides support for <code>AccessibleHypertext</code> in case
1854             * there is an HTML document being displayed in this
1855             * <code>JEditorPane</code>.
1856             * 
1857             */
1858            protected class JEditorPaneAccessibleHypertextSupport extends
1859                    AccessibleJEditorPane implements  AccessibleHypertext {
1860
1861                public class HTMLLink extends AccessibleHyperlink {
1862                    Element element;
1863
1864                    public HTMLLink(Element e) {
1865                        element = e;
1866                    }
1867
1868                    /**
1869                     * Since the document a link is associated with may have
1870                     * changed, this method returns whether this Link is valid
1871                     * anymore (with respect to the document it references).
1872                     *
1873                     * @return a flag indicating whether this link is still valid with
1874                     *         respect to the AccessibleHypertext it belongs to
1875                     */
1876                    public boolean isValid() {
1877                        return JEditorPaneAccessibleHypertextSupport.this .linksValid;
1878                    }
1879
1880                    /**
1881                     * Returns the number of accessible actions available in this Link
1882                     * If there are more than one, the first one is NOT considered the
1883                     * "default" action of this LINK object (e.g. in an HTML imagemap).
1884                     * In general, links will have only one AccessibleAction in them.
1885                     *
1886                     * @return the zero-based number of Actions in this object
1887                     */
1888                    public int getAccessibleActionCount() {
1889                        return 1;
1890                    }
1891
1892                    /**
1893                     * Perform the specified Action on the object
1894                     *
1895                     * @param i zero-based index of actions
1896                     * @return true if the the action was performed; else false.
1897                     * @see #getAccessibleActionCount
1898                     */
1899                    public boolean doAccessibleAction(int i) {
1900                        if (i == 0 && isValid() == true) {
1901                            URL u = (URL) getAccessibleActionObject(i);
1902                            if (u != null) {
1903                                HyperlinkEvent linkEvent = new HyperlinkEvent(
1904                                        JEditorPane.this ,
1905                                        HyperlinkEvent.EventType.ACTIVATED, u);
1906                                JEditorPane.this .fireHyperlinkUpdate(linkEvent);
1907                                return true;
1908                            }
1909                        }
1910                        return false; // link invalid or i != 0
1911                    }
1912
1913                    /**
1914                     * Return a String description of this particular
1915                     * link action.  The string returned is the text
1916                     * within the document associated with the element
1917                     * which contains this link.
1918                     *
1919                     * @param i zero-based index of the actions
1920                     * @return a String description of the action
1921                     * @see #getAccessibleActionCount
1922                     */
1923                    public String getAccessibleActionDescription(int i) {
1924                        if (i == 0 && isValid() == true) {
1925                            Document d = JEditorPane.this .getDocument();
1926                            if (d != null) {
1927                                try {
1928                                    return d.getText(getStartIndex(),
1929                                            getEndIndex() - getStartIndex());
1930                                } catch (BadLocationException exception) {
1931                                    return null;
1932                                }
1933                            }
1934                        }
1935                        return null;
1936                    }
1937
1938                    /**
1939                     * Returns a URL object that represents the link.
1940                     *
1941                     * @param i zero-based index of the actions
1942                     * @return an URL representing the HTML link itself
1943                     * @see #getAccessibleActionCount
1944                     */
1945                    public Object getAccessibleActionObject(int i) {
1946                        if (i == 0 && isValid() == true) {
1947                            AttributeSet as = element.getAttributes();
1948                            AttributeSet anchor = (AttributeSet) as
1949                                    .getAttribute(HTML.Tag.A);
1950                            String href = (anchor != null) ? (String) anchor
1951                                    .getAttribute(HTML.Attribute.HREF) : null;
1952                            if (href != null) {
1953                                URL u;
1954                                try {
1955                                    u = new URL(JEditorPane.this .getPage(),
1956                                            href);
1957                                } catch (MalformedURLException m) {
1958                                    u = null;
1959                                }
1960                                return u;
1961                            }
1962                        }
1963                        return null; // link invalid or i != 0
1964                    }
1965
1966                    /**
1967                     * Return an object that represents the link anchor,
1968                     * as appropriate for that link.  E.g. from HTML:
1969                     *   <a href="http://www.sun.com/access">Accessibility</a>
1970                     * this method would return a String containing the text:
1971                     * 'Accessibility'.
1972                     *
1973                     * Similarly, from this HTML:
1974                     *   &lt;a HREF="#top"&gt;&lt;img src="top-hat.gif" alt="top hat"&gt;&lt;/a&gt;
1975                     * this might return the object ImageIcon("top-hat.gif", "top hat");
1976                     *
1977                     * @param i zero-based index of the actions
1978                     * @return an Object representing the hypertext anchor
1979                     * @see #getAccessibleActionCount
1980                     */
1981                    public Object getAccessibleActionAnchor(int i) {
1982                        return getAccessibleActionDescription(i);
1983                    }
1984
1985                    /**
1986                     * Get the index with the hypertext document at which this
1987                     * link begins
1988                     *
1989                     * @return index of start of link
1990                     */
1991                    public int getStartIndex() {
1992                        return element.getStartOffset();
1993                    }
1994
1995                    /**
1996                     * Get the index with the hypertext document at which this
1997                     * link ends
1998                     *
1999                     * @return index of end of link
2000                     */
2001                    public int getEndIndex() {
2002                        return element.getEndOffset();
2003                    }
2004                }
2005
2006                private class LinkVector extends Vector {
2007                    public int baseElementIndex(Element e) {
2008                        HTMLLink l;
2009                        for (int i = 0; i < elementCount; i++) {
2010                            l = (HTMLLink) elementAt(i);
2011                            if (l.element == e) {
2012                                return i;
2013                            }
2014                        }
2015                        return -1;
2016                    }
2017                }
2018
2019                LinkVector hyperlinks;
2020                boolean linksValid = false;
2021
2022                /**
2023                 * Build the private table mapping links to locations in the text
2024                 */
2025                private void buildLinkTable() {
2026                    hyperlinks.removeAllElements();
2027                    Document d = JEditorPane.this .getDocument();
2028                    if (d != null) {
2029                        ElementIterator ei = new ElementIterator(d);
2030                        Element e;
2031                        AttributeSet as;
2032                        AttributeSet anchor;
2033                        String href;
2034                        while ((e = ei.next()) != null) {
2035                            if (e.isLeaf()) {
2036                                as = e.getAttributes();
2037                                anchor = (AttributeSet) as
2038                                        .getAttribute(HTML.Tag.A);
2039                                href = (anchor != null) ? (String) anchor
2040                                        .getAttribute(HTML.Attribute.HREF)
2041                                        : null;
2042                                if (href != null) {
2043                                    hyperlinks.addElement(new HTMLLink(e));
2044                                }
2045                            }
2046                        }
2047                    }
2048                    linksValid = true;
2049                }
2050
2051                /**
2052                 * Make one of these puppies
2053                 */
2054                public JEditorPaneAccessibleHypertextSupport() {
2055                    hyperlinks = new LinkVector();
2056                    Document d = JEditorPane.this .getDocument();
2057                    if (d != null) {
2058                        d.addDocumentListener(new DocumentListener() {
2059                            public void changedUpdate(DocumentEvent theEvent) {
2060                                linksValid = false;
2061                            }
2062
2063                            public void insertUpdate(DocumentEvent theEvent) {
2064                                linksValid = false;
2065                            }
2066
2067                            public void removeUpdate(DocumentEvent theEvent) {
2068                                linksValid = false;
2069                            }
2070                        });
2071                    }
2072                }
2073
2074                /**
2075                 * Returns the number of links within this hypertext doc.
2076                 *
2077                 * @return number of links in this hypertext doc.
2078                 */
2079                public int getLinkCount() {
2080                    if (linksValid == false) {
2081                        buildLinkTable();
2082                    }
2083                    return hyperlinks.size();
2084                }
2085
2086                /**
2087                 * Returns the index into an array of hyperlinks that
2088                 * is associated with this character index, or -1 if there
2089                 * is no hyperlink associated with this index.
2090                 *
2091                 * @param  charIndex index within the text
2092                 * @return index into the set of hyperlinks for this hypertext doc.
2093                 */
2094                public int getLinkIndex(int charIndex) {
2095                    if (linksValid == false) {
2096                        buildLinkTable();
2097                    }
2098                    Element e = null;
2099                    Document doc = JEditorPane.this .getDocument();
2100                    if (doc != null) {
2101                        for (e = doc.getDefaultRootElement(); !e.isLeaf();) {
2102                            int index = e.getElementIndex(charIndex);
2103                            e = e.getElement(index);
2104                        }
2105                    }
2106
2107                    // don't need to verify that it's an HREF element; if
2108                    // not, then it won't be in the hyperlinks Vector, and
2109                    // so indexOf will return -1 in any case
2110                    return hyperlinks.baseElementIndex(e);
2111                }
2112
2113                /**
2114                 * Returns the index into an array of hyperlinks that
2115                 * index.  If there is no hyperlink at this index, it returns
2116                 * null.
2117                 *
2118                 * @param linkIndex into the set of hyperlinks for this hypertext doc.
2119                 * @return string representation of the hyperlink
2120                 */
2121                public AccessibleHyperlink getLink(int linkIndex) {
2122                    if (linksValid == false) {
2123                        buildLinkTable();
2124                    }
2125                    if (linkIndex >= 0 && linkIndex < hyperlinks.size()) {
2126                        return (AccessibleHyperlink) hyperlinks
2127                                .elementAt(linkIndex);
2128                    } else {
2129                        return null;
2130                    }
2131                }
2132
2133                /**
2134                 * Returns the contiguous text within the document that
2135                 * is associated with this hyperlink.
2136                 *
2137                 * @param linkIndex into the set of hyperlinks for this hypertext doc.
2138                 * @return the contiguous text sharing the link at this index
2139                 */
2140                public String getLinkText(int linkIndex) {
2141                    if (linksValid == false) {
2142                        buildLinkTable();
2143                    }
2144                    Element e = (Element) hyperlinks.elementAt(linkIndex);
2145                    if (e != null) {
2146                        Document d = JEditorPane.this .getDocument();
2147                        if (d != null) {
2148                            try {
2149                                return d.getText(e.getStartOffset(), e
2150                                        .getEndOffset()
2151                                        - e.getStartOffset());
2152                            } catch (BadLocationException exception) {
2153                                return null;
2154                            }
2155                        }
2156                    }
2157                    return null;
2158                }
2159            }
2160
2161            static class PlainEditorKit extends DefaultEditorKit implements 
2162                    ViewFactory {
2163
2164                /**
2165                 * Fetches a factory that is suitable for producing 
2166                 * views of any models that are produced by this
2167                 * kit.  The default is to have the UI produce the
2168                 * factory, so this method has no implementation.
2169                 *
2170                 * @return the view factory
2171                 */
2172                public ViewFactory getViewFactory() {
2173                    return this ;
2174                }
2175
2176                /**
2177                 * Creates a view from the given structural element of a
2178                 * document.
2179                 *
2180                 * @param elem  the piece of the document to build a view of
2181                 * @return the view
2182                 * @see View
2183                 */
2184                public View create(Element elem) {
2185                    Document doc = elem.getDocument();
2186                    Object i18nFlag = doc
2187                            .getProperty("i18n"/*AbstractDocument.I18NProperty*/);
2188                    if ((i18nFlag != null) && i18nFlag.equals(Boolean.TRUE)) {
2189                        // build a view that support bidi
2190                        return createI18N(elem);
2191                    } else {
2192                        return new WrappedPlainView(elem);
2193                    }
2194                }
2195
2196                View createI18N(Element elem) {
2197                    String kind = elem.getName();
2198                    if (kind != null) {
2199                        if (kind.equals(AbstractDocument.ContentElementName)) {
2200                            return new PlainParagraph(elem);
2201                        } else if (kind
2202                                .equals(AbstractDocument.ParagraphElementName)) {
2203                            return new BoxView(elem, View.Y_AXIS);
2204                        }
2205                    }
2206                    return null;
2207                }
2208
2209                /**
2210                 * Paragraph for representing plain-text lines that support
2211                 * bidirectional text.
2212                 */
2213                static class PlainParagraph extends
2214                        javax.swing.text.ParagraphView {
2215
2216                    PlainParagraph(Element elem) {
2217                        super (elem);
2218                        layoutPool = new LogicalView(elem);
2219                        layoutPool.setParent(this );
2220                    }
2221
2222                    protected void setPropertiesFromAttributes() {
2223                        Component c = getContainer();
2224                        if ((c != null)
2225                                && (!c.getComponentOrientation()
2226                                        .isLeftToRight())) {
2227                            setJustification(StyleConstants.ALIGN_RIGHT);
2228                        } else {
2229                            setJustification(StyleConstants.ALIGN_LEFT);
2230                        }
2231                    }
2232
2233                    /**
2234                     * Fetch the constraining span to flow against for
2235                     * the given child index.
2236                     */
2237                    public int getFlowSpan(int index) {
2238                        Component c = getContainer();
2239                        if (c instanceof  JTextArea) {
2240                            JTextArea area = (JTextArea) c;
2241                            if (!area.getLineWrap()) {
2242                                // no limit if unwrapped
2243                                return Integer.MAX_VALUE;
2244                            }
2245                        }
2246                        return super .getFlowSpan(index);
2247                    }
2248
2249                    protected SizeRequirements calculateMinorAxisRequirements(
2250                            int axis, SizeRequirements r) {
2251                        SizeRequirements req = super 
2252                                .calculateMinorAxisRequirements(axis, r);
2253                        Component c = getContainer();
2254                        if (c instanceof  JTextArea) {
2255                            JTextArea area = (JTextArea) c;
2256                            if (!area.getLineWrap()) {
2257                                // min is pref if unwrapped
2258                                req.minimum = req.preferred;
2259                            }
2260                        }
2261                        return req;
2262                    }
2263
2264                    /**
2265                     * This class can be used to represent a logical view for 
2266                     * a flow.  It keeps the children updated to reflect the state
2267                     * of the model, gives the logical child views access to the
2268                     * view hierarchy, and calculates a preferred span.  It doesn't
2269                     * do any rendering, layout, or model/view translation.
2270                     */
2271                    static class LogicalView extends CompositeView {
2272
2273                        LogicalView(Element elem) {
2274                            super (elem);
2275                        }
2276
2277                        protected int getViewIndexAtPosition(int pos) {
2278                            Element elem = getElement();
2279                            if (elem.getElementCount() > 0) {
2280                                return elem.getElementIndex(pos);
2281                            }
2282                            return 0;
2283                        }
2284
2285                        protected boolean updateChildren(
2286                                DocumentEvent.ElementChange ec,
2287                                DocumentEvent e, ViewFactory f) {
2288                            return false;
2289                        }
2290
2291                        protected void loadChildren(ViewFactory f) {
2292                            Element elem = getElement();
2293                            if (elem.getElementCount() > 0) {
2294                                super .loadChildren(f);
2295                            } else {
2296                                View v = new GlyphView(elem);
2297                                append(v);
2298                            }
2299                        }
2300
2301                        public float getPreferredSpan(int axis) {
2302                            if (getViewCount() != 1)
2303                                throw new Error("One child view is assumed.");
2304
2305                            View v = getView(0);
2306                            //((GlyphView)v).setGlyphPainter(null);
2307                            return v.getPreferredSpan(axis);
2308                        }
2309
2310                        /**
2311                         * Forward the DocumentEvent to the given child view.  This
2312                         * is implemented to reparent the child to the logical view
2313                         * (the children may have been parented by a row in the flow
2314                         * if they fit without breaking) and then execute the 
2315                         * superclass behavior.
2316                         *
2317                         * @param v the child view to forward the event to.
2318                         * @param e the change information from the associated document
2319                         * @param a the current allocation of the view
2320                         * @param f the factory to use to rebuild if the view has 
2321                         *          children
2322                         * @see #forwardUpdate
2323                         * @since 1.3
2324                         */
2325                        protected void forwardUpdateToView(View v,
2326                                DocumentEvent e, Shape a, ViewFactory f) {
2327                            v.setParent(this );
2328                            super .forwardUpdateToView(v, e, a, f);
2329                        }
2330
2331                        // The following methods don't do anything useful, they
2332                        // simply keep the class from being abstract.
2333
2334                        public void paint(Graphics g, Shape allocation) {
2335                        }
2336
2337                        protected boolean isBefore(int x, int y, Rectangle alloc) {
2338                            return false;
2339                        }
2340
2341                        protected boolean isAfter(int x, int y, Rectangle alloc) {
2342                            return false;
2343                        }
2344
2345                        protected View getViewAtPoint(int x, int y,
2346                                Rectangle alloc) {
2347                            return null;
2348                        }
2349
2350                        protected void childAllocation(int index, Rectangle a) {
2351                        }
2352                    }
2353                }
2354            }
2355
2356            /* This is useful for the nightmare of parsing multi-part HTTP/RFC822 headers
2357             * sensibly:
2358             * From a String like: 'timeout=15, max=5'
2359             * create an array of Strings:
2360             * { {"timeout", "15"},
2361             *   {"max", "5"}
2362             * }
2363             * From one like: 'Basic Realm="FuzzFace" Foo="Biz Bar Baz"'
2364             * create one like (no quotes in literal):
2365             * { {"basic", null},
2366             *   {"realm", "FuzzFace"}
2367             *   {"foo", "Biz Bar Baz"}
2368             * }
2369             * keys are converted to lower case, vals are left as is....
2370             *
2371             * author Dave Brown
2372             */
2373
2374            static class HeaderParser {
2375
2376                /* table of key/val pairs - maxes out at 10!!!!*/
2377                String raw;
2378                String[][] tab;
2379
2380                public HeaderParser(String raw) {
2381                    this .raw = raw;
2382                    tab = new String[10][2];
2383                    parse();
2384                }
2385
2386                private void parse() {
2387
2388                    if (raw != null) {
2389                        raw = raw.trim();
2390                        char[] ca = raw.toCharArray();
2391                        int beg = 0, end = 0, i = 0;
2392                        boolean inKey = true;
2393                        boolean inQuote = false;
2394                        int len = ca.length;
2395                        while (end < len) {
2396                            char c = ca[end];
2397                            if (c == '=') { // end of a key
2398                                tab[i][0] = new String(ca, beg, end - beg)
2399                                        .toLowerCase();
2400                                inKey = false;
2401                                end++;
2402                                beg = end;
2403                            } else if (c == '\"') {
2404                                if (inQuote) {
2405                                    tab[i++][1] = new String(ca, beg, end - beg);
2406                                    inQuote = false;
2407                                    do {
2408                                        end++;
2409                                    } while (end < len
2410                                            && (ca[end] == ' ' || ca[end] == ','));
2411                                    inKey = true;
2412                                    beg = end;
2413                                } else {
2414                                    inQuote = true;
2415                                    end++;
2416                                    beg = end;
2417                                }
2418                            } else if (c == ' ' || c == ',') { // end key/val, of whatever we're in
2419                                if (inQuote) {
2420                                    end++;
2421                                    continue;
2422                                } else if (inKey) {
2423                                    tab[i++][0] = (new String(ca, beg, end
2424                                            - beg)).toLowerCase();
2425                                } else {
2426                                    tab[i++][1] = (new String(ca, beg, end
2427                                            - beg));
2428                                }
2429                                while (end < len
2430                                        && (ca[end] == ' ' || ca[end] == ',')) {
2431                                    end++;
2432                                }
2433                                inKey = true;
2434                                beg = end;
2435                            } else {
2436                                end++;
2437                            }
2438                        }
2439                        // get last key/val, if any
2440                        if (--end > beg) {
2441                            if (!inKey) {
2442                                if (ca[end] == '\"') {
2443                                    tab[i++][1] = (new String(ca, beg, end
2444                                            - beg));
2445                                } else {
2446                                    tab[i++][1] = (new String(ca, beg, end
2447                                            - beg + 1));
2448                                }
2449                            } else {
2450                                tab[i][0] = (new String(ca, beg, end - beg + 1))
2451                                        .toLowerCase();
2452                            }
2453                        } else if (end == beg) {
2454                            if (!inKey) {
2455                                if (ca[end] == '\"') {
2456                                    tab[i++][1] = String.valueOf(ca[end - 1]);
2457                                } else {
2458                                    tab[i++][1] = String.valueOf(ca[end]);
2459                                }
2460                            } else {
2461                                tab[i][0] = String.valueOf(ca[end])
2462                                        .toLowerCase();
2463                            }
2464                        }
2465                    }
2466
2467                }
2468
2469                public String findKey(int i) {
2470                    if (i < 0 || i > 10)
2471                        return null;
2472                    return tab[i][0];
2473                }
2474
2475                public String findValue(int i) {
2476                    if (i < 0 || i > 10)
2477                        return null;
2478                    return tab[i][1];
2479                }
2480
2481                public String findValue(String key) {
2482                    return findValue(key, null);
2483                }
2484
2485                public String findValue(String k, String Default) {
2486                    if (k == null)
2487                        return Default;
2488                    k = k.toLowerCase();
2489                    for (int i = 0; i < 10; ++i) {
2490                        if (tab[i][0] == null) {
2491                            return Default;
2492                        } else if (k.equals(tab[i][0])) {
2493                            return tab[i][1];
2494                        }
2495                    }
2496                    return Default;
2497                }
2498
2499                public int findInt(String k, int Default) {
2500                    try {
2501                        return Integer.parseInt(findValue(k, String
2502                                .valueOf(Default)));
2503                    } catch (Throwable t) {
2504                        return Default;
2505                    }
2506                }
2507            }
2508
2509        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.