4180 lines · 159.8 KB · Java
RawDownload
/* * Copyright (c) 1997, 2010, 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 java.awt.font.TextAttribute;import java.util.*;import java.net.URL;import java.net.MalformedURLException;import java.io.*;import javax.swing.*;import javax.swing.event.*;import javax.swing.text.*;import javax.swing.undo.*;import sun.swing.SwingUtilities2;import static sun.swing.SwingUtilities2.IMPLIED_CR; /** * A document that models HTML.  The purpose of this model is to * support both browsing and editing.  As a result, the structure * described by an HTML document is not exactly replicated by default. * The element structure that is modeled by default, is built by the * class <code>HTMLDocument.HTMLReader</code>, which implements the * <code>HTMLEditorKit.ParserCallback</code> protocol that the parser * expects.  To change the structure one can subclass * <code>HTMLReader</code>, and reimplement the method {@link * #getReader(int)} to return the new reader implementation.  The * documentation for <code>HTMLReader</code> should be consulted for * the details of the default structure created.  The intent is that * the document be non-lossy (although reproducing the HTML format may * result in a different format). * * <p>The document models only HTML, and makes no attempt to store * view attributes in it.  The elements are identified by the * <code>StyleContext.NameAttribute</code> attribute, which should * always have a value of type <code>HTML.Tag</code> that identifies * the kind of element.  Some of the elements (such as comments) are * synthesized.  The <code>HTMLFactory</code> uses this attribute to * determine what kind of view to build.</p> * * <p>This document supports incremental loading.  The * <code>TokenThreshold</code> property controls how much of the parse * is buffered before trying to update the element structure of the * document.  This property is set by the <code>EditorKit</code> so * that subclasses can disable it.</p> * * <p>The <code>Base</code> property determines the URL against which * relative URLs are resolved.  By default, this will be the * <code>Document.StreamDescriptionProperty</code> if the value of the * property is a URL.  If a &lt;BASE&gt; tag is encountered, the base * will become the URL specified by that tag.  Because the base URL is * a property, it can of course be set directly.</p> * * <p>The default content storage mechanism for this document is a gap * buffer (<code>GapContent</code>).  Alternatives can be supplied by * using the constructor that takes a <code>Content</code> * implementation.</p> * * <h2>Modifying HTMLDocument</h2> * * <p>In addition to the methods provided by Document and * StyledDocument for mutating an HTMLDocument, HTMLDocument provides * a number of convenience methods.  The following methods can be used * to insert HTML content into an existing document.</p> * * <ul> *   <li>{@link #setInnerHTML(Element, String)}</li> *   <li>{@link #setOuterHTML(Element, String)}</li> *   <li>{@link #insertBeforeStart(Element, String)}</li> *   <li>{@link #insertAfterStart(Element, String)}</li> *   <li>{@link #insertBeforeEnd(Element, String)}</li> *   <li>{@link #insertAfterEnd(Element, String)}</li> * </ul> * * <p>The following examples illustrate using these methods.  Each * example assumes the HTML document is initialized in the following * way:</p> * * <pre> * JEditorPane p = new JEditorPane(); * p.setContentType("text/html"); * p.setText("..."); // Document text is provided below. * HTMLDocument d = (HTMLDocument) p.getDocument(); * </pre> * * <p>With the following HTML content:</p> * * <pre> * &lt;html> *   &lt;head> *     &lt;title>An example HTMLDocument&lt;/title> *     &lt;style type="text/css"> *       div { background-color: silver; } *       ul { color: red; } *     &lt;/style> *   &lt;/head> *   &lt;body> *     &lt;div id="BOX"> *       &lt;p>Paragraph 1&lt;/p> *       &lt;p>Paragraph 2&lt;/p> *     &lt;/div> *   &lt;/body> * &lt;/html> * </pre> * * <p>All the methods for modifying an HTML document require an {@link * Element}.  Elements can be obtained from an HTML document by using * the method {@link #getElement(Element e, Object attribute, Object * value)}.  It returns the first descendant element that contains the * specified attribute with the given value, in depth-first order. * For example, <code>d.getElement(d.getDefaultRootElement(), * StyleConstants.NameAttribute, HTML.Tag.P)</code> returns the first * paragraph element.</p> * * <p>A convenient shortcut for locating elements is the method {@link * #getElement(String)}; returns an element whose <code>ID</code> * attribute matches the specified value.  For example, * <code>d.getElement("BOX")</code> returns the <code>DIV</code> * element.</p> * * <p>The {@link #getIterator(HTML.Tag t)} method can also be used for * finding all occurrences of the specified HTML tag in the * document.</p> * * <h3>Inserting elements</h3> * * <p>Elements can be inserted before or after the existing children * of any non-leaf element by using the methods * <code>insertAfterStart</code> and <code>insertBeforeEnd</code>. * For example, if <code>e</code> is the <code>DIV</code> element, * <code>d.insertAfterStart(e, "&lt;ul>&lt;li>List * Item&lt;/li>&lt;/ul>")</code> inserts the list before the first * paragraph, and <code>d.insertBeforeEnd(e, "&lt;ul>&lt;li>List * Item&lt;/li>&lt;/ul>")</code> inserts the list after the last * paragraph.  The <code>DIV</code> block becomes the parent of the * newly inserted elements.</p> * * <p>Sibling elements can be inserted before or after any element by * using the methods <code>insertBeforeStart</code> and * <code>insertAfterEnd</code>.  For example, if <code>e</code> is the * <code>DIV</code> element, <code>d.insertBeforeStart(e, * "&lt;ul>&lt;li>List Item&lt;/li>&lt;/ul>")</code> inserts the list * before the <code>DIV</code> element, and <code>d.insertAfterEnd(e, * "&lt;ul>&lt;li>List Item&lt;/li>&lt;/ul>")</code> inserts the list * after the <code>DIV</code> element.  The newly inserted elements * become siblings of the <code>DIV</code> element.</p> * * <h3>Replacing elements</h3> * * <p>Elements and all their descendants can be replaced by using the * methods <code>setInnerHTML</code> and <code>setOuterHTML</code>. * For example, if <code>e</code> is the <code>DIV</code> element, * <code>d.setInnerHTML(e, "&lt;ul>&lt;li>List * Item&lt;/li>&lt;/ul>")</code> replaces all children paragraphs with * the list, and <code>d.setOuterHTML(e, "&lt;ul>&lt;li>List * Item&lt;/li>&lt;/ul>")</code> replaces the <code>DIV</code> element * itself.  In latter case the parent of the list is the * <code>BODY</code> element. * * <h3>Summary</h3> * * <p>The following table shows the example document and the results * of various methods described above.</p> * * <table border=1 cellspacing=0> *   <tr> *     <th>Example</th> *     <th><code>insertAfterStart</code></th> *     <th><code>insertBeforeEnd</code></th> *     <th><code>insertBeforeStart</code></th> *     <th><code>insertAfterEnd</code></th> *     <th><code>setInnerHTML</code></th> *     <th><code>setOuterHTML</code></th> *   </tr> *   <tr valign="top"> *     <td nowrap="nowrap"> *       <div style="background-color: silver;"> *         <p>Paragraph 1</p> *         <p>Paragraph 2</p> *       </div> *     </td> * <!--insertAfterStart--> *     <td nowrap="nowrap"> *       <div style="background-color: silver;"> *         <ul style="color: red;"> *           <li>List Item</li> *         </ul> *         <p>Paragraph 1</p> *         <p>Paragraph 2</p> *       </div> *     </td> * <!--insertBeforeEnd--> *     <td nowrap="nowrap"> *       <div style="background-color: silver;"> *         <p>Paragraph 1</p> *         <p>Paragraph 2</p> *         <ul style="color: red;"> *           <li>List Item</li> *         </ul> *       </div> *     </td> * <!--insertBeforeStart--> *     <td nowrap="nowrap"> *       <ul style="color: red;"> *         <li>List Item</li> *       </ul> *       <div style="background-color: silver;"> *         <p>Paragraph 1</p> *         <p>Paragraph 2</p> *       </div> *     </td> * <!--insertAfterEnd--> *     <td nowrap="nowrap"> *       <div style="background-color: silver;"> *         <p>Paragraph 1</p> *         <p>Paragraph 2</p> *       </div> *       <ul style="color: red;"> *         <li>List Item</li> *       </ul> *     </td> * <!--setInnerHTML--> *     <td nowrap="nowrap"> *       <div style="background-color: silver;"> *         <ul style="color: red;"> *           <li>List Item</li> *         </ul> *       </div> *     </td> * <!--setOuterHTML--> *     <td nowrap="nowrap"> *       <ul style="color: red;"> *         <li>List Item</li> *       </ul> *     </td> *   </tr> * </table> * * <p><strong>Warning:</strong> Serialized objects of this class will * not be compatible with future Swing releases. The current * serialization support is appropriate for short term storage or RMI * between applications running the same version of Swing.  As of 1.4, * support for long term storage of all JavaBeans<sup><font * size="-2">TM</font></sup> has been added to the * <code>java.beans</code> package.  Please see {@link * java.beans.XMLEncoder}.</p> * * @author  Timothy Prinzing * @author  Scott Violet * @author  Sunita Mani */public class HTMLDocument extends DefaultStyledDocument {    /**     * Constructs an HTML document using the default buffer size     * and a default <code>StyleSheet</code>.  This is a convenience     * method for the constructor     * <code>HTMLDocument(Content, StyleSheet)</code>.     */    public HTMLDocument() {        this(new GapContent(BUFFER_SIZE_DEFAULT), new StyleSheet());    }     /**     * Constructs an HTML document with the default content     * storage implementation and the specified style/attribute     * storage mechanism.  This is a convenience method for the     * constructor     * <code>HTMLDocument(Content, StyleSheet)</code>.     *     * @param styles  the styles     */    public HTMLDocument(StyleSheet styles) {        this(new GapContent(BUFFER_SIZE_DEFAULT), styles);    }     /**     * Constructs an HTML document with the given content     * storage implementation and the given style/attribute     * storage mechanism.     *     * @param c  the container for the content     * @param styles the styles     */    public HTMLDocument(Content c, StyleSheet styles) {        super(c, styles);    }     /**     * Fetches the reader for the parser to use when loading the document     * with HTML.  This is implemented to return an instance of     * <code>HTMLDocument.HTMLReader</code>.     * Subclasses can reimplement this     * method to change how the document gets structured if desired.     * (For example, to handle custom tags, or structurally represent character     * style elements.)     *     * @param pos the starting position     * @return the reader used by the parser to load the document     */    public HTMLEditorKit.ParserCallback getReader(int pos) {        Object desc = getProperty(Document.StreamDescriptionProperty);        if (desc instanceof URL) {            setBase((URL)desc);        }        HTMLReader reader = new HTMLReader(pos);        return reader;    }     /**     * Returns the reader for the parser to use to load the document     * with HTML.  This is implemented to return an instance of     * <code>HTMLDocument.HTMLReader</code>.     * Subclasses can reimplement this     * method to change how the document gets structured if desired.     * (For example, to handle custom tags, or structurally represent character     * style elements.)     * <p>This is a convenience method for     * <code>getReader(int, int, int, HTML.Tag, TRUE)</code>.     *     * @param popDepth   the number of <code>ElementSpec.EndTagTypes</code>     *          to generate before inserting     * @param pushDepth  the number of <code>ElementSpec.StartTagTypes</code>     *          with a direction of <code>ElementSpec.JoinNextDirection</code>     *          that should be generated before inserting,     *          but after the end tags have been generated     * @param insertTag  the first tag to start inserting into document     * @return the reader used by the parser to load the document     */    public HTMLEditorKit.ParserCallback getReader(int pos, int popDepth,                                                  int pushDepth,                                                  HTML.Tag insertTag) {        return getReader(pos, popDepth, pushDepth, insertTag, true);    }     /**     * Fetches the reader for the parser to use to load the document     * with HTML.  This is implemented to return an instance of     * HTMLDocument.HTMLReader.  Subclasses can reimplement this     * method to change how the document get structured if desired     * (e.g. to handle custom tags, structurally represent character     * style elements, etc.).     *     * @param popDepth   the number of <code>ElementSpec.EndTagTypes</code>     *          to generate before inserting     * @param pushDepth  the number of <code>ElementSpec.StartTagTypes</code>     *          with a direction of <code>ElementSpec.JoinNextDirection</code>     *          that should be generated before inserting,     *          but after the end tags have been generated     * @param insertTag  the first tag to start inserting into document     * @param insertInsertTag  false if all the Elements after insertTag should     *        be inserted; otherwise insertTag will be inserted     * @return the reader used by the parser to load the document     */    HTMLEditorKit.ParserCallback getReader(int pos, int popDepth,                                           int pushDepth,                                           HTML.Tag insertTag,                                           boolean insertInsertTag) {        Object desc = getProperty(Document.StreamDescriptionProperty);        if (desc instanceof URL) {            setBase((URL)desc);        }        HTMLReader reader = new HTMLReader(pos, popDepth, pushDepth,                                           insertTag, insertInsertTag, false,                                           true);        return reader;    }     /**     * Returns the location to resolve relative URLs against.  By     * default this will be the document's URL if the document     * was loaded from a URL.  If a base tag is found and     * can be parsed, it will be used as the base location.     *     * @return the base location     */    public URL getBase() {        return base;    }     /**     * Sets the location to resolve relative URLs against.  By     * default this will be the document's URL if the document     * was loaded from a URL.  If a base tag is found and     * can be parsed, it will be used as the base location.     * <p>This also sets the base of the <code>StyleSheet</code>     * to be <code>u</code> as well as the base of the document.     *     * @param u  the desired base URL     */    public void setBase(URL u) {        base = u;        getStyleSheet().setBase(u);    }     /**     * Inserts new elements in bulk.  This is how elements get created     * in the document.  The parsing determines what structure is needed     * and creates the specification as a set of tokens that describe the     * edit while leaving the document free of a write-lock.  This method     * can then be called in bursts by the reader to acquire a write-lock     * for a shorter duration (i.e. while the document is actually being     * altered).     *     * @param offset the starting offset     * @param data the element data     * @exception BadLocationException  if the given position does not     *   represent a valid location in the associated document.     */    protected void insert(int offset, ElementSpec[] data) throws BadLocationException {        super.insert(offset, data);    }     /**     * Updates document structure as a result of text insertion.  This     * will happen within a write lock.  This implementation simply     * parses the inserted content for line breaks and builds up a set     * of instructions for the element buffer.     *     * @param chng a description of the document change     * @param attr the attributes     */    protected void insertUpdate(DefaultDocumentEvent chng, AttributeSet attr) {        if(attr == null) {            attr = contentAttributeSet;        }         // If this is the composed text element, merge the content attribute to it        else if (attr.isDefined(StyleConstants.ComposedTextAttribute)) {            ((MutableAttributeSet)attr).addAttributes(contentAttributeSet);        }         if (attr.isDefined(IMPLIED_CR)) {            ((MutableAttributeSet)attr).removeAttribute(IMPLIED_CR);        }         super.insertUpdate(chng, attr);    }     /**     * Replaces the contents of the document with the given     * element specifications.  This is called before insert if     * the loading is done in bursts.  This is the only method called     * if loading the document entirely in one burst.     *     * @param data  the new contents of the document     */    protected void create(ElementSpec[] data) {        super.create(data);    }     /**     * Sets attributes for a paragraph.     * <p>     * This method is thread safe, although most Swing methods     * are not. Please see     * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How     * to Use Threads</A> for more information.     *     * @param offset the offset into the paragraph (must be at least 0)     * @param length the number of characters affected (must be at least 0)     * @param s the attributes     * @param replace whether to replace existing attributes, or merge them     */    public void setParagraphAttributes(int offset, int length, AttributeSet s,                                       boolean replace) {        try {            writeLock();            // Make sure we send out a change for the length of the paragraph.            int end = Math.min(offset + length, getLength());            Element e = getParagraphElement(offset);            offset = e.getStartOffset();            e = getParagraphElement(end);            length = Math.max(0, e.getEndOffset() - offset);            DefaultDocumentEvent changes =                new DefaultDocumentEvent(offset, length,                                         DocumentEvent.EventType.CHANGE);            AttributeSet sCopy = s.copyAttributes();            int lastEnd = Integer.MAX_VALUE;            for (int pos = offset; pos <= end; pos = lastEnd) {

Lines 1–500 of 4180.