/* * 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 <BASE> 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> * <html> * <head> * <title>An example HTMLDocument</title> * <style type="text/css"> * div { background-color: silver; } * ul { color: red; } * </style> * </head> * <body> * <div id="BOX"> * <p>Paragraph 1</p> * <p>Paragraph 2</p> * </div> * </body> * </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, "<ul><li>List * Item</li></ul>")</code> inserts the list before the first * paragraph, and <code>d.insertBeforeEnd(e, "<ul><li>List * Item</li></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, * "<ul><li>List Item</li></ul>")</code> inserts the list * before the <code>DIV</code> element, and <code>d.insertAfterEnd(e, * "<ul><li>List Item</li></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, "<ul><li>List * Item</li></ul>")</code> replaces all children paragraphs with * the list, and <code>d.setOuterHTML(e, "<ul><li>List * Item</li></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. Load more
/* * 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 <BASE> 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> * <html> * <head> * <title>An example HTMLDocument</title> * <style type="text/css"> * div { background-color: silver; } * ul { color: red; } * </style> * </head> * <body> * <div id="BOX"> * <p>Paragraph 1</p> * <p>Paragraph 2</p> * </div> * </body> * </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, "<ul><li>List * Item</li></ul>")</code> inserts the list before the first * paragraph, and <code>d.insertBeforeEnd(e, "<ul><li>List * Item</li></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, * "<ul><li>List Item</li></ul>")</code> inserts the list * before the <code>DIV</code> element, and <code>d.insertAfterEnd(e, * "<ul><li>List Item</li></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, "<ul><li>List * Item</li></ul>")</code> replaces all children paragraphs with * the list, and <code>d.setOuterHTML(e, "<ul><li>List * Item</li></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) {