2288 lines · 88.6 KB · Java
RawDownload
/* * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */package javax.swing.text.html; import sun.awt.AppContext; import java.lang.reflect.Method;import java.awt.*;import java.awt.event.*;import java.io.*;import java.net.MalformedURLException;import java.net.URL;import javax.swing.text.*;import javax.swing.*;import javax.swing.border.*;import javax.swing.event.*;import javax.swing.plaf.TextUI;import java.util.*;import javax.accessibility.*;import java.lang.ref.*; /** * The Swing JEditorPane text component supports different kinds * of content via a plug-in mechanism called an EditorKit.  Because * HTML is a very popular format of content, some support is provided * by default.  The default support is provided by this class, which * supports HTML version 3.2 (with some extensions), and is migrating * toward version 4.0. * The &lt;applet&gt; tag is not supported, but some support is provided * for the &lt;object&gt; tag. * <p> * There are several goals of the HTML EditorKit provided, that have * an effect upon the way that HTML is modeled.  These * have influenced its design in a substantial way. * <dl> * <p> * <dt> * Support editing * <dd> * It might seem fairly obvious that a plug-in for JEditorPane * should provide editing support, but that fact has several * design considerations.  There are a substantial number of HTML * documents that don't properly conform to an HTML specification. * These must be normalized somewhat into a correct form if one * is to edit them.  Additionally, users don't like to be presented * with an excessive amount of structure editing, so using traditional * text editing gestures is preferred over using the HTML structure * exactly as defined in the HTML document. * <p> * The modeling of HTML is provided by the class <code>HTMLDocument</code>. * Its documention describes the details of how the HTML is modeled. * The editing support leverages heavily off of the text package. * <p> * <dt> * Extendable/Scalable * <dd> * To maximize the usefulness of this kit, a great deal of effort * has gone into making it extendable.  These are some of the * features. * <ol> *   <li> *   The parser is replacable.  The default parser is the Hot Java *   parser which is DTD based.  A different DTD can be used, or an *   entirely different parser can be used.  To change the parser, *   reimplement the getParser method.  The default parser is *   dynamically loaded when first asked for, so the class files *   will never be loaded if an alternative parser is used.  The *   default parser is in a separate package called parser below *   this package. *   <li> *   The parser drives the ParserCallback, which is provided by *   HTMLDocument.  To change the callback, subclass HTMLDocument *   and reimplement the createDefaultDocument method to return *   document that produces a different reader.  The reader controls *   how the document is structured.  Although the Document provides *   HTML support by default, there is nothing preventing support of *   non-HTML tags that result in alternative element structures. *   <li> *   The default view of the models are provided as a hierarchy of *   View implementations, so one can easily customize how a particular *   element is displayed or add capabilities for new kinds of elements *   by providing new View implementations.  The default set of views *   are provided by the <code>HTMLFactory</code> class.  This can *   be easily changed by subclassing or replacing the HTMLFactory *   and reimplementing the getViewFactory method to return the alternative *   factory. *   <li> *   The View implementations work primarily off of CSS attributes, *   which are kept in the views.  This makes it possible to have *   multiple views mapped over the same model that appear substantially *   different.  This can be especially useful for printing.  For *   most HTML attributes, the HTML attributes are converted to CSS *   attributes for display.  This helps make the View implementations *   more general purpose * </ol> * <p> * <dt> * Asynchronous Loading * <dd> * Larger documents involve a lot of parsing and take some time * to load.  By default, this kit produces documents that will be * loaded asynchronously if loaded using <code>JEditorPane.setPage</code>. * This is controlled by a property on the document.  The method * {@link #createDefaultDocument createDefaultDocument} can * be overriden to change this.  The batching of work is done * by the <code>HTMLDocument.HTMLReader</code> class.  The actual * work is done by the <code>DefaultStyledDocument</code> and * <code>AbstractDocument</code> classes in the text package. * <p> * <dt> * Customization from current LAF * <dd> * HTML provides a well known set of features without exactly * specifying the display characteristics.  Swing has a theme * mechanism for its look-and-feel implementations.  It is desirable * for the look-and-feel to feed display characteristics into the * HTML views.  An user with poor vision for example would want * high contrast and larger than typical fonts. * <p> * The support for this is provided by the <code>StyleSheet</code> * class.  The presentation of the HTML can be heavily influenced * by the setting of the StyleSheet property on the EditorKit. * <p> * <dt> * Not lossy * <dd> * An EditorKit has the ability to be read and save documents. * It is generally the most pleasing to users if there is no loss * of data between the two operation.  The policy of the HTMLEditorKit * will be to store things not recognized or not necessarily visible * so they can be subsequently written out.  The model of the HTML document * should therefore contain all information discovered while reading the * document.  This is constrained in some ways by the need to support * editing (i.e. incorrect documents sometimes must be normalized). * The guiding principle is that information shouldn't be lost, but * some might be synthesized to produce a more correct model or it might * be rearranged. * </dl> * * @author  Timothy Prinzing */public class HTMLEditorKit extends StyledEditorKit implements Accessible {     private JEditorPane theEditor;     /**     * Constructs an HTMLEditorKit, creates a StyleContext,     * and loads the style sheet.     */    public HTMLEditorKit() {     }     /**     * Get the MIME type of the data that this     * kit represents support for.  This kit supports     * the type <code>text/html</code>.     *     * @return the type     */    public String getContentType() {        return "text/html";    }     /**     * Fetch a factory that is suitable for producing     * views of any models that are produced by this     * kit.     *     * @return the factory     */    public ViewFactory getViewFactory() {        return defaultFactory;    }     /**     * Create an uninitialized text storage model     * that is appropriate for this type of editor.     *     * @return the model     */    public Document createDefaultDocument() {        StyleSheet styles = getStyleSheet();        StyleSheet ss = new StyleSheet();         ss.addStyleSheet(styles);         HTMLDocument doc = new HTMLDocument(ss);        doc.setParser(getParser());        doc.setAsynchronousLoadPriority(4);        doc.setTokenThreshold(100);        return doc;    }     /**     * Try to get an HTML parser from the document.  If no parser is set for     * the document, return the editor kit's default parser.  It is an error     * if no parser could be obtained from the editor kit.     */    private Parser ensureParser(HTMLDocument doc) throws IOException {        Parser p = doc.getParser();        if (p == null) {            p = getParser();        }        if (p == null) {            throw new IOException("Can't load parser");        }        return p;    }     /**     * Inserts content from the given stream. If <code>doc</code> is     * an instance of HTMLDocument, this will read     * HTML 3.2 text. Inserting HTML into a non-empty document must be inside     * the body Element, if you do not insert into the body an exception will     * be thrown. When inserting into a non-empty document all tags outside     * of the body (head, title) will be dropped.     *     * @param in  the stream to read from     * @param doc the destination for the insertion     * @param pos the location in the document to place the     *   content     * @exception IOException on any I/O error     * @exception BadLocationException if pos represents an invalid     *   location within the document     * @exception RuntimeException (will eventually be a BadLocationException)     *            if pos is invalid     */    public void read(Reader in, Document doc, int pos) throws IOException, BadLocationException {         if (doc instanceof HTMLDocument) {            HTMLDocument hdoc = (HTMLDocument) doc;            if (pos > doc.getLength()) {                throw new BadLocationException("Invalid location", pos);            }             Parser p = ensureParser(hdoc);            ParserCallback receiver = hdoc.getReader(pos);            Boolean ignoreCharset = (Boolean)doc.getProperty("IgnoreCharsetDirective");            p.parse(in, receiver, (ignoreCharset == null) ? false : ignoreCharset.booleanValue());            receiver.flush();        } else {            super.read(in, doc, pos);        }    }     /**     * Inserts HTML into an existing document.     *     * @param doc       the document to insert into     * @param offset    the offset to insert HTML at     * @param popDepth  the number of ElementSpec.EndTagTypes to generate before     *        inserting     * @param pushDepth the number of ElementSpec.StartTagTypes with a direction     *        of ElementSpec.JoinNextDirection that should be generated     *        before inserting, but after the end tags have been generated     * @param insertTag the first tag to start inserting into document     * @exception RuntimeException (will eventually be a BadLocationException)     *            if pos is invalid     */    public void insertHTML(HTMLDocument doc, int offset, String html,                           int popDepth, int pushDepth,                           HTML.Tag insertTag) throws                       BadLocationException, IOException {        if (offset > doc.getLength()) {            throw new BadLocationException("Invalid location", offset);        }         Parser p = ensureParser(doc);        ParserCallback receiver = doc.getReader(offset, popDepth, pushDepth,                                                insertTag);        Boolean ignoreCharset = (Boolean)doc.getProperty                                ("IgnoreCharsetDirective");        p.parse(new StringReader(html), receiver, (ignoreCharset == null) ?                false : ignoreCharset.booleanValue());        receiver.flush();    }     /**     * Write content from a document to the given stream     * in a format appropriate for this kind of content handler.     *     * @param out  the stream to write to     * @param doc  the source for the write     * @param pos  the location in the document to fetch the     *   content     * @param len  the amount to write out     * @exception IOException on any I/O error     * @exception BadLocationException if pos represents an invalid     *   location within the document     */    public void write(Writer out, Document doc, int pos, int len)        throws IOException, BadLocationException {         if (doc instanceof HTMLDocument) {            HTMLWriter w = new HTMLWriter(out, (HTMLDocument)doc, pos, len);            w.write();        } else if (doc instanceof StyledDocument) {            MinimalHTMLWriter w = new MinimalHTMLWriter(out, (StyledDocument)doc, pos, len);            w.write();        } else {            super.write(out, doc, pos, len);        }    }     /**     * Called when the kit is being installed into the     * a JEditorPane.     *     * @param c the JEditorPane     */    public void install(JEditorPane c) {        c.addMouseListener(linkHandler);        c.addMouseMotionListener(linkHandler);        c.addCaretListener(nextLinkAction);        super.install(c);        theEditor = c;    }     /**     * Called when the kit is being removed from the     * JEditorPane.  This is used to unregister any     * listeners that were attached.     *     * @param c the JEditorPane     */    public void deinstall(JEditorPane c) {        c.removeMouseListener(linkHandler);        c.removeMouseMotionListener(linkHandler);        c.removeCaretListener(nextLinkAction);        super.deinstall(c);        theEditor = null;    }     /**     * Default Cascading Style Sheet file that sets     * up the tag views.     */    public static final String DEFAULT_CSS = "default.css";     /**     * Set the set of styles to be used to render the various     * HTML elements.  These styles are specified in terms of     * CSS specifications.  Each document produced by the kit     * will have a copy of the sheet which it can add the     * document specific styles to.  By default, the StyleSheet     * specified is shared by all HTMLEditorKit instances.     * This should be reimplemented to provide a finer granularity     * if desired.     */    public void setStyleSheet(StyleSheet s) {        if (s == null) {            AppContext.getAppContext().remove(DEFAULT_STYLES_KEY);        } else {            AppContext.getAppContext().put(DEFAULT_STYLES_KEY, s);        }    }     /**     * Get the set of styles currently being used to render the     * HTML elements.  By default the resource specified by     * DEFAULT_CSS gets loaded, and is shared by all HTMLEditorKit     * instances.     */    public StyleSheet getStyleSheet() {        AppContext appContext = AppContext.getAppContext();        StyleSheet defaultStyles = (StyleSheet) appContext.get(DEFAULT_STYLES_KEY);         if (defaultStyles == null) {            defaultStyles = new StyleSheet();            appContext.put(DEFAULT_STYLES_KEY, defaultStyles);            try {                InputStream is = HTMLEditorKit.getResourceAsStream(DEFAULT_CSS);                Reader r = new BufferedReader(                        new InputStreamReader(is, "ISO-8859-1"));                defaultStyles.loadRules(r, null);                r.close();            } catch (Throwable e) {                // on error we simply have no styles... the html                // will look mighty wrong but still function.            }        }        return defaultStyles;    }     /**     * Fetch a resource relative to the HTMLEditorKit classfile.     * If this is called on 1.2 the loading will occur under the     * protection of a doPrivileged call to allow the HTMLEditorKit     * to function when used in an applet.     *     * @param name the name of the resource, relative to the     *  HTMLEditorKit class     * @return a stream representing the resource     */    static InputStream getResourceAsStream(String name) {        try {            return ResourceLoader.getResourceAsStream(name);        } catch (Throwable e) {            // If the class doesn't exist or we have some other            // problem we just try to call getResourceAsStream directly.            return HTMLEditorKit.class.getResourceAsStream(name);        }    }     /**     * Fetches the command list for the editor.  This is     * the list of commands supported by the superclass     * augmented by the collection of commands defined     * locally for style operations.     *     * @return the command list     */    public Action[] getActions() {        return TextAction.augmentList(super.getActions(), this.defaultActions);    }     /**     * Copies the key/values in <code>element</code>s AttributeSet into     * <code>set</code>. This does not copy component, icon, or element     * names attributes. Subclasses may wish to refine what is and what     * isn't copied here. But be sure to first remove all the attributes that     * are in <code>set</code>.<p>     * This is called anytime the caret moves over a different location.     *     */    protected void createInputAttributes(Element element,                                         MutableAttributeSet set) {        set.removeAttributes(set);        set.addAttributes(element.getAttributes());        set.removeAttribute(StyleConstants.ComposedTextAttribute);         Object o = set.getAttribute(StyleConstants.NameAttribute);        if (o instanceof HTML.Tag) {            HTML.Tag tag = (HTML.Tag)o;            // PENDING: we need a better way to express what shouldn't be            // copied when editing...            if(tag == HTML.Tag.IMG) {                // Remove the related image attributes, src, width, height                set.removeAttribute(HTML.Attribute.SRC);                set.removeAttribute(HTML.Attribute.HEIGHT);                set.removeAttribute(HTML.Attribute.WIDTH);                set.addAttribute(StyleConstants.NameAttribute,                                 HTML.Tag.CONTENT);            }            else if (tag == HTML.Tag.HR || tag == HTML.Tag.BR) {                // Don't copy HRs or BRs either.                set.addAttribute(StyleConstants.NameAttribute,                                 HTML.Tag.CONTENT);            }            else if (tag == HTML.Tag.COMMENT) {                // Don't copy COMMENTs either                set.addAttribute(StyleConstants.NameAttribute,                                 HTML.Tag.CONTENT);                set.removeAttribute(HTML.Attribute.COMMENT);            }            else if (tag == HTML.Tag.INPUT) {                // or INPUT either                set.addAttribute(StyleConstants.NameAttribute,                                 HTML.Tag.CONTENT);                set.removeAttribute(HTML.Tag.INPUT);            }            else if (tag instanceof HTML.UnknownTag) {                // Don't copy unknowns either:(                set.addAttribute(StyleConstants.NameAttribute,                                 HTML.Tag.CONTENT);                set.removeAttribute(HTML.Attribute.ENDTAG);            }        }    }     /**     * Gets the input attributes used for the styled     * editing actions.     *     * @return the attribute set     */    public MutableAttributeSet getInputAttributes() {

Lines 1–500 of 2288.