/*
 * Copyright (c) 1997, 2017, 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 HTMLDocument.HTMLReader, which implements the HTMLEditorKit.ParserCallback protocol that the parser expects. To change the structure one can subclass HTMLReader, and reimplement the method getReader(int) to return the new reader implementation. The documentation for HTMLReader 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).

The document models only HTML, and makes no attempt to store view attributes in it. The elements are identified by the StyleContext.NameAttribute attribute, which should always have a value of type HTML.Tag that identifies the kind of element. Some of the elements (such as comments) are synthesized. The HTMLFactory uses this attribute to determine what kind of view to build.

This document supports incremental loading. The TokenThreshold 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 EditorKit so that subclasses can disable it.

The Base property determines the URL against which relative URLs are resolved. By default, this will be the Document.StreamDescriptionProperty 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.

The default content storage mechanism for this document is a gap buffer (GapContent). Alternatives can be supplied by using the constructor that takes a Content implementation.

Modifying HTMLDocument

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.

The following examples illustrate using these methods. Each example assumes the HTML document is initialized in the following way:

JEditorPane p = new JEditorPane();
p.setContentType("text/html");
p.setText("..."); // Document text is provided below.
HTMLDocument d = (HTMLDocument) p.getDocument();

With the following HTML content:

<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>

All the methods for modifying an HTML document require an Element. Elements can be obtained from an HTML document by using the method 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, d.getElement(d.getDefaultRootElement(), StyleConstants.NameAttribute, HTML.Tag.P) returns the first paragraph element.

A convenient shortcut for locating elements is the method getElement(String); returns an element whose ID attribute matches the specified value. For example, d.getElement("BOX") returns the DIV element.

The getIterator(Tag t) method can also be used for finding all occurrences of the specified HTML tag in the document.

Inserting elements

Elements can be inserted before or after the existing children of any non-leaf element by using the methods insertAfterStart and insertBeforeEnd. For example, if e is the DIV element, d.insertAfterStart(e, "<ul><li>List Item</li></ul>") inserts the list before the first paragraph, and d.insertBeforeEnd(e, "<ul><li>List Item</li></ul>") inserts the list after the last paragraph. The DIV block becomes the parent of the newly inserted elements.

Sibling elements can be inserted before or after any element by using the methods insertBeforeStart and insertAfterEnd. For example, if e is the DIV element, d.insertBeforeStart(e, "<ul><li>List Item</li></ul>") inserts the list before the DIV element, and d.insertAfterEnd(e, "<ul><li>List Item</li></ul>") inserts the list after the DIV element. The newly inserted elements become siblings of the DIV element.

Replacing elements

Elements and all their descendants can be replaced by using the methods setInnerHTML and setOuterHTML. For example, if e is the DIV element, d.setInnerHTML(e, "<ul><li>List Item</li></ul>") replaces all children paragraphs with the list, and d.setOuterHTML(e, "<ul><li>List Item</li></ul>") replaces the DIV element itself. In latter case the parent of the list is the BODY element.

Summary

The following table shows the example document and the results of various methods described above.

HTML Content of example above
Example insertAfterStart insertBeforeEnd insertBeforeStart insertAfterEnd setInnerHTML setOuterHTML

Paragraph 1

Paragraph 2

  • List Item

Paragraph 1

Paragraph 2

Paragraph 1

Paragraph 2

  • List Item
  • List Item

Paragraph 1

Paragraph 2

Paragraph 1

Paragraph 2

  • List Item
  • List Item
  • List Item

Warning: 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™ has been added to the java.beans package. Please see XMLEncoder.

Author: Timothy Prinzing, Scott Violet, Sunita Mani
/** * 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&gt; * &lt;head&gt; * &lt;title&gt;An example HTMLDocument&lt;/title&gt; * &lt;style type="text/css"&gt; * div { background-color: silver; } * ul { color: red; } * &lt;/style&gt; * &lt;/head&gt; * &lt;body&gt; * &lt;div id="BOX"&gt; * &lt;p&gt;Paragraph 1&lt;/p&gt; * &lt;p&gt;Paragraph 2&lt;/p&gt; * &lt;/div&gt; * &lt;/body&gt; * &lt;/html&gt; * </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&gt;&lt;li&gt;List * Item&lt;/li&gt;&lt;/ul&gt;")</code> inserts the list before the first * paragraph, and <code>d.insertBeforeEnd(e, "&lt;ul&gt;&lt;li&gt;List * Item&lt;/li&gt;&lt;/ul&gt;")</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&gt;&lt;li&gt;List Item&lt;/li&gt;&lt;/ul&gt;")</code> inserts the list * before the <code>DIV</code> element, and <code>d.insertAfterEnd(e, * "&lt;ul&gt;&lt;li&gt;List Item&lt;/li&gt;&lt;/ul&gt;")</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&gt;&lt;li&gt;List * Item&lt;/li&gt;&lt;/ul&gt;")</code> replaces all children paragraphs with * the list, and <code>d.setOuterHTML(e, "&lt;ul&gt;&lt;li&gt;List * Item&lt;/li&gt;&lt;/ul&gt;")</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 class="plain"> * <caption>HTML Content of example above</caption> * <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 style="white-space:nowrap"> * <div style="background-color: silver;"> * <p>Paragraph 1</p> * <p>Paragraph 2</p> * </div> * </td> * <!--insertAfterStart--> * <td style="white-space: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 style="white-space: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 style="white-space: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 style="white-space: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 style="white-space:nowrap"> * <div style="background-color: silver;"> * <ul style="color: red;"> * <li>List Item</li> * </ul> * </div> * </td> * <!--setOuterHTML--> * <td style="white-space: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&trade; * 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 */
@SuppressWarnings("serial") // Same-version serialization only public class HTMLDocument extends DefaultStyledDocument {
Constructs an HTML document using the default buffer size and a default StyleSheet. This is a convenience method for the constructor HTMLDocument(Content, StyleSheet).
/** * 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 HTMLDocument(Content, StyleSheet).
Params:
  • styles – the styles
/** * 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.
Params:
  • c – the container for the content
  • styles – the 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 HTMLDocument.HTMLReader. 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.)
Params:
  • pos – the starting position
Returns:the reader used by the parser to load the document
/** * 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 HTMLDocument.HTMLReader. 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.)

This is a convenience method for getReader(int, int, int, HTML.Tag, TRUE).

Params:
  • pos – the starting position
  • popDepth – the number of ElementSpec.EndTagTypes to generate before inserting
  • 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
  • insertTag – the first tag to start inserting into document
Returns:the reader used by the parser to load the document
/** * 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 pos the starting position * @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.).
Params:
  • popDepth – the number of ElementSpec.EndTagTypes to generate before inserting
  • 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
  • insertTag – the first tag to start inserting into document
  • insertInsertTag – false if all the Elements after insertTag should be inserted; otherwise insertTag will be inserted
Returns:the reader used by the parser to load the document
/** * 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.
Returns:the base location
/** * 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.

This also sets the base of the StyleSheet to be u as well as the base of the document.

Params:
  • u – the desired base URL
/** * 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).
Params:
  • offset – the starting offset
  • data – the element data
Throws:
  • BadLocationException – if the given position does not represent a valid location in the associated document.
/** * 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.
Params:
  • chng – a description of the document change
  • attr – the attributes
/** * 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.
Params:
  • data – the new contents of the document
/** * 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.

This method is thread safe, although most Swing methods are not. Please see Concurrency in Swing for more information.

Params:
  • offset – the offset into the paragraph (must be at least 0)
  • length – the number of characters affected (must be at least 0)
  • s – the attributes
  • replace – whether to replace existing attributes, or merge them
/** * Sets attributes for a paragraph. * <p> * This method is thread safe, although most Swing methods * are not. Please see * <A HREF="http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html">Concurrency * in Swing</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) { Element paragraph = getParagraphElement(pos); if (lastEnd == paragraph.getEndOffset()) { lastEnd++; } else { lastEnd = paragraph.getEndOffset(); } MutableAttributeSet attr = (MutableAttributeSet) paragraph.getAttributes(); changes.addEdit(new AttributeUndoableEdit(paragraph, sCopy, replace)); if (replace) { attr.removeAttributes(attr); } attr.addAttributes(s); } changes.end(); fireChangedUpdate(changes); fireUndoableEditUpdate(new UndoableEditEvent(this, changes)); } finally { writeUnlock(); } }
Fetches the StyleSheet with the document-specific display rules (CSS) that were specified in the HTML document itself.
Returns:the StyleSheet
/** * Fetches the <code>StyleSheet</code> with the document-specific display * rules (CSS) that were specified in the HTML document itself. * * @return the <code>StyleSheet</code> */
public StyleSheet getStyleSheet() { return (StyleSheet) getAttributeContext(); }
Fetches an iterator for the specified HTML tag. This can be used for things like iterating over the set of anchors contained, or iterating over the input elements.
Params:
  • t – the requested HTML.Tag
See Also:
Returns:the Iterator for the given HTML tag
/** * Fetches an iterator for the specified HTML tag. * This can be used for things like iterating over the * set of anchors contained, or iterating over the input * elements. * * @param t the requested <code>HTML.Tag</code> * @return the <code>Iterator</code> for the given HTML tag * @see javax.swing.text.html.HTML.Tag */
public Iterator getIterator(HTML.Tag t) { if (t.isBlock()) { // TBD return null; } return new LeafIterator(t, this); }
Creates a document leaf element that directly represents text (doesn't have any children). This is implemented to return an element of type HTMLDocument.RunElement.
Params:
  • parent – the parent element
  • a – the attributes for the element
  • p0 – the beginning of the range (must be at least 0)
  • p1 – the end of the range (must be at least p0)
Returns:the new element
/** * Creates a document leaf element that directly represents * text (doesn't have any children). This is implemented * to return an element of type * <code>HTMLDocument.RunElement</code>. * * @param parent the parent element * @param a the attributes for the element * @param p0 the beginning of the range (must be at least 0) * @param p1 the end of the range (must be at least p0) * @return the new element */
protected Element createLeafElement(Element parent, AttributeSet a, int p0, int p1) { return new RunElement(parent, a, p0, p1); }
Creates a document branch element, that can contain other elements. This is implemented to return an element of type HTMLDocument.BlockElement.
Params:
  • parent – the parent element
  • a – the attributes
Returns:the element
/** * Creates a document branch element, that can contain other elements. * This is implemented to return an element of type * <code>HTMLDocument.BlockElement</code>. * * @param parent the parent element * @param a the attributes * @return the element */
protected Element createBranchElement(Element parent, AttributeSet a) { return new BlockElement(parent, a); }
Creates the root element to be used to represent the default document structure.
Returns:the element base
/** * Creates the root element to be used to represent the * default document structure. * * @return the element base */
protected AbstractElement createDefaultRoot() { // grabs a write-lock for this initialization and // abandon it during initialization so in normal // operation we can detect an illegitimate attempt // to mutate attributes. writeLock(); MutableAttributeSet a = new SimpleAttributeSet(); a.addAttribute(StyleConstants.NameAttribute, HTML.Tag.HTML); BlockElement html = new BlockElement(null, a.copyAttributes()); a.removeAttributes(a); a.addAttribute(StyleConstants.NameAttribute, HTML.Tag.BODY); BlockElement body = new BlockElement(html, a.copyAttributes()); a.removeAttributes(a); a.addAttribute(StyleConstants.NameAttribute, HTML.Tag.P); getStyleSheet().addCSSAttributeFromHTML(a, CSS.Attribute.MARGIN_TOP, "0"); BlockElement paragraph = new BlockElement(body, a.copyAttributes()); a.removeAttributes(a); a.addAttribute(StyleConstants.NameAttribute, HTML.Tag.CONTENT); RunElement brk = new RunElement(paragraph, a, 0, 1); Element[] buff = new Element[1]; buff[0] = brk; paragraph.replace(0, 0, buff); buff[0] = paragraph; body.replace(0, 0, buff); buff[0] = body; html.replace(0, 0, buff); writeUnlock(); return html; }
Sets the number of tokens to buffer before trying to update the documents element structure.
Params:
  • n – the number of tokens to buffer
/** * Sets the number of tokens to buffer before trying to update * the documents element structure. * * @param n the number of tokens to buffer */
public void setTokenThreshold(int n) { putProperty(TokenThreshold, n); }
Gets the number of tokens to buffer before trying to update the documents element structure. The default value is Integer.MAX_VALUE.
Returns:the number of tokens to buffer
/** * Gets the number of tokens to buffer before trying to update * the documents element structure. The default value is * <code>Integer.MAX_VALUE</code>. * * @return the number of tokens to buffer */
public int getTokenThreshold() { Integer i = (Integer) getProperty(TokenThreshold); if (i != null) { return i.intValue(); } return Integer.MAX_VALUE; }
Determines how unknown tags are handled by the parser. If set to true, unknown tags are put in the model, otherwise they are dropped.
Params:
  • preservesTags – true if unknown tags should be saved in the model, otherwise tags are dropped
See Also:
/** * Determines how unknown tags are handled by the parser. * If set to true, unknown * tags are put in the model, otherwise they are dropped. * * @param preservesTags true if unknown tags should be * saved in the model, otherwise tags are dropped * @see javax.swing.text.html.HTML.Tag */
public void setPreservesUnknownTags(boolean preservesTags) { preservesUnknownTags = preservesTags; }
Returns the behavior the parser observes when encountering unknown tags.
See Also:
  • Tag
Returns:true if unknown tags are to be preserved when parsing
/** * Returns the behavior the parser observes when encountering * unknown tags. * * @see javax.swing.text.html.HTML.Tag * @return true if unknown tags are to be preserved when parsing */
public boolean getPreservesUnknownTags() { return preservesUnknownTags; }
Processes HyperlinkEvents that are generated by documents in an HTML frame. The HyperlinkEvent type, as the parameter suggests, is HTMLFrameHyperlinkEvent. In addition to the typical information contained in a HyperlinkEvent, this event contains the element that corresponds to the frame in which the click happened (the source element) and the target name. The target name has 4 possible values:
  • _self
  • _parent
  • _top
  • a named frame
If target is _self, the action is to change the value of the HTML.Attribute.SRC attribute and fires a ChangedUpdate event.

If the target is _parent, then it deletes the parent element, which is a <FRAMESET> element, and inserts a new <FRAME> element, and sets its HTML.Attribute.SRC attribute to have a value equal to the destination URL and fire a RemovedUpdate and InsertUpdate.

If the target is _top, this method does nothing. In the implementation of the view for a frame, namely the FrameView, the processing of _top is handled. Given that _top implies replacing the entire document, it made sense to handle this outside of the document that it will replace.

If the target is a named frame, then the element hierarchy is searched for an element with a name equal to the target, its HTML.Attribute.SRC attribute is updated and a ChangedUpdate event is fired.

Params:
  • e – the event
/** * Processes <code>HyperlinkEvents</code> that * are generated by documents in an HTML frame. * The <code>HyperlinkEvent</code> type, as the parameter suggests, * is <code>HTMLFrameHyperlinkEvent</code>. * In addition to the typical information contained in a * <code>HyperlinkEvent</code>, * this event contains the element that corresponds to the frame in * which the click happened (the source element) and the * target name. The target name has 4 possible values: * <ul> * <li> _self * <li> _parent * <li> _top * <li> a named frame * </ul> * * If target is _self, the action is to change the value of the * <code>HTML.Attribute.SRC</code> attribute and fires a * <code>ChangedUpdate</code> event. *<p> * If the target is _parent, then it deletes the parent element, * which is a &lt;FRAMESET&gt; element, and inserts a new &lt;FRAME&gt; * element, and sets its <code>HTML.Attribute.SRC</code> attribute * to have a value equal to the destination URL and fire a * <code>RemovedUpdate</code> and <code>InsertUpdate</code>. *<p> * If the target is _top, this method does nothing. In the implementation * of the view for a frame, namely the <code>FrameView</code>, * the processing of _top is handled. Given that _top implies * replacing the entire document, it made sense to handle this outside * of the document that it will replace. *<p> * If the target is a named frame, then the element hierarchy is searched * for an element with a name equal to the target, its * <code>HTML.Attribute.SRC</code> attribute is updated and a * <code>ChangedUpdate</code> event is fired. * * @param e the event */
public void processHTMLFrameHyperlinkEvent(HTMLFrameHyperlinkEvent e) { String frameName = e.getTarget(); Element element = e.getSourceElement(); String urlStr = e.getURL().toString(); if (frameName.equals("_self")) { /* The source and destination elements are the same. */ updateFrame(element, urlStr); } else if (frameName.equals("_parent")) { /* The destination is the parent of the frame. */ updateFrameSet(element.getParentElement(), urlStr); } else { /* locate a named frame */ Element targetElement = findFrame(frameName); if (targetElement != null) { updateFrame(targetElement, urlStr); } } }
Searches the element hierarchy for an FRAME element that has its name attribute equal to the frameName.
Params:
  • frameName –
Returns:the element whose NAME attribute has a value of frameName; returns null if not found
/** * Searches the element hierarchy for an FRAME element * that has its name attribute equal to the <code>frameName</code>. * * @param frameName * @return the element whose NAME attribute has a value of * <code>frameName</code>; returns <code>null</code> * if not found */
private Element findFrame(String frameName) { ElementIterator it = new ElementIterator(this); Element next; while ((next = it.next()) != null) { AttributeSet attr = next.getAttributes(); if (matchNameAttribute(attr, HTML.Tag.FRAME)) { String frameTarget = (String)attr.getAttribute(HTML.Attribute.NAME); if (frameTarget != null && frameTarget.equals(frameName)) { break; } } } return next; }
Returns true if StyleConstants.NameAttribute is equal to the tag that is passed in as a parameter.
Params:
  • attr – the attributes to be matched
  • tag – the value to be matched
See Also:
Returns:true if there is a match, false otherwise
/** * Returns true if <code>StyleConstants.NameAttribute</code> is * equal to the tag that is passed in as a parameter. * * @param attr the attributes to be matched * @param tag the value to be matched * @return true if there is a match, false otherwise * @see javax.swing.text.html.HTML.Attribute */
static boolean matchNameAttribute(AttributeSet attr, HTML.Tag tag) { Object o = attr.getAttribute(StyleConstants.NameAttribute); if (o instanceof HTML.Tag) { HTML.Tag name = (HTML.Tag) o; if (name == tag) { return true; } } return false; }
Replaces a frameset branch Element with a frame leaf element.
Params:
  • element – the frameset element to remove
  • url – the value for the SRC attribute for the new frame that will replace the frameset
/** * Replaces a frameset branch Element with a frame leaf element. * * @param element the frameset element to remove * @param url the value for the SRC attribute for the * new frame that will replace the frameset */
private void updateFrameSet(Element element, String url) { try { int startOffset = element.getStartOffset(); int endOffset = Math.min(getLength(), element.getEndOffset()); String html = "<frame"; if (url != null) { html += " src=\"" + url + "\""; } html += ">"; installParserIfNecessary(); setOuterHTML(element, html); } catch (BadLocationException e1) { // Should handle this better } catch (IOException ioe) { // Should handle this better } }
Updates the Frame elements HTML.Attribute.SRC attribute and fires a ChangedUpdate event.
Params:
  • element – a FRAME element whose SRC attribute will be updated
  • url – a string specifying the new value for the SRC attribute
/** * Updates the Frame elements <code>HTML.Attribute.SRC attribute</code> * and fires a <code>ChangedUpdate</code> event. * * @param element a FRAME element whose SRC attribute will be updated * @param url a string specifying the new value for the SRC attribute */
private void updateFrame(Element element, String url) { try { writeLock(); DefaultDocumentEvent changes = new DefaultDocumentEvent(element.getStartOffset(), 1, DocumentEvent.EventType.CHANGE); AttributeSet sCopy = element.getAttributes().copyAttributes(); MutableAttributeSet attr = (MutableAttributeSet) element.getAttributes(); changes.addEdit(new AttributeUndoableEdit(element, sCopy, false)); attr.removeAttribute(HTML.Attribute.SRC); attr.addAttribute(HTML.Attribute.SRC, url); changes.end(); fireChangedUpdate(changes); fireUndoableEditUpdate(new UndoableEditEvent(this, changes)); } finally { writeUnlock(); } }
Returns true if the document will be viewed in a frame.
Returns:true if document will be viewed in a frame, otherwise false
/** * Returns true if the document will be viewed in a frame. * @return true if document will be viewed in a frame, otherwise false */
boolean isFrameDocument() { return frameDocument; }
Sets a boolean state about whether the document will be viewed in a frame.
Params:
  • frameDoc – true if the document will be viewed in a frame, otherwise false
/** * Sets a boolean state about whether the document will be * viewed in a frame. * @param frameDoc true if the document will be viewed in a frame, * otherwise false */
void setFrameDocumentState(boolean frameDoc) { this.frameDocument = frameDoc; }
Adds the specified map, this will remove a Map that has been previously registered with the same name.
Params:
  • map – the Map to be registered
/** * Adds the specified map, this will remove a Map that has been * previously registered with the same name. * * @param map the <code>Map</code> to be registered */
void addMap(Map map) { String name = map.getName(); if (name != null) { Object maps = getProperty(MAP_PROPERTY); if (maps == null) { maps = new Hashtable<>(11); putProperty(MAP_PROPERTY, maps); } if (maps instanceof Hashtable) { @SuppressWarnings("unchecked") Hashtable<Object, Object> tmp = (Hashtable)maps; tmp.put("#" + name, map); } } }
Removes a previously registered map.
Params:
  • map – the Map to be removed
/** * Removes a previously registered map. * @param map the <code>Map</code> to be removed */
void removeMap(Map map) { String name = map.getName(); if (name != null) { Object maps = getProperty(MAP_PROPERTY); if (maps instanceof Hashtable) { ((Hashtable)maps).remove("#" + name); } } }
Returns the Map associated with the given name.
Params:
  • name – the name of the desired Map
Returns:the Map or null if it can't be found, or if name is null
/** * Returns the Map associated with the given name. * @param name the name of the desired <code>Map</code> * @return the <code>Map</code> or <code>null</code> if it can't * be found, or if <code>name</code> is <code>null</code> */
Map getMap(String name) { if (name != null) { Object maps = getProperty(MAP_PROPERTY); if (maps != null && (maps instanceof Hashtable)) { return (Map)((Hashtable)maps).get(name); } } return null; }
Returns an Enumeration of the possible Maps.
Returns:the enumerated list of maps, or null if the maps are not an instance of Hashtable
/** * Returns an <code>Enumeration</code> of the possible Maps. * @return the enumerated list of maps, or <code>null</code> * if the maps are not an instance of <code>Hashtable</code> */
Enumeration<Object> getMaps() { Object maps = getProperty(MAP_PROPERTY); if (maps instanceof Hashtable) { @SuppressWarnings("unchecked") Hashtable<Object, Object> tmp = (Hashtable) maps; return tmp.elements(); } return null; }
Sets the content type language used for style sheets that do not explicitly specify the type. The default is text/css.
Params:
  • contentType – the content type language for the style sheets
/** * Sets the content type language used for style sheets that do not * explicitly specify the type. The default is text/css. * @param contentType the content type language for the style sheets */
/* public */ void setDefaultStyleSheetType(String contentType) { putProperty(StyleType, contentType); }
Returns the content type language used for style sheets. The default is text/css.
Returns:the content type language used for the style sheets
/** * Returns the content type language used for style sheets. The default * is text/css. * @return the content type language used for the style sheets */
/* public */ String getDefaultStyleSheetType() { String retValue = (String)getProperty(StyleType); if (retValue == null) { return "text/css"; } return retValue; }
Sets the parser that is used by the methods that insert html into the existing document, such as setInnerHTML, and setOuterHTML.

HTMLEditorKit.createDefaultDocument will set the parser for you. If you create an HTMLDocument by hand, be sure and set the parser accordingly.

Params:
  • parser – the parser to be used for text insertion
Since:1.3
/** * Sets the parser that is used by the methods that insert html * into the existing document, such as <code>setInnerHTML</code>, * and <code>setOuterHTML</code>. * <p> * <code>HTMLEditorKit.createDefaultDocument</code> will set the parser * for you. If you create an <code>HTMLDocument</code> by hand, * be sure and set the parser accordingly. * @param parser the parser to be used for text insertion * * @since 1.3 */
public void setParser(HTMLEditorKit.Parser parser) { this.parser = parser; putProperty("__PARSER__", null); }
Returns the parser that is used when inserting HTML into the existing document.
Returns:the parser used for text insertion
Since:1.3
/** * Returns the parser that is used when inserting HTML into the existing * document. * @return the parser used for text insertion * * @since 1.3 */
public HTMLEditorKit.Parser getParser() { Object p = getProperty("__PARSER__"); if (p instanceof HTMLEditorKit.Parser) { return (HTMLEditorKit.Parser)p; } return parser; }
Replaces the children of the given element with the contents specified as an HTML string.

This will be seen as at least two events, n inserts followed by a remove.

Consider the following structure (the elem parameter is in bold).

    <body>
      |
    <div>
     /  \
   <p>   <p>

Invoking setInnerHTML(elem, "<ul><li>") results in the following structure (new elements are in red).

    <body>
      |
    <div>
        \
        <ul>
          \
          <li>

Parameter elem must not be a leaf element, otherwise an IllegalArgumentException is thrown. If either elem or htmlText parameter is null, no changes are made to the document.

For this to work correctly, the document must have an HTMLEditorKit.Parser set. This will be the case if the document was created from an HTMLEditorKit via the createDefaultDocument method.

Params:
  • elem – the branch element whose children will be replaced
  • htmlText – the string to be parsed and assigned to elem
Throws:
Since:1.3
/** * Replaces the children of the given element with the contents * specified as an HTML string. * * <p>This will be seen as at least two events, n inserts followed by * a remove.</p> * * <p>Consider the following structure (the <code>elem</code> * parameter is <b>in bold</b>).</p> * * <pre> * &lt;body&gt; * | * <b>&lt;div&gt;</b> * / \ * &lt;p&gt; &lt;p&gt; * </pre> * * <p>Invoking <code>setInnerHTML(elem, "&lt;ul&gt;&lt;li&gt;")</code> * results in the following structure (new elements are <span * style="color: red;">in red</span>).</p> * * <pre> * &lt;body&gt; * | * <b>&lt;div&gt;</b> * \ * <span style="color: red;">&lt;ul&gt;</span> * \ * <span style="color: red;">&lt;li&gt;</span> * </pre> * * <p>Parameter <code>elem</code> must not be a leaf element, * otherwise an <code>IllegalArgumentException</code> is thrown. * If either <code>elem</code> or <code>htmlText</code> parameter * is <code>null</code>, no changes are made to the document.</p> * * <p>For this to work correctly, the document must have an * <code>HTMLEditorKit.Parser</code> set. This will be the case * if the document was created from an HTMLEditorKit via the * <code>createDefaultDocument</code> method.</p> * * @param elem the branch element whose children will be replaced * @param htmlText the string to be parsed and assigned to <code>elem</code> * @throws IllegalArgumentException if <code>elem</code> is a leaf * @throws IllegalStateException if an <code>HTMLEditorKit.Parser</code> * has not been defined * @throws BadLocationException if replacement is impossible because of * a structural issue * @throws IOException if an I/O exception occurs * @since 1.3 */
public void setInnerHTML(Element elem, String htmlText) throws BadLocationException, IOException { verifyParser(); if (elem != null && elem.isLeaf()) { throw new IllegalArgumentException ("Can not set inner HTML of a leaf"); } if (elem != null && htmlText != null) { int oldCount = elem.getElementCount(); int insertPosition = elem.getStartOffset(); insertHTML(elem, elem.getStartOffset(), htmlText, true); if (elem.getElementCount() > oldCount) { // Elements were inserted, do the cleanup. removeElements(elem, elem.getElementCount() - oldCount, oldCount); } } }
Replaces the given element in the parent with the contents specified as an HTML string.

This will be seen as at least two events, n inserts followed by a remove.

When replacing a leaf this will attempt to make sure there is a newline present if one is needed. This may result in an additional element being inserted. Consider, if you were to replace a character element that contained a newline with <img> this would create two elements, one for the image, and one for the newline.

If you try to replace the element at length you will most likely end up with two elements, eg setOuterHTML(getCharacterElement (getLength()), "blah") will result in two leaf elements at the end, one representing 'blah', and the other representing the end element.

Consider the following structure (the elem parameter is in bold).

    <body>
      |
    <div>
     /  \
   <p>   <p>

Invoking setOuterHTML(elem, "<ul><li>") results in the following structure (new elements are in red).

   <body>
     |
    <ul>
      \
      <li>

If either elem or htmlText parameter is null, no changes are made to the document.

For this to work correctly, the document must have an HTMLEditorKit.Parser set. This will be the case if the document was created from an HTMLEditorKit via the createDefaultDocument method.

Params:
  • elem – the element to replace
  • htmlText – the string to be parsed and inserted in place of elem
Throws:
Since:1.3
/** * Replaces the given element in the parent with the contents * specified as an HTML string. * * <p>This will be seen as at least two events, n inserts followed by * a remove.</p> * * <p>When replacing a leaf this will attempt to make sure there is * a newline present if one is needed. This may result in an additional * element being inserted. Consider, if you were to replace a character * element that contained a newline with &lt;img&gt; this would create * two elements, one for the image, and one for the newline.</p> * * <p>If you try to replace the element at length you will most * likely end up with two elements, eg * <code>setOuterHTML(getCharacterElement (getLength()), * "blah")</code> will result in two leaf elements at the end, one * representing 'blah', and the other representing the end * element.</p> * * <p>Consider the following structure (the <code>elem</code> * parameter is <b>in bold</b>).</p> * * <pre> * &lt;body&gt; * | * <b>&lt;div&gt;</b> * / \ * &lt;p&gt; &lt;p&gt; * </pre> * * <p>Invoking <code>setOuterHTML(elem, "&lt;ul&gt;&lt;li&gt;")</code> * results in the following structure (new elements are <span * style="color: red;">in red</span>).</p> * * <pre> * &lt;body&gt; * | * <span style="color: red;">&lt;ul&gt;</span> * \ * <span style="color: red;">&lt;li&gt;</span> * </pre> * * <p>If either <code>elem</code> or <code>htmlText</code> * parameter is <code>null</code>, no changes are made to the * document.</p> * * <p>For this to work correctly, the document must have an * HTMLEditorKit.Parser set. This will be the case if the document * was created from an HTMLEditorKit via the * <code>createDefaultDocument</code> method.</p> * * @param elem the element to replace * @param htmlText the string to be parsed and inserted in place of <code>elem</code> * @throws IllegalStateException if an HTMLEditorKit.Parser has not * been set * @throws BadLocationException if replacement is impossible because of * a structural issue * @throws IOException if an I/O exception occurs * @since 1.3 */
public void setOuterHTML(Element elem, String htmlText) throws BadLocationException, IOException { verifyParser(); if (elem != null && elem.getParentElement() != null && htmlText != null) { int start = elem.getStartOffset(); int end = elem.getEndOffset(); int startLength = getLength(); // We don't want a newline if elem is a leaf, and doesn't contain // a newline. boolean wantsNewline = !elem.isLeaf(); if (!wantsNewline && (end > startLength || getText(end - 1, 1).charAt(0) == NEWLINE[0])){ wantsNewline = true; } Element parent = elem.getParentElement(); int oldCount = parent.getElementCount(); insertHTML(parent, start, htmlText, wantsNewline); // Remove old. int newLength = getLength(); if (oldCount != parent.getElementCount()) { int removeIndex = parent.getElementIndex(start + newLength - startLength); removeElements(parent, removeIndex, 1); } } }
Inserts the HTML specified as a string at the start of the element.

Consider the following structure (the elem parameter is in bold).

    <body>
      |
    <div>
     /  \
   <p>   <p>

Invoking insertAfterStart(elem, "<ul><li>") results in the following structure (new elements are in red).

       <body>
         |
       <div>
      /  |  \
   <ul> <p> <p>
    /
 <li>

Unlike the insertBeforeStart method, new elements become children of the specified element, not siblings.

Parameter elem must not be a leaf element, otherwise an IllegalArgumentException is thrown. If either elem or htmlText parameter is null, no changes are made to the document.

For this to work correctly, the document must have an HTMLEditorKit.Parser set. This will be the case if the document was created from an HTMLEditorKit via the createDefaultDocument method.

Params:
  • elem – the branch element to be the root for the new text
  • htmlText – the string to be parsed and assigned to elem
Throws:
Since:1.3
/** * Inserts the HTML specified as a string at the start * of the element. * * <p>Consider the following structure (the <code>elem</code> * parameter is <b>in bold</b>).</p> * * <pre> * &lt;body&gt; * | * <b>&lt;div&gt;</b> * / \ * &lt;p&gt; &lt;p&gt; * </pre> * * <p>Invoking <code>insertAfterStart(elem, * "&lt;ul&gt;&lt;li&gt;")</code> results in the following structure * (new elements are <span style="color: red;">in red</span>).</p> * * <pre> * &lt;body&gt; * | * <b>&lt;div&gt;</b> * / | \ * <span style="color: red;">&lt;ul&gt;</span> &lt;p&gt; &lt;p&gt; * / * <span style="color: red;">&lt;li&gt;</span> * </pre> * * <p>Unlike the <code>insertBeforeStart</code> method, new * elements become <em>children</em> of the specified element, * not siblings.</p> * * <p>Parameter <code>elem</code> must not be a leaf element, * otherwise an <code>IllegalArgumentException</code> is thrown. * If either <code>elem</code> or <code>htmlText</code> parameter * is <code>null</code>, no changes are made to the document.</p> * * <p>For this to work correctly, the document must have an * <code>HTMLEditorKit.Parser</code> set. This will be the case * if the document was created from an HTMLEditorKit via the * <code>createDefaultDocument</code> method.</p> * * @param elem the branch element to be the root for the new text * @param htmlText the string to be parsed and assigned to <code>elem</code> * @throws IllegalArgumentException if <code>elem</code> is a leaf * @throws IllegalStateException if an HTMLEditorKit.Parser has not * been set on the document * @throws BadLocationException if insertion is impossible because of * a structural issue * @throws IOException if an I/O exception occurs * @since 1.3 */
public void insertAfterStart(Element elem, String htmlText) throws BadLocationException, IOException { verifyParser(); if (elem == null || htmlText == null) { return; } if (elem.isLeaf()) { throw new IllegalArgumentException ("Can not insert HTML after start of a leaf"); } insertHTML(elem, elem.getStartOffset(), htmlText, false); }
Inserts the HTML specified as a string at the end of the element.

If elem's children are leaves, and the character at a elem.getEndOffset() - 1 is a newline, this will insert before the newline so that there isn't text after the newline.

Consider the following structure (the elem parameter is in bold).

    <body>
      |
    <div>
     /  \
   <p>   <p>

Invoking insertBeforeEnd(elem, "<ul><li>") results in the following structure (new elements are in red).

       <body>
         |
       <div>
      /  |  \
    <p> <p> <ul>
              \
              <li>

Unlike the insertAfterEnd method, new elements become children of the specified element, not siblings.

Parameter elem must not be a leaf element, otherwise an IllegalArgumentException is thrown. If either elem or htmlText parameter is null, no changes are made to the document.

For this to work correctly, the document must have an HTMLEditorKit.Parser set. This will be the case if the document was created from an HTMLEditorKit via the createDefaultDocument method.

Params:
  • elem – the element to be the root for the new text
  • htmlText – the string to be parsed and assigned to elem
Throws:
Since:1.3
/** * Inserts the HTML specified as a string at the end of * the element. * * <p> If <code>elem</code>'s children are leaves, and the * character at a <code>elem.getEndOffset() - 1</code> is a newline, * this will insert before the newline so that there isn't text after * the newline.</p> * * <p>Consider the following structure (the <code>elem</code> * parameter is <b>in bold</b>).</p> * * <pre> * &lt;body&gt; * | * <b>&lt;div&gt;</b> * / \ * &lt;p&gt; &lt;p&gt; * </pre> * * <p>Invoking <code>insertBeforeEnd(elem, "&lt;ul&gt;&lt;li&gt;")</code> * results in the following structure (new elements are <span * style="color: red;">in red</span>).</p> * * <pre> * &lt;body&gt; * | * <b>&lt;div&gt;</b> * / | \ * &lt;p&gt; &lt;p&gt; <span style="color: red;">&lt;ul&gt;</span> * \ * <span style="color: red;">&lt;li&gt;</span> * </pre> * * <p>Unlike the <code>insertAfterEnd</code> method, new elements * become <em>children</em> of the specified element, not * siblings.</p> * * <p>Parameter <code>elem</code> must not be a leaf element, * otherwise an <code>IllegalArgumentException</code> is thrown. * If either <code>elem</code> or <code>htmlText</code> parameter * is <code>null</code>, no changes are made to the document.</p> * * <p>For this to work correctly, the document must have an * <code>HTMLEditorKit.Parser</code> set. This will be the case * if the document was created from an HTMLEditorKit via the * <code>createDefaultDocument</code> method.</p> * * @param elem the element to be the root for the new text * @param htmlText the string to be parsed and assigned to <code>elem</code> * @throws IllegalArgumentException if <code>elem</code> is a leaf * @throws IllegalStateException if an HTMLEditorKit.Parser has not * been set on the document * @throws BadLocationException if insertion is impossible because of * a structural issue * @throws IOException if an I/O exception occurs * @since 1.3 */
public void insertBeforeEnd(Element elem, String htmlText) throws BadLocationException, IOException { verifyParser(); if (elem != null && elem.isLeaf()) { throw new IllegalArgumentException ("Can not set inner HTML before end of leaf"); } if (elem != null) { int offset = elem.getEndOffset(); if (elem.getElement(elem.getElementIndex(offset - 1)).isLeaf() && getText(offset - 1, 1).charAt(0) == NEWLINE[0]) { offset--; } insertHTML(elem, offset, htmlText, false); } }
Inserts the HTML specified as a string before the start of the given element.

Consider the following structure (the elem parameter is in bold).

    <body>
      |
    <div>
     /  \
   <p>   <p>

Invoking insertBeforeStart(elem, "<ul><li>") results in the following structure (new elements are in red).

       <body>
        /  \
     <ul> <div>
      /    /  \
    <li> <p>  <p>

Unlike the insertAfterStart method, new elements become siblings of the specified element, not children.

If either elem or htmlText parameter is null, no changes are made to the document.

For this to work correctly, the document must have an HTMLEditorKit.Parser set. This will be the case if the document was created from an HTMLEditorKit via the createDefaultDocument method.

Params:
  • elem – the element the content is inserted before
  • htmlText – the string to be parsed and inserted before elem
Throws:
Since:1.3
/** * Inserts the HTML specified as a string before the start of * the given element. * * <p>Consider the following structure (the <code>elem</code> * parameter is <b>in bold</b>).</p> * * <pre> * &lt;body&gt; * | * <b>&lt;div&gt;</b> * / \ * &lt;p&gt; &lt;p&gt; * </pre> * * <p>Invoking <code>insertBeforeStart(elem, * "&lt;ul&gt;&lt;li&gt;")</code> results in the following structure * (new elements are <span style="color: red;">in red</span>).</p> * * <pre> * &lt;body&gt; * / \ * <span style="color: red;">&lt;ul&gt;</span> <b>&lt;div&gt;</b> * / / \ * <span style="color: red;">&lt;li&gt;</span> &lt;p&gt; &lt;p&gt; * </pre> * * <p>Unlike the <code>insertAfterStart</code> method, new * elements become <em>siblings</em> of the specified element, not * children.</p> * * <p>If either <code>elem</code> or <code>htmlText</code> * parameter is <code>null</code>, no changes are made to the * document.</p> * * <p>For this to work correctly, the document must have an * <code>HTMLEditorKit.Parser</code> set. This will be the case * if the document was created from an HTMLEditorKit via the * <code>createDefaultDocument</code> method.</p> * * @param elem the element the content is inserted before * @param htmlText the string to be parsed and inserted before <code>elem</code> * @throws IllegalStateException if an HTMLEditorKit.Parser has not * been set on the document * @throws BadLocationException if insertion is impossible because of * a structural issue * @throws IOException if an I/O exception occurs * @since 1.3 */
public void insertBeforeStart(Element elem, String htmlText) throws BadLocationException, IOException { verifyParser(); if (elem != null) { Element parent = elem.getParentElement(); if (parent != null) { insertHTML(parent, elem.getStartOffset(), htmlText, false); } } }
Inserts the HTML specified as a string after the end of the given element.

Consider the following structure (the elem parameter is in bold).

    <body>
      |
    <div>
     /  \
   <p>   <p>

Invoking insertAfterEnd(elem, "<ul><li>") results in the following structure (new elements are in red).

       <body>
        /  \
     <div> <ul>
      / \    \
    <p> <p>  <li>

Unlike the insertBeforeEnd method, new elements become siblings of the specified element, not children.

If either elem or htmlText parameter is null, no changes are made to the document.

For this to work correctly, the document must have an HTMLEditorKit.Parser set. This will be the case if the document was created from an HTMLEditorKit via the createDefaultDocument method.

Params:
  • elem – the element the content is inserted after
  • htmlText – the string to be parsed and inserted after elem
Throws:
Since:1.3
/** * Inserts the HTML specified as a string after the end of the * given element. * * <p>Consider the following structure (the <code>elem</code> * parameter is <b>in bold</b>).</p> * * <pre> * &lt;body&gt; * | * <b>&lt;div&gt;</b> * / \ * &lt;p&gt; &lt;p&gt; * </pre> * * <p>Invoking <code>insertAfterEnd(elem, "&lt;ul&gt;&lt;li&gt;")</code> * results in the following structure (new elements are <span * style="color: red;">in red</span>).</p> * * <pre> * &lt;body&gt; * / \ * <b>&lt;div&gt;</b> <span style="color: red;">&lt;ul&gt;</span> * / \ \ * &lt;p&gt; &lt;p&gt; <span style="color: red;">&lt;li&gt;</span> * </pre> * * <p>Unlike the <code>insertBeforeEnd</code> method, new elements * become <em>siblings</em> of the specified element, not * children.</p> * * <p>If either <code>elem</code> or <code>htmlText</code> * parameter is <code>null</code>, no changes are made to the * document.</p> * * <p>For this to work correctly, the document must have an * <code>HTMLEditorKit.Parser</code> set. This will be the case * if the document was created from an HTMLEditorKit via the * <code>createDefaultDocument</code> method.</p> * * @param elem the element the content is inserted after * @param htmlText the string to be parsed and inserted after <code>elem</code> * @throws IllegalStateException if an HTMLEditorKit.Parser has not * been set on the document * @throws BadLocationException if insertion is impossible because of * a structural issue * @throws IOException if an I/O exception occurs * @since 1.3 */
public void insertAfterEnd(Element elem, String htmlText) throws BadLocationException, IOException { verifyParser(); if (elem != null) { Element parent = elem.getParentElement(); if (parent != null) { // If we are going to insert the string into the body // section, it is necessary to set the corrsponding flag. if (HTML.Tag.BODY.name.equals(parent.getName())) { insertInBody = true; } int offset = elem.getEndOffset(); if (offset > (getLength() + 1)) { offset--; } else if (elem.isLeaf() && getText(offset - 1, 1). charAt(0) == NEWLINE[0]) { offset--; } insertHTML(parent, offset, htmlText, false); // Cleanup the flag, if any. if (insertInBody) { insertInBody = false; } } } }
Returns the element that has the given id Attribute. If the element can't be found, null is returned. Note that this method works on an Attribute, not a character tag. In the following HTML snippet: <a id="HelloThere"> the attribute is 'id' and the character tag is 'a'. This is a convenience method for getElement(RootElement, HTML.Attribute.id, id). This is not thread-safe.
Params:
  • id – the string representing the desired Attribute
See Also:
Returns:the element with the specified Attribute or null if it can't be found, or null if id is null
Since:1.3
/** * Returns the element that has the given id <code>Attribute</code>. * If the element can't be found, <code>null</code> is returned. * Note that this method works on an <code>Attribute</code>, * <i>not</i> a character tag. In the following HTML snippet: * <code>&lt;a id="HelloThere"&gt;</code> the attribute is * 'id' and the character tag is 'a'. * This is a convenience method for * <code>getElement(RootElement, HTML.Attribute.id, id)</code>. * This is not thread-safe. * * @param id the string representing the desired <code>Attribute</code> * @return the element with the specified <code>Attribute</code> * or <code>null</code> if it can't be found, * or <code>null</code> if <code>id</code> is <code>null</code> * @see javax.swing.text.html.HTML.Attribute * @since 1.3 */
public Element getElement(String id) { if (id == null) { return null; } return getElement(getDefaultRootElement(), HTML.Attribute.ID, id, true); }
Returns the child element of e that contains the attribute, attribute with value value, or null if one isn't found. This is not thread-safe.
Params:
  • e – the root element where the search begins
  • attribute – the desired Attribute
  • value – the values for the specified Attribute
See Also:
Returns:the element with the specified Attribute and the specified value, or null if it can't be found
Since:1.3
/** * Returns the child element of <code>e</code> that contains the * attribute, <code>attribute</code> with value <code>value</code>, or * <code>null</code> if one isn't found. This is not thread-safe. * * @param e the root element where the search begins * @param attribute the desired <code>Attribute</code> * @param value the values for the specified <code>Attribute</code> * @return the element with the specified <code>Attribute</code> * and the specified <code>value</code>, or <code>null</code> * if it can't be found * @see javax.swing.text.html.HTML.Attribute * @since 1.3 */
public Element getElement(Element e, Object attribute, Object value) { return getElement(e, attribute, value, true); }
Returns the child element of e that contains the attribute, attribute with value value, or null if one isn't found. This is not thread-safe.

If searchLeafAttributes is true, and e is a leaf, any attributes that are instances of HTML.Tag with a value that is an AttributeSet will also be checked.

Params:
  • e – the root element where the search begins
  • attribute – the desired Attribute
  • value – the values for the specified Attribute
See Also:
Returns:the element with the specified Attribute and the specified value, or null if it can't be found
/** * Returns the child element of <code>e</code> that contains the * attribute, <code>attribute</code> with value <code>value</code>, or * <code>null</code> if one isn't found. This is not thread-safe. * <p> * If <code>searchLeafAttributes</code> is true, and <code>e</code> is * a leaf, any attributes that are instances of <code>HTML.Tag</code> * with a value that is an <code>AttributeSet</code> will also be checked. * * @param e the root element where the search begins * @param attribute the desired <code>Attribute</code> * @param value the values for the specified <code>Attribute</code> * @return the element with the specified <code>Attribute</code> * and the specified <code>value</code>, or <code>null</code> * if it can't be found * @see javax.swing.text.html.HTML.Attribute */
private Element getElement(Element e, Object attribute, Object value, boolean searchLeafAttributes) { AttributeSet attr = e.getAttributes(); if (attr != null && attr.isDefined(attribute)) { if (value.equals(attr.getAttribute(attribute))) { return e; } } if (!e.isLeaf()) { for (int counter = 0, maxCounter = e.getElementCount(); counter < maxCounter; counter++) { Element retValue = getElement(e.getElement(counter), attribute, value, searchLeafAttributes); if (retValue != null) { return retValue; } } } else if (searchLeafAttributes && attr != null) { // For some leaf elements we store the actual attributes inside // the AttributeSet of the Element (such as anchors). Enumeration<?> names = attr.getAttributeNames(); if (names != null) { while (names.hasMoreElements()) { Object name = names.nextElement(); if ((name instanceof HTML.Tag) && (attr.getAttribute(name) instanceof AttributeSet)) { AttributeSet check = (AttributeSet)attr. getAttribute(name); if (check.isDefined(attribute) && value.equals(check.getAttribute(attribute))) { return e; } } } } } return null; }
Verifies the document has an HTMLEditorKit.Parser set. If getParser returns null, this will throw an IllegalStateException.
Throws:
  • IllegalStateException – if the document does not have a Parser
/** * Verifies the document has an <code>HTMLEditorKit.Parser</code> set. * If <code>getParser</code> returns <code>null</code>, this will throw an * IllegalStateException. * * @throws IllegalStateException if the document does not have a Parser */
private void verifyParser() { if (getParser() == null) { throw new IllegalStateException("No HTMLEditorKit.Parser"); } }
Installs a default Parser if one has not been installed yet.
/** * Installs a default Parser if one has not been installed yet. */
private void installParserIfNecessary() { if (getParser() == null) { setParser(new HTMLEditorKit().getParser()); } }
Inserts a string of HTML into the document at the given position. parent is used to identify the location to insert the html. If parent is a leaf this can have unexpected results.
/** * Inserts a string of HTML into the document at the given position. * <code>parent</code> is used to identify the location to insert the * <code>html</code>. If <code>parent</code> is a leaf this can have * unexpected results. */
private void insertHTML(Element parent, int offset, String html, boolean wantsTrailingNewline) throws BadLocationException, IOException { if (parent != null && html != null) { HTMLEditorKit.Parser parser = getParser(); if (parser != null) { int lastOffset = Math.max(0, offset - 1); Element charElement = getCharacterElement(lastOffset); Element commonParent = parent; int pop = 0; int push = 0; if (parent.getStartOffset() > lastOffset) { while (commonParent != null && commonParent.getStartOffset() > lastOffset) { commonParent = commonParent.getParentElement(); push++; } if (commonParent == null) { throw new BadLocationException("No common parent", offset); } } while (charElement != null && charElement != commonParent) { pop++; charElement = charElement.getParentElement(); } if (charElement != null) { // Found it, do the insert. HTMLReader reader = new HTMLReader(offset, pop - 1, push, null, false, true, wantsTrailingNewline); parser.parse(new StringReader(html), reader, true); reader.flush(); } } } }
Removes child Elements of the passed in Element e. This will do the necessary cleanup to ensure the element representing the end character is correctly created.

This is not a general purpose method, it assumes that e will still have at least one child after the remove, and it assumes the character at e.getStartOffset() - 1 is a newline and is of length 1.

/** * Removes child Elements of the passed in Element <code>e</code>. This * will do the necessary cleanup to ensure the element representing the * end character is correctly created. * <p>This is not a general purpose method, it assumes that <code>e</code> * will still have at least one child after the remove, and it assumes * the character at <code>e.getStartOffset() - 1</code> is a newline and * is of length 1. */
private void removeElements(Element e, int index, int count) throws BadLocationException { writeLock(); try { int start = e.getElement(index).getStartOffset(); int end = e.getElement(index + count - 1).getEndOffset(); if (end > getLength()) { removeElementsAtEnd(e, index, count, start, end); } else { removeElements(e, index, count, start, end); } } finally { writeUnlock(); } }
Called to remove child elements of e when one of the elements to remove is representing the end character.

Since the Content will not allow a removal to the end character this will do a remove from start - 1 to end. The end Element(s) will be removed, and the element representing start - 1 to start will be recreated. This Element has to be recreated as after the content removal its offsets become start - 1 to start - 1.

/** * Called to remove child elements of <code>e</code> when one of the * elements to remove is representing the end character. * <p>Since the Content will not allow a removal to the end character * this will do a remove from <code>start - 1</code> to <code>end</code>. * The end Element(s) will be removed, and the element representing * <code>start - 1</code> to <code>start</code> will be recreated. This * Element has to be recreated as after the content removal its offsets * become <code>start - 1</code> to <code>start - 1</code>. */
private void removeElementsAtEnd(Element e, int index, int count, int start, int end) throws BadLocationException { // index must be > 0 otherwise no insert would have happened. boolean isLeaf = (e.getElement(index - 1).isLeaf()); DefaultDocumentEvent dde = new DefaultDocumentEvent( start - 1, end - start + 1, DocumentEvent. EventType.REMOVE); if (isLeaf) { Element endE = getCharacterElement(getLength()); // e.getElement(index - 1) should represent the newline. index--; if (endE.getParentElement() != e) { // The hiearchies don't match, we'll have to manually // recreate the leaf at e.getElement(index - 1) replace(dde, e, index, ++count, start, end, true, true); } else { // The hierarchies for the end Element and // e.getElement(index - 1), match, we can safely remove // the Elements and the end content will be aligned // appropriately. replace(dde, e, index, count, start, end, true, false); } } else { // Not a leaf, descend until we find the leaf representing // start - 1 and remove it. Element newLineE = e.getElement(index - 1); while (!newLineE.isLeaf()) { newLineE = newLineE.getElement(newLineE.getElementCount() - 1); } newLineE = newLineE.getParentElement(); replace(dde, e, index, count, start, end, false, false); replace(dde, newLineE, newLineE.getElementCount() - 1, 1, start, end, true, true); } postRemoveUpdate(dde); dde.end(); fireRemoveUpdate(dde); fireUndoableEditUpdate(new UndoableEditEvent(this, dde)); }
This is used by removeElementsAtEnd, it removes count elements starting at start from e. If remove is true text of length start - 1 to end - 1 is removed. If create is true a new leaf is created of length 1.
/** * This is used by <code>removeElementsAtEnd</code>, it removes * <code>count</code> elements starting at <code>start</code> from * <code>e</code>. If <code>remove</code> is true text of length * <code>start - 1</code> to <code>end - 1</code> is removed. If * <code>create</code> is true a new leaf is created of length 1. */
private void replace(DefaultDocumentEvent dde, Element e, int index, int count, int start, int end, boolean remove, boolean create) throws BadLocationException { Element[] added; AttributeSet attrs = e.getElement(index).getAttributes(); Element[] removed = new Element[count]; for (int counter = 0; counter < count; counter++) { removed[counter] = e.getElement(counter + index); } if (remove) { UndoableEdit u = getContent().remove(start - 1, end - start); if (u != null) { dde.addEdit(u); } } if (create) { added = new Element[1]; added[0] = createLeafElement(e, attrs, start - 1, start); } else { added = new Element[0]; } dde.addEdit(new ElementEdit(e, index, removed, added)); ((AbstractDocument.BranchElement)e).replace( index, removed.length, added); }
Called to remove child Elements when the end is not touched.
/** * Called to remove child Elements when the end is not touched. */
private void removeElements(Element e, int index, int count, int start, int end) throws BadLocationException { Element[] removed = new Element[count]; Element[] added = new Element[0]; for (int counter = 0; counter < count; counter++) { removed[counter] = e.getElement(counter + index); } DefaultDocumentEvent dde = new DefaultDocumentEvent (start, end - start, DocumentEvent.EventType.REMOVE); ((AbstractDocument.BranchElement)e).replace(index, removed.length, added); dde.addEdit(new ElementEdit(e, index, removed, added)); UndoableEdit u = getContent().remove(start, end - start); if (u != null) { dde.addEdit(u); } postRemoveUpdate(dde); dde.end(); fireRemoveUpdate(dde); if (u != null) { fireUndoableEditUpdate(new UndoableEditEvent(this, dde)); } } // These two are provided for inner class access. The are named different // than the super class as the super class implementations are final. void obtainLock() { writeLock(); } void releaseLock() { writeUnlock(); } // // Provided for inner class access. //
Notifies all listeners that have registered interest for notification on this event type. The event instance is lazily created using the parameters passed into the fire method.
Params:
  • e – the event
See Also:
/** * Notifies all listeners that have registered interest for * notification on this event type. The event instance * is lazily created using the parameters passed into * the fire method. * * @param e the event * @see EventListenerList */
protected void fireChangedUpdate(DocumentEvent e) { super.fireChangedUpdate(e); }
Notifies all listeners that have registered interest for notification on this event type. The event instance is lazily created using the parameters passed into the fire method.
Params:
  • e – the event
See Also:
/** * Notifies all listeners that have registered interest for * notification on this event type. The event instance * is lazily created using the parameters passed into * the fire method. * * @param e the event * @see EventListenerList */
protected void fireUndoableEditUpdate(UndoableEditEvent e) { super.fireUndoableEditUpdate(e); } boolean hasBaseTag() { return hasBaseTag; } String getBaseTarget() { return baseTarget; } /* * state defines whether the document is a frame document * or not. */ private boolean frameDocument = false; private boolean preservesUnknownTags = true; /* * Used to store button groups for radio buttons in * a form. */ private HashMap<String, ButtonGroup> radioButtonGroupsMap;
Document property for the number of tokens to buffer before building an element subtree to represent them.
/** * Document property for the number of tokens to buffer * before building an element subtree to represent them. */
static final String TokenThreshold = "token threshold"; private static final int MaxThreshold = 10000; private static final int StepThreshold = 5;
Document property key value. The value for the key will be a Vector of Strings that are comments not found in the body.
/** * Document property key value. The value for the key will be a Vector * of Strings that are comments not found in the body. */
public static final String AdditionalComments = "AdditionalComments";
Document property key value. The value for the key will be a String indicating the default type of stylesheet links.
/** * Document property key value. The value for the key will be a * String indicating the default type of stylesheet links. */
/* public */ static final String StyleType = "StyleType";
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.
/** * 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. */
URL base;
does the document have base tag
/** * does the document have base tag */
boolean hasBaseTag = false;
BASE tag's TARGET attribute value
/** * BASE tag's TARGET attribute value */
private String baseTarget = null;
The parser that is used when inserting html into the existing document.
/** * The parser that is used when inserting html into the existing * document. */
private HTMLEditorKit.Parser parser;
Used for inserts when a null AttributeSet is supplied.
/** * Used for inserts when a null AttributeSet is supplied. */
private static AttributeSet contentAttributeSet;
Property Maps are registered under, will be a Hashtable.
/** * Property Maps are registered under, will be a Hashtable. */
static String MAP_PROPERTY = "__MAP__"; private static char[] NEWLINE;
Indicates that direct insertion to body section takes place.
/** * Indicates that direct insertion to body section takes place. */
private boolean insertInBody = false;
I18N property key.
See Also:
  • I18NProperty.I18NProperty
/** * I18N property key. * * @see AbstractDocument#I18NProperty */
private static final String I18NProperty = "i18n"; static { contentAttributeSet = new SimpleAttributeSet(); ((MutableAttributeSet)contentAttributeSet). addAttribute(StyleConstants.NameAttribute, HTML.Tag.CONTENT); NEWLINE = new char[1]; NEWLINE[0] = '\n'; }
An iterator to iterate over a particular type of tag. The iterator is not thread safe. If reliable access to the document is not already ensured by the context under which the iterator is being used, its use should be performed under the protection of Document.render.
/** * An iterator to iterate over a particular type of * tag. The iterator is not thread safe. If reliable * access to the document is not already ensured by * the context under which the iterator is being used, * its use should be performed under the protection of * Document.render. */
public abstract static class Iterator {
Return the attributes for this tag.
Returns:the AttributeSet for this tag, or null if none can be found
/** * Return the attributes for this tag. * @return the <code>AttributeSet</code> for this tag, or * <code>null</code> if none can be found */
public abstract AttributeSet getAttributes();
Returns the start of the range for which the current occurrence of the tag is defined and has the same attributes.
Returns:the start of the range, or -1 if it can't be found
/** * Returns the start of the range for which the current occurrence of * the tag is defined and has the same attributes. * * @return the start of the range, or -1 if it can't be found */
public abstract int getStartOffset();
Returns the end of the range for which the current occurrence of the tag is defined and has the same attributes.
Returns:the end of the range
/** * Returns the end of the range for which the current occurrence of * the tag is defined and has the same attributes. * * @return the end of the range */
public abstract int getEndOffset();
Move the iterator forward to the next occurrence of the tag it represents.
/** * Move the iterator forward to the next occurrence * of the tag it represents. */
public abstract void next();
Indicates if the iterator is currently representing an occurrence of a tag. If false there are no more tags for this iterator.
Returns:true if the iterator is currently representing an occurrence of a tag, otherwise returns false
/** * Indicates if the iterator is currently * representing an occurrence of a tag. If * false there are no more tags for this iterator. * @return true if the iterator is currently representing an * occurrence of a tag, otherwise returns false */
public abstract boolean isValid();
Type of tag this iterator represents.
Returns:the tag
/** * Type of tag this iterator represents. * @return the tag */
public abstract HTML.Tag getTag(); }
An iterator to iterate over a particular type of tag.
/** * An iterator to iterate over a particular type of tag. */
static class LeafIterator extends Iterator { LeafIterator(HTML.Tag t, Document doc) { tag = t; pos = new ElementIterator(doc); endOffset = 0; next(); }
Returns the attributes for this tag.
Returns:the AttributeSet for this tag, or null if none can be found
/** * Returns the attributes for this tag. * @return the <code>AttributeSet</code> for this tag, * or <code>null</code> if none can be found */
public AttributeSet getAttributes() { Element elem = pos.current(); if (elem != null) { AttributeSet a = (AttributeSet) elem.getAttributes().getAttribute(tag); if (a == null) { a = elem.getAttributes(); } return a; } return null; }
Returns the start of the range for which the current occurrence of the tag is defined and has the same attributes.
Returns:the start of the range, or -1 if it can't be found
/** * Returns the start of the range for which the current occurrence of * the tag is defined and has the same attributes. * * @return the start of the range, or -1 if it can't be found */
public int getStartOffset() { Element elem = pos.current(); if (elem != null) { return elem.getStartOffset(); } return -1; }
Returns the end of the range for which the current occurrence of the tag is defined and has the same attributes.
Returns:the end of the range
/** * Returns the end of the range for which the current occurrence of * the tag is defined and has the same attributes. * * @return the end of the range */
public int getEndOffset() { return endOffset; }
Moves the iterator forward to the next occurrence of the tag it represents.
/** * Moves the iterator forward to the next occurrence * of the tag it represents. */
public void next() { for (nextLeaf(pos); isValid(); nextLeaf(pos)) { Element elem = pos.current(); if (elem.getStartOffset() >= endOffset) { AttributeSet a = pos.current().getAttributes(); if (a.isDefined(tag) || a.getAttribute(StyleConstants.NameAttribute) == tag) { // we found the next one setEndOffset(); break; } } } }
Returns the type of tag this iterator represents.
See Also:
Returns:the HTML.Tag that this iterator represents.
/** * Returns the type of tag this iterator represents. * * @return the <code>HTML.Tag</code> that this iterator represents. * @see javax.swing.text.html.HTML.Tag */
public HTML.Tag getTag() { return tag; }
Returns true if the current position is not null.
Returns:true if current position is not null, otherwise returns false
/** * Returns true if the current position is not <code>null</code>. * @return true if current position is not <code>null</code>, * otherwise returns false */
public boolean isValid() { return (pos.current() != null); }
Moves the given iterator to the next leaf element.
Params:
  • iter – the iterator to be scanned
/** * Moves the given iterator to the next leaf element. * @param iter the iterator to be scanned */
void nextLeaf(ElementIterator iter) { for (iter.next(); iter.current() != null; iter.next()) { Element e = iter.current(); if (e.isLeaf()) { break; } } }
Marches a cloned iterator forward to locate the end of the run. This sets the value of endOffset.
/** * Marches a cloned iterator forward to locate the end * of the run. This sets the value of <code>endOffset</code>. */
void setEndOffset() { AttributeSet a0 = getAttributes(); endOffset = pos.current().getEndOffset(); ElementIterator fwd = (ElementIterator) pos.clone(); for (nextLeaf(fwd); fwd.current() != null; nextLeaf(fwd)) { Element e = fwd.current(); AttributeSet a1 = (AttributeSet) e.getAttributes().getAttribute(tag); if ((a1 == null) || (! a1.equals(a0))) { break; } endOffset = e.getEndOffset(); } } private int endOffset; private HTML.Tag tag; private ElementIterator pos; }
An HTML reader to load an HTML document with an HTML element structure. This is a set of callbacks from the parser, implemented to create a set of elements tagged with attributes. The parse builds up tokens (ElementSpec) that describe the element subtree desired, and burst it into the document under the protection of a write lock using the insert method on the document outer class.

The reader can be configured by registering actions (of type HTMLDocument.HTMLReader.TagAction) that describe how to handle the action. The idea behind the actions provided is that the most natural text editing operations can be provided if the element structure boils down to paragraphs with runs of some kind of style in them. Some things are more naturally specified structurally, so arbitrary structure should be allowed above the paragraphs, but will need to be edited with structural actions. The implication of this is that some of the HTML elements specified in the stream being parsed will be collapsed into attributes, and in some cases paragraphs will be synthesized. When HTML elements have been converted to attributes, the attribute key will be of type HTML.Tag, and the value will be of type AttributeSet so that no information is lost. This enables many of the existing actions to work so that the user can type input, hit the return key, backspace, delete, etc and have a reasonable result. Selections can be created, and attributes applied or removed, etc. With this in mind, the work done by the reader can be categorized into the following kinds of tasks:

Block
Build the structure like it's specified in the stream. This produces elements that contain other elements.
Paragraph
Like block except that it's expected that the element will be used with a paragraph view so a paragraph element won't need to be synthesized.
Character
Contribute the element as an attribute that will start and stop at arbitrary text locations. This will ultimately be mixed into a run of text, with all of the currently flattened HTML character elements.
Special
Produce an embedded graphical element.
Form
Produce an element that is like the embedded graphical element, except that it also has a component model associated with it.
Hidden
Create an element that is hidden from view when the document is being viewed read-only, and visible when the document is being edited. This is useful to keep the model from losing information, and used to store things like comments and unrecognized tags.

Currently, <APPLET>, <PARAM>, <MAP>, <AREA>, <LINK>, <SCRIPT> and <STYLE> are unsupported.

The assignment of the actions described is shown in the following table for the tags defined in HTML.Tag.

HTML tags and assigned actions
Tag Action
HTML.Tag.A CharacterAction
HTML.Tag.ADDRESS CharacterAction
HTML.Tag.APPLET HiddenAction
HTML.Tag.AREA AreaAction
HTML.Tag.B CharacterAction
HTML.Tag.BASE BaseAction
HTML.Tag.BASEFONT CharacterAction
HTML.Tag.BIG CharacterAction
HTML.Tag.BLOCKQUOTE BlockAction
HTML.Tag.BODY BlockAction
HTML.Tag.BR SpecialAction
HTML.Tag.CAPTION BlockAction
HTML.Tag.CENTER BlockAction
HTML.Tag.CITE CharacterAction
HTML.Tag.CODE CharacterAction
HTML.Tag.DD BlockAction
HTML.Tag.DFN CharacterAction
HTML.Tag.DIR BlockAction
HTML.Tag.DIV BlockAction
HTML.Tag.DL BlockAction
HTML.Tag.DT ParagraphAction
HTML.Tag.EM CharacterAction
HTML.Tag.FONT CharacterAction
HTML.Tag.FORM As of 1.4 a BlockAction
HTML.Tag.FRAME SpecialAction
HTML.Tag.FRAMESET BlockAction
HTML.Tag.H1 ParagraphAction
HTML.Tag.H2 ParagraphAction
HTML.Tag.H3 ParagraphAction
HTML.Tag.H4 ParagraphAction
HTML.Tag.H5 ParagraphAction
HTML.Tag.H6 ParagraphAction
HTML.Tag.HEAD HeadAction
HTML.Tag.HR SpecialAction
HTML.Tag.HTML BlockAction
HTML.Tag.I CharacterAction
HTML.Tag.IMG SpecialAction
HTML.Tag.INPUT FormAction
HTML.Tag.ISINDEX IsndexAction
HTML.Tag.KBD CharacterAction
HTML.Tag.LI BlockAction
HTML.Tag.LINK LinkAction
HTML.Tag.MAP MapAction
HTML.Tag.MENU BlockAction
HTML.Tag.META MetaAction
HTML.Tag.NOFRAMES BlockAction
HTML.Tag.OBJECT SpecialAction
HTML.Tag.OL BlockAction
HTML.Tag.OPTION FormAction
HTML.Tag.P ParagraphAction
HTML.Tag.PARAM HiddenAction
HTML.Tag.PRE PreAction
HTML.Tag.SAMP CharacterAction
HTML.Tag.SCRIPT HiddenAction
HTML.Tag.SELECT FormAction
HTML.Tag.SMALL CharacterAction
HTML.Tag.STRIKE CharacterAction
HTML.Tag.S CharacterAction
HTML.Tag.STRONG CharacterAction
HTML.Tag.STYLE StyleAction
HTML.Tag.SUB CharacterAction
HTML.Tag.SUP CharacterAction
HTML.Tag.TABLE BlockAction
HTML.Tag.TD BlockAction
HTML.Tag.TEXTAREA FormAction
HTML.Tag.TH BlockAction
HTML.Tag.TITLE TitleAction
HTML.Tag.TR BlockAction
HTML.Tag.TT CharacterAction
HTML.Tag.U CharacterAction
HTML.Tag.UL BlockAction
HTML.Tag.VAR CharacterAction

Once </html> is encountered, the Actions are no longer notified.

/** * An HTML reader to load an HTML document with an HTML * element structure. This is a set of callbacks from * the parser, implemented to create a set of elements * tagged with attributes. The parse builds up tokens * (ElementSpec) that describe the element subtree desired, * and burst it into the document under the protection of * a write lock using the insert method on the document * outer class. * <p> * The reader can be configured by registering actions * (of type <code>HTMLDocument.HTMLReader.TagAction</code>) * that describe how to handle the action. The idea behind * the actions provided is that the most natural text editing * operations can be provided if the element structure boils * down to paragraphs with runs of some kind of style * in them. Some things are more naturally specified * structurally, so arbitrary structure should be allowed * above the paragraphs, but will need to be edited with structural * actions. The implication of this is that some of the * HTML elements specified in the stream being parsed will * be collapsed into attributes, and in some cases paragraphs * will be synthesized. When HTML elements have been * converted to attributes, the attribute key will be of * type HTML.Tag, and the value will be of type AttributeSet * so that no information is lost. This enables many of the * existing actions to work so that the user can type input, * hit the return key, backspace, delete, etc and have a * reasonable result. Selections can be created, and attributes * applied or removed, etc. With this in mind, the work done * by the reader can be categorized into the following kinds * of tasks: * <dl> * <dt>Block * <dd>Build the structure like it's specified in the stream. * This produces elements that contain other elements. * <dt>Paragraph * <dd>Like block except that it's expected that the element * will be used with a paragraph view so a paragraph element * won't need to be synthesized. * <dt>Character * <dd>Contribute the element as an attribute that will start * and stop at arbitrary text locations. This will ultimately * be mixed into a run of text, with all of the currently * flattened HTML character elements. * <dt>Special * <dd>Produce an embedded graphical element. * <dt>Form * <dd>Produce an element that is like the embedded graphical * element, except that it also has a component model associated * with it. * <dt>Hidden * <dd>Create an element that is hidden from view when the * document is being viewed read-only, and visible when the * document is being edited. This is useful to keep the * model from losing information, and used to store things * like comments and unrecognized tags. * * </dl> * <p> * Currently, &lt;APPLET&gt;, &lt;PARAM&gt;, &lt;MAP&gt;, &lt;AREA&gt;, &lt;LINK&gt;, * &lt;SCRIPT&gt; and &lt;STYLE&gt; are unsupported. * * <p> * The assignment of the actions described is shown in the * following table for the tags defined in <code>HTML.Tag</code>. * * <table class="striped"> * <caption>HTML tags and assigned actions</caption> * <thead> * <tr> * <th scope="col">Tag * <th scope="col">Action * </thead> * <tbody> * <tr> * <th scope="row">{@code HTML.Tag.A} * <td>CharacterAction * <tr> * <th scope="row">{@code HTML.Tag.ADDRESS} * <td>CharacterAction * <tr> * <th scope="row">{@code HTML.Tag.APPLET} * <td>HiddenAction * <tr> * <th scope="row">{@code HTML.Tag.AREA} * <td>AreaAction * <tr> * <th scope="row">{@code HTML.Tag.B} * <td>CharacterAction * <tr> * <th scope="row">{@code HTML.Tag.BASE} * <td>BaseAction * <tr> * <th scope="row">{@code HTML.Tag.BASEFONT} * <td>CharacterAction * <tr> * <th scope="row">{@code HTML.Tag.BIG} * <td>CharacterAction * <tr> * <th scope="row">{@code HTML.Tag.BLOCKQUOTE} * <td>BlockAction * <tr> * <th scope="row">{@code HTML.Tag.BODY} * <td>BlockAction * <tr> * <th scope="row">{@code HTML.Tag.BR} * <td>SpecialAction * <tr> * <th scope="row">{@code HTML.Tag.CAPTION} * <td>BlockAction * <tr> * <th scope="row">{@code HTML.Tag.CENTER} * <td>BlockAction * <tr> * <th scope="row">{@code HTML.Tag.CITE} * <td>CharacterAction * <tr> * <th scope="row">{@code HTML.Tag.CODE} * <td>CharacterAction * <tr> * <th scope="row">{@code HTML.Tag.DD} * <td>BlockAction * <tr> * <th scope="row">{@code HTML.Tag.DFN} * <td>CharacterAction * <tr> * <th scope="row">{@code HTML.Tag.DIR} * <td>BlockAction * <tr> * <th scope="row">{@code HTML.Tag.DIV} * <td>BlockAction * <tr> * <th scope="row">{@code HTML.Tag.DL} * <td>BlockAction * <tr> * <th scope="row">{@code HTML.Tag.DT} * <td>ParagraphAction * <tr> * <th scope="row">{@code HTML.Tag.EM} * <td>CharacterAction * <tr> * <th scope="row">{@code HTML.Tag.FONT} * <td>CharacterAction * <tr> * <th scope="row">{@code HTML.Tag.FORM} * <td>As of 1.4 a BlockAction * <tr> * <th scope="row">{@code HTML.Tag.FRAME} * <td>SpecialAction * <tr> * <th scope="row">{@code HTML.Tag.FRAMESET} * <td>BlockAction * <tr> * <th scope="row">{@code HTML.Tag.H1} * <td>ParagraphAction * <tr> * <th scope="row">{@code HTML.Tag.H2} * <td>ParagraphAction * <tr> * <th scope="row">{@code HTML.Tag.H3} * <td>ParagraphAction * <tr> * <th scope="row">{@code HTML.Tag.H4} * <td>ParagraphAction * <tr> * <th scope="row">{@code HTML.Tag.H5} * <td>ParagraphAction * <tr> * <th scope="row">{@code HTML.Tag.H6} * <td>ParagraphAction * <tr> * <th scope="row">{@code HTML.Tag.HEAD} * <td>HeadAction * <tr> * <th scope="row">{@code HTML.Tag.HR} * <td>SpecialAction * <tr> * <th scope="row">{@code HTML.Tag.HTML} * <td>BlockAction * <tr> * <th scope="row">{@code HTML.Tag.I} * <td>CharacterAction * <tr> * <th scope="row">{@code HTML.Tag.IMG} * <td>SpecialAction * <tr> * <th scope="row">{@code HTML.Tag.INPUT} * <td>FormAction * <tr> * <th scope="row">{@code HTML.Tag.ISINDEX} * <td>IsndexAction * <tr> * <th scope="row">{@code HTML.Tag.KBD} * <td>CharacterAction * <tr> * <th scope="row">{@code HTML.Tag.LI} * <td>BlockAction * <tr> * <th scope="row">{@code HTML.Tag.LINK} * <td>LinkAction * <tr> * <th scope="row">{@code HTML.Tag.MAP} * <td>MapAction * <tr> * <th scope="row">{@code HTML.Tag.MENU} * <td>BlockAction * <tr> * <th scope="row">{@code HTML.Tag.META} * <td>MetaAction * <tr> * <th scope="row">{@code HTML.Tag.NOFRAMES} * <td>BlockAction * <tr> * <th scope="row">{@code HTML.Tag.OBJECT} * <td>SpecialAction * <tr> * <th scope="row">{@code HTML.Tag.OL} * <td>BlockAction * <tr> * <th scope="row">{@code HTML.Tag.OPTION} * <td>FormAction * <tr> * <th scope="row">{@code HTML.Tag.P} * <td>ParagraphAction * <tr> * <th scope="row">{@code HTML.Tag.PARAM} * <td>HiddenAction * <tr> * <th scope="row">{@code HTML.Tag.PRE} * <td>PreAction * <tr> * <th scope="row">{@code HTML.Tag.SAMP} * <td>CharacterAction * <tr> * <th scope="row">{@code HTML.Tag.SCRIPT} * <td>HiddenAction * <tr> * <th scope="row">{@code HTML.Tag.SELECT} * <td>FormAction * <tr> * <th scope="row">{@code HTML.Tag.SMALL} * <td>CharacterAction * <tr> * <th scope="row">{@code HTML.Tag.STRIKE} * <td>CharacterAction * <tr> * <th scope="row">{@code HTML.Tag.S} * <td>CharacterAction * <tr> * <th scope="row">{@code HTML.Tag.STRONG} * <td>CharacterAction * <tr> * <th scope="row">{@code HTML.Tag.STYLE} * <td>StyleAction * <tr> * <th scope="row">{@code HTML.Tag.SUB} * <td>CharacterAction * <tr> * <th scope="row">{@code HTML.Tag.SUP} * <td>CharacterAction * <tr> * <th scope="row">{@code HTML.Tag.TABLE} * <td>BlockAction * <tr> * <th scope="row">{@code HTML.Tag.TD} * <td>BlockAction * <tr> * <th scope="row">{@code HTML.Tag.TEXTAREA} * <td>FormAction * <tr> * <th scope="row">{@code HTML.Tag.TH} * <td>BlockAction * <tr> * <th scope="row">{@code HTML.Tag.TITLE} * <td>TitleAction * <tr> * <th scope="row">{@code HTML.Tag.TR} * <td>BlockAction * <tr> * <th scope="row">{@code HTML.Tag.TT} * <td>CharacterAction * <tr> * <th scope="row">{@code HTML.Tag.U} * <td>CharacterAction * <tr> * <th scope="row">{@code HTML.Tag.UL} * <td>BlockAction * <tr> * <th scope="row">{@code HTML.Tag.VAR} * <td>CharacterAction * </tbody> * </table> * <p> * Once &lt;/html&gt; is encountered, the Actions are no longer notified. */
public class HTMLReader extends HTMLEditorKit.ParserCallback {
Constructs an HTMLReader using default pop and push depth and no tag to insert.
Params:
  • offset – the starting offset
/** * Constructs an HTMLReader using default pop and push depth and no tag to insert. * * @param offset the starting offset */
public HTMLReader(int offset) { this(offset, 0, 0, null); }
Constructs an HTMLReader.
Params:
  • offset – the starting offset
  • popDepth – how many parents to ascend before insert new element
  • pushDepth – how many parents to descend (relative to popDepth) before inserting
  • insertTag – a tag to insert (may be null)
/** * Constructs an HTMLReader. * * @param offset the starting offset * @param popDepth how many parents to ascend before insert new element * @param pushDepth how many parents to descend (relative to popDepth) before * inserting * @param insertTag a tag to insert (may be null) */
public HTMLReader(int offset, int popDepth, int pushDepth, HTML.Tag insertTag) { this(offset, popDepth, pushDepth, insertTag, true, false, true); }
Generates a RuntimeException (will eventually generate a BadLocationException when API changes are alloced) if inserting into non empty document, insertTag is non-null, and offset is not in the body.
/** * Generates a RuntimeException (will eventually generate * a BadLocationException when API changes are alloced) if inserting * into non empty document, <code>insertTag</code> is * non-<code>null</code>, and <code>offset</code> is not in the body. */
// PENDING(sky): Add throws BadLocationException and remove // RuntimeException HTMLReader(int offset, int popDepth, int pushDepth, HTML.Tag insertTag, boolean insertInsertTag, boolean insertAfterImplied, boolean wantsTrailingNewline) { emptyDocument = (getLength() == 0); isStyleCSS = "text/css".equals(getDefaultStyleSheetType()); this.offset = offset; threshold = HTMLDocument.this.getTokenThreshold(); tagMap = new Hashtable<HTML.Tag, TagAction>(57); TagAction na = new TagAction(); TagAction ba = new BlockAction(); TagAction pa = new ParagraphAction(); TagAction ca = new CharacterAction(); TagAction sa = new SpecialAction(); TagAction fa = new FormAction(); TagAction ha = new HiddenAction(); TagAction conv = new ConvertAction(); // register handlers for the well known tags tagMap.put(HTML.Tag.A, new AnchorAction()); tagMap.put(HTML.Tag.ADDRESS, ca); tagMap.put(HTML.Tag.APPLET, ha); tagMap.put(HTML.Tag.AREA, new AreaAction()); tagMap.put(HTML.Tag.B, conv); tagMap.put(HTML.Tag.BASE, new BaseAction()); tagMap.put(HTML.Tag.BASEFONT, ca); tagMap.put(HTML.Tag.BIG, ca); tagMap.put(HTML.Tag.BLOCKQUOTE, ba); tagMap.put(HTML.Tag.BODY, ba); tagMap.put(HTML.Tag.BR, sa); tagMap.put(HTML.Tag.CAPTION, ba); tagMap.put(HTML.Tag.CENTER, ba); tagMap.put(HTML.Tag.CITE, ca); tagMap.put(HTML.Tag.CODE, ca); tagMap.put(HTML.Tag.DD, ba); tagMap.put(HTML.Tag.DFN, ca); tagMap.put(HTML.Tag.DIR, ba); tagMap.put(HTML.Tag.DIV, ba); tagMap.put(HTML.Tag.DL, ba); tagMap.put(HTML.Tag.DT, pa); tagMap.put(HTML.Tag.EM, ca); tagMap.put(HTML.Tag.FONT, conv); tagMap.put(HTML.Tag.FORM, new FormTagAction()); tagMap.put(HTML.Tag.FRAME, sa); tagMap.put(HTML.Tag.FRAMESET, ba); tagMap.put(HTML.Tag.H1, pa); tagMap.put(HTML.Tag.H2, pa); tagMap.put(HTML.Tag.H3, pa); tagMap.put(HTML.Tag.H4, pa); tagMap.put(HTML.Tag.H5, pa); tagMap.put(HTML.Tag.H6, pa); tagMap.put(HTML.Tag.HEAD, new HeadAction()); tagMap.put(HTML.Tag.HR, sa); tagMap.put(HTML.Tag.HTML, ba); tagMap.put(HTML.Tag.I, conv); tagMap.put(HTML.Tag.IMG, sa); tagMap.put(HTML.Tag.INPUT, fa); tagMap.put(HTML.Tag.ISINDEX, new IsindexAction()); tagMap.put(HTML.Tag.KBD, ca); tagMap.put(HTML.Tag.LI, ba); tagMap.put(HTML.Tag.LINK, new LinkAction()); tagMap.put(HTML.Tag.MAP, new MapAction()); tagMap.put(HTML.Tag.MENU, ba); tagMap.put(HTML.Tag.META, new MetaAction()); tagMap.put(HTML.Tag.NOBR, ca); tagMap.put(HTML.Tag.NOFRAMES, ba); tagMap.put(HTML.Tag.OBJECT, sa); tagMap.put(HTML.Tag.OL, ba); tagMap.put(HTML.Tag.OPTION, fa); tagMap.put(HTML.Tag.P, pa); tagMap.put(HTML.Tag.PARAM, new ObjectAction()); tagMap.put(HTML.Tag.PRE, new PreAction()); tagMap.put(HTML.Tag.SAMP, ca); tagMap.put(HTML.Tag.SCRIPT, ha); tagMap.put(HTML.Tag.SELECT, fa); tagMap.put(HTML.Tag.SMALL, ca); tagMap.put(HTML.Tag.SPAN, ca); tagMap.put(HTML.Tag.STRIKE, conv); tagMap.put(HTML.Tag.S, ca); tagMap.put(HTML.Tag.STRONG, ca); tagMap.put(HTML.Tag.STYLE, new StyleAction()); tagMap.put(HTML.Tag.SUB, conv); tagMap.put(HTML.Tag.SUP, conv); tagMap.put(HTML.Tag.TABLE, ba); tagMap.put(HTML.Tag.TD, ba); tagMap.put(HTML.Tag.TEXTAREA, fa); tagMap.put(HTML.Tag.TH, ba); tagMap.put(HTML.Tag.TITLE, new TitleAction()); tagMap.put(HTML.Tag.TR, ba); tagMap.put(HTML.Tag.TT, ca); tagMap.put(HTML.Tag.U, conv); tagMap.put(HTML.Tag.UL, ba); tagMap.put(HTML.Tag.VAR, ca); if (insertTag != null) { this.insertTag = insertTag; this.popDepth = popDepth; this.pushDepth = pushDepth; this.insertInsertTag = insertInsertTag; foundInsertTag = false; } else { foundInsertTag = true; } if (insertAfterImplied) { this.popDepth = popDepth; this.pushDepth = pushDepth; this.insertAfterImplied = true; foundInsertTag = false; midInsert = false; this.insertInsertTag = true; this.wantsTrailingNewline = wantsTrailingNewline; } else { midInsert = (!emptyDocument && insertTag == null); if (midInsert) { generateEndsSpecsForMidInsert(); } } /** * This block initializes the <code>inParagraph</code> flag. * It is left in <code>false</code> value automatically * if the target document is empty or future inserts * were positioned into the 'body' tag. */ if (!emptyDocument && !midInsert) { int targetOffset = Math.max(this.offset - 1, 0); Element elem = HTMLDocument.this.getCharacterElement(targetOffset); /* Going up by the left document structure path */ for (int i = 0; i <= this.popDepth; i++) { elem = elem.getParentElement(); } /* Going down by the right document structure path */ for (int i = 0; i < this.pushDepth; i++) { int index = elem.getElementIndex(this.offset); elem = elem.getElement(index); } AttributeSet attrs = elem.getAttributes(); if (attrs != null) { HTML.Tag tagToInsertInto = (HTML.Tag) attrs.getAttribute(StyleConstants.NameAttribute); if (tagToInsertInto != null) { this.inParagraph = tagToInsertInto.isParagraph(); } } } }
Generates an initial batch of end ElementSpecs in parseBuffer to position future inserts into the body.
/** * Generates an initial batch of end <code>ElementSpecs</code> * in parseBuffer to position future inserts into the body. */
private void generateEndsSpecsForMidInsert() { int count = heightToElementWithName(HTML.Tag.BODY, Math.max(0, offset - 1)); boolean joinNext = false; if (count == -1 && offset > 0) { count = heightToElementWithName(HTML.Tag.BODY, offset); if (count != -1) { // Previous isn't in body, but current is. Have to // do some end specs, followed by join next. count = depthTo(offset - 1) - 1; joinNext = true; } } if (count == -1) { throw new RuntimeException("Must insert new content into body element-"); } if (count != -1) { // Insert a newline, if necessary. try { if (!joinNext && offset > 0 && !getText(offset - 1, 1).equals("\n")) { SimpleAttributeSet newAttrs = new SimpleAttributeSet(); newAttrs.addAttribute(StyleConstants.NameAttribute, HTML.Tag.CONTENT); ElementSpec spec = new ElementSpec(newAttrs, ElementSpec.ContentType, NEWLINE, 0, 1); parseBuffer.addElement(spec); } // Should never throw, but will catch anyway. } catch (BadLocationException ble) {} while (count-- > 0) { parseBuffer.addElement(new ElementSpec (null, ElementSpec.EndTagType)); } if (joinNext) { ElementSpec spec = new ElementSpec(null, ElementSpec. StartTagType); spec.setDirection(ElementSpec.JoinNextDirection); parseBuffer.addElement(spec); } } // We should probably throw an exception if (count == -1) // Or look for the body and reset the offset. }
Returns:number of parents to reach the child at offset.
/** * @return number of parents to reach the child at offset. */
private int depthTo(int offset) { Element e = getDefaultRootElement(); int count = 0; while (!e.isLeaf()) { count++; e = e.getElement(e.getElementIndex(offset)); } return count; }
Returns:number of parents of the leaf at offset until a parent with name, name has been found. -1 indicates no matching parent with name.
/** * @return number of parents of the leaf at <code>offset</code> * until a parent with name, <code>name</code> has been * found. -1 indicates no matching parent with * <code>name</code>. */
private int heightToElementWithName(Object name, int offset) { Element e = getCharacterElement(offset).getParentElement(); int count = 0; while (e != null && e.getAttributes().getAttribute (StyleConstants.NameAttribute) != name) { count++; e = e.getParentElement(); } return (e == null) ? -1 : count; }
This will make sure there aren't two BODYs (the second is typically created when you do a remove all, and then an insert).
/** * This will make sure there aren't two BODYs (the second is * typically created when you do a remove all, and then an insert). */
private void adjustEndElement() { int length = getLength(); if (length == 0) { return; } obtainLock(); try { Element[] pPath = getPathTo(length - 1); int pLength = pPath.length; if (pLength > 1 && pPath[1].getAttributes().getAttribute (StyleConstants.NameAttribute) == HTML.Tag.BODY && pPath[1].getEndOffset() == length) { String lastText = getText(length - 1, 1); DefaultDocumentEvent event; Element[] added; Element[] removed; int index; // Remove the fake second body. added = new Element[0]; removed = new Element[1]; index = pPath[0].getElementIndex(length); removed[0] = pPath[0].getElement(index); ((BranchElement)pPath[0]).replace(index, 1, added); ElementEdit firstEdit = new ElementEdit(pPath[0], index, removed, added); // Insert a new element to represent the end that the // second body was representing. SimpleAttributeSet sas = new SimpleAttributeSet(); sas.addAttribute(StyleConstants.NameAttribute, HTML.Tag.CONTENT); sas.addAttribute(IMPLIED_CR, Boolean.TRUE); added = new Element[1]; added[0] = createLeafElement(pPath[pLength - 1], sas, length, length + 1); index = pPath[pLength - 1].getElementCount(); ((BranchElement)pPath[pLength - 1]).replace(index, 0, added); event = new DefaultDocumentEvent(length, 1, DocumentEvent.EventType.CHANGE); event.addEdit(new ElementEdit(pPath[pLength - 1], index, new Element[0], added)); event.addEdit(firstEdit); event.end(); fireChangedUpdate(event); fireUndoableEditUpdate(new UndoableEditEvent(this, event)); if (lastText.equals("\n")) { // We now have two \n's, one part of the Document. // We need to remove one event = new DefaultDocumentEvent(length - 1, 1, DocumentEvent.EventType.REMOVE); removeUpdate(event); UndoableEdit u = getContent().remove(length - 1, 1); if (u != null) { event.addEdit(u); } postRemoveUpdate(event); // Mark the edit as done. event.end(); fireRemoveUpdate(event); fireUndoableEditUpdate(new UndoableEditEvent( this, event)); } } } catch (BadLocationException ble) { } finally { releaseLock(); } } private Element[] getPathTo(int offset) { Stack<Element> elements = new Stack<Element>(); Element e = getDefaultRootElement(); int index; while (!e.isLeaf()) { elements.push(e); e = e.getElement(e.getElementIndex(offset)); } Element[] retValue = new Element[elements.size()]; elements.copyInto(retValue); return retValue; } // -- HTMLEditorKit.ParserCallback methods --------------------
The last method called on the reader. It allows any pending changes to be flushed into the document. Since this is currently loading synchronously, the entire set of changes are pushed in at this point.
/** * The last method called on the reader. It allows * any pending changes to be flushed into the document. * Since this is currently loading synchronously, the entire * set of changes are pushed in at this point. */
public void flush() throws BadLocationException { if (emptyDocument && !insertAfterImplied) { if (HTMLDocument.this.getLength() > 0 || parseBuffer.size() > 0) { flushBuffer(true); adjustEndElement(); } // We won't insert when } else { flushBuffer(true); } }
Called by the parser to indicate a block of text was encountered.
/** * Called by the parser to indicate a block of text was * encountered. */
public void handleText(char[] data, int pos) { if (receivedEndHTML || (midInsert && !inBody)) { return; } // see if complex glyph layout support is needed if(HTMLDocument.this.getProperty(I18NProperty).equals( Boolean.FALSE ) ) { // if a default direction of right-to-left has been specified, // we want complex layout even if the text is all left to right. Object d = getProperty(TextAttribute.RUN_DIRECTION); if ((d != null) && (d.equals(TextAttribute.RUN_DIRECTION_RTL))) { HTMLDocument.this.putProperty( I18NProperty, Boolean.TRUE); } else { if (SwingUtilities2.isComplexLayout(data, 0, data.length)) { HTMLDocument.this.putProperty( I18NProperty, Boolean.TRUE); } } } if (inTextArea) { textAreaContent(data); } else if (inPre) { preContent(data); } else if (inTitle) { putProperty(Document.TitleProperty, new String(data)); } else if (option != null) { option.setLabel(new String(data)); } else if (inStyle) { if (styles != null) { styles.addElement(new String(data)); } } else if (inBlock > 0) { if (!foundInsertTag && insertAfterImplied) { // Assume content should be added. foundInsertTag(false); foundInsertTag = true; // If content is added directly to the body, it should // be wrapped by p-implied. inParagraph = impliedP = !insertInBody; } if (data.length >= 1) { addContent(data, 0, data.length); } } }
Callback from the parser. Route to the appropriate handler for the tag.
/** * Callback from the parser. Route to the appropriate * handler for the tag. */
public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) { if (receivedEndHTML) { return; } if (midInsert && !inBody) { if (t == HTML.Tag.BODY) { inBody = true; // Increment inBlock since we know we are in the body, // this is needed incase an implied-p is needed. If // inBlock isn't incremented, and an implied-p is // encountered, addContent won't be called! inBlock++; } return; } if (!inBody && t == HTML.Tag.BODY) { inBody = true; } if (isStyleCSS && a.isDefined(HTML.Attribute.STYLE)) { // Map the style attributes. String decl = (String)a.getAttribute(HTML.Attribute.STYLE); a.removeAttribute(HTML.Attribute.STYLE); styleAttributes = getStyleSheet().getDeclaration(decl); a.addAttributes(styleAttributes); } else { styleAttributes = null; } TagAction action = tagMap.get(t); if (action != null) { action.start(t, a); } } public void handleComment(char[] data, int pos) { if (receivedEndHTML) { addExternalComment(new String(data)); return; } if (inStyle) { if (styles != null) { styles.addElement(new String(data)); } } else if (getPreservesUnknownTags()) { if (inBlock == 0 && (foundInsertTag || insertTag != HTML.Tag.COMMENT)) { // Comment outside of body, will not be able to show it, // but can add it as a property on the Document. addExternalComment(new String(data)); return; } SimpleAttributeSet sas = new SimpleAttributeSet(); sas.addAttribute(HTML.Attribute.COMMENT, new String(data)); addSpecialElement(HTML.Tag.COMMENT, sas); } TagAction action = tagMap.get(HTML.Tag.COMMENT); if (action != null) { action.start(HTML.Tag.COMMENT, new SimpleAttributeSet()); action.end(HTML.Tag.COMMENT); } }
Adds the comment comment to the set of comments maintained outside of the scope of elements.
/** * Adds the comment <code>comment</code> to the set of comments * maintained outside of the scope of elements. */
private void addExternalComment(String comment) { Object comments = getProperty(AdditionalComments); if (comments != null && !(comments instanceof Vector)) { // No place to put comment. return; } if (comments == null) { comments = new Vector<>(); putProperty(AdditionalComments, comments); } @SuppressWarnings("unchecked") Vector<Object> v = (Vector<Object>)comments; v.addElement(comment); }
Callback from the parser. Route to the appropriate handler for the tag.
/** * Callback from the parser. Route to the appropriate * handler for the tag. */
public void handleEndTag(HTML.Tag t, int pos) { if (receivedEndHTML || (midInsert && !inBody)) { return; } if (t == HTML.Tag.HTML) { receivedEndHTML = true; } if (t == HTML.Tag.BODY) { inBody = false; if (midInsert) { inBlock--; } } TagAction action = tagMap.get(t); if (action != null) { action.end(t); } }
Callback from the parser. Route to the appropriate handler for the tag.
/** * Callback from the parser. Route to the appropriate * handler for the tag. */
public void handleSimpleTag(HTML.Tag t, MutableAttributeSet a, int pos) { if (receivedEndHTML || (midInsert && !inBody)) { return; } if (isStyleCSS && a.isDefined(HTML.Attribute.STYLE)) { // Map the style attributes. String decl = (String)a.getAttribute(HTML.Attribute.STYLE); a.removeAttribute(HTML.Attribute.STYLE); styleAttributes = getStyleSheet().getDeclaration(decl); a.addAttributes(styleAttributes); } else { styleAttributes = null; } TagAction action = tagMap.get(t); if (action != null) { action.start(t, a); action.end(t); } else if (getPreservesUnknownTags()) { // unknown tag, only add if should preserve it. addSpecialElement(t, a); } }
This is invoked after the stream has been parsed, but before flush. eol will be one of \n, \r or \r\n, which ever is encountered the most in parsing the stream.
Since:1.3
/** * This is invoked after the stream has been parsed, but before * <code>flush</code>. <code>eol</code> will be one of \n, \r * or \r\n, which ever is encountered the most in parsing the * stream. * * @since 1.3 */
public void handleEndOfLineString(String eol) { if (emptyDocument && eol != null) { putProperty(DefaultEditorKit.EndOfLineStringProperty, eol); } } // ---- tag handling support ------------------------------
Registers a handler for the given tag. By default all of the well-known tags will have been registered. This can be used to change the handling of a particular tag or to add support for custom tags.
Params:
  • t – an HTML tag
  • a – tag action handler
/** * Registers a handler for the given tag. By default * all of the well-known tags will have been registered. * This can be used to change the handling of a particular * tag or to add support for custom tags. * * @param t an HTML tag * @param a tag action handler */
protected void registerTag(HTML.Tag t, TagAction a) { tagMap.put(t, a); }
An action to be performed in response to parsing a tag. This allows customization of how each tag is handled and avoids a large switch statement.
/** * An action to be performed in response * to parsing a tag. This allows customization * of how each tag is handled and avoids a large * switch statement. */
public class TagAction {
Called when a start tag is seen for the type of tag this action was registered to. The tag argument indicates the actual tag for those actions that are shared across many tags. By default this does nothing and completely ignores the tag.
Params:
  • t – the HTML tag
  • a – the attributes
/** * Called when a start tag is seen for the * type of tag this action was registered * to. The tag argument indicates the actual * tag for those actions that are shared across * many tags. By default this does nothing and * completely ignores the tag. * * @param t the HTML tag * @param a the attributes */
public void start(HTML.Tag t, MutableAttributeSet a) { }
Called when an end tag is seen for the type of tag this action was registered to. The tag argument indicates the actual tag for those actions that are shared across many tags. By default this does nothing and completely ignores the tag.
Params:
  • t – the HTML tag
/** * Called when an end tag is seen for the * type of tag this action was registered * to. The tag argument indicates the actual * tag for those actions that are shared across * many tags. By default this does nothing and * completely ignores the tag. * * @param t the HTML tag */
public void end(HTML.Tag t) { } }
Action assigned by default to handle the Block task of the reader.
/** * Action assigned by default to handle the Block task of the reader. */
public class BlockAction extends TagAction { public void start(HTML.Tag t, MutableAttributeSet attr) { blockOpen(t, attr); } public void end(HTML.Tag t) { blockClose(t); } }
Action used for the actual element form tag. This is named such as there was already a public class named FormAction.
/** * Action used for the actual element form tag. This is named such * as there was already a public class named FormAction. */
private class FormTagAction extends BlockAction { public void start(HTML.Tag t, MutableAttributeSet attr) { super.start(t, attr); // initialize a ButtonGroupsMap when // FORM tag is encountered. This will // be used for any radio buttons that // might be defined in the FORM. // for new group new ButtonGroup will be created (fix for 4529702) // group name is a key in radioButtonGroupsMap radioButtonGroupsMap = new HashMap<String, ButtonGroup>(); } public void end(HTML.Tag t) { super.end(t); // reset the button group to null since // the form has ended. radioButtonGroupsMap = null; } }
Action assigned by default to handle the Paragraph task of the reader.
/** * Action assigned by default to handle the Paragraph task of the reader. */
public class ParagraphAction extends BlockAction { public void start(HTML.Tag t, MutableAttributeSet a) { super.start(t, a); inParagraph = true; } public void end(HTML.Tag t) { super.end(t); inParagraph = false; } }
Action assigned by default to handle the Special task of the reader.
/** * Action assigned by default to handle the Special task of the reader. */
public class SpecialAction extends TagAction { public void start(HTML.Tag t, MutableAttributeSet a) { addSpecialElement(t, a); } }
Action assigned by default to handle the Isindex task of the reader.
/** * Action assigned by default to handle the Isindex task of the reader. */
public class IsindexAction extends TagAction { public void start(HTML.Tag t, MutableAttributeSet a) { blockOpen(HTML.Tag.IMPLIED, new SimpleAttributeSet()); addSpecialElement(t, a); blockClose(HTML.Tag.IMPLIED); } }
Action assigned by default to handle the Hidden task of the reader.
/** * Action assigned by default to handle the Hidden task of the reader. */
public class HiddenAction extends TagAction { public void start(HTML.Tag t, MutableAttributeSet a) { addSpecialElement(t, a); } public void end(HTML.Tag t) { if (!isEmpty(t)) { MutableAttributeSet a = new SimpleAttributeSet(); a.addAttribute(HTML.Attribute.ENDTAG, "true"); addSpecialElement(t, a); } } boolean isEmpty(HTML.Tag t) { if (t == HTML.Tag.APPLET || t == HTML.Tag.SCRIPT) { return false; } return true; } }
Subclass of HiddenAction to set the content type for style sheets, and to set the name of the default style sheet.
/** * Subclass of HiddenAction to set the content type for style sheets, * and to set the name of the default style sheet. */
class MetaAction extends HiddenAction { public void start(HTML.Tag t, MutableAttributeSet a) { Object equiv = a.getAttribute(HTML.Attribute.HTTPEQUIV); if (equiv != null) { equiv = ((String)equiv).toLowerCase(); if (equiv.equals("content-style-type")) { String value = (String)a.getAttribute (HTML.Attribute.CONTENT); setDefaultStyleSheetType(value); isStyleCSS = "text/css".equals (getDefaultStyleSheetType()); } else if (equiv.equals("default-style")) { defaultStyle = (String)a.getAttribute (HTML.Attribute.CONTENT); } } super.start(t, a); } boolean isEmpty(HTML.Tag t) { return true; } }
End if overridden to create the necessary stylesheets that are referenced via the link tag. It is done in this manner as the meta tag can be used to specify an alternate style sheet, and is not guaranteed to come before the link tags.
/** * End if overridden to create the necessary stylesheets that * are referenced via the link tag. It is done in this manner * as the meta tag can be used to specify an alternate style sheet, * and is not guaranteed to come before the link tags. */
class HeadAction extends BlockAction { public void start(HTML.Tag t, MutableAttributeSet a) { inHead = true; // This check of the insertTag is put in to avoid considering // the implied-p that is generated for the head. This allows // inserts for HR to work correctly. if ((insertTag == null && !insertAfterImplied) || (insertTag == HTML.Tag.HEAD) || (insertAfterImplied && (foundInsertTag || !a.isDefined(IMPLIED)))) { super.start(t, a); } } public void end(HTML.Tag t) { inHead = inStyle = false; // See if there is a StyleSheet to link to. if (styles != null) { boolean isDefaultCSS = isStyleCSS; for (int counter = 0, maxCounter = styles.size(); counter < maxCounter;) { Object value = styles.elementAt(counter); if (value == HTML.Tag.LINK) { handleLink((AttributeSet)styles. elementAt(++counter)); counter++; } else { // Rule. // First element gives type. String type = (String)styles.elementAt(++counter); boolean isCSS = (type == null) ? isDefaultCSS : type.equals("text/css"); while (++counter < maxCounter && (styles.elementAt(counter) instanceof String)) { if (isCSS) { addCSSRules((String)styles.elementAt (counter)); } } } } } if ((insertTag == null && !insertAfterImplied) || insertTag == HTML.Tag.HEAD || (insertAfterImplied && foundInsertTag)) { super.end(t); } } boolean isEmpty(HTML.Tag t) { return false; } private void handleLink(AttributeSet attr) { // Link. String type = (String)attr.getAttribute(HTML.Attribute.TYPE); if (type == null) { type = getDefaultStyleSheetType(); } // Only choose if type==text/css // Select link if rel==stylesheet. // Otherwise if rel==alternate stylesheet and // title matches default style. if (type.equals("text/css")) { String rel = (String)attr.getAttribute(HTML.Attribute.REL); String title = (String)attr.getAttribute (HTML.Attribute.TITLE); String media = (String)attr.getAttribute (HTML.Attribute.MEDIA); if (media == null) { media = "all"; } else { media = media.toLowerCase(); } if (rel != null) { rel = rel.toLowerCase(); if ((media.indexOf("all") != -1 || media.indexOf("screen") != -1) && (rel.equals("stylesheet") || (rel.equals("alternate stylesheet") && title.equals(defaultStyle)))) { linkCSSStyleSheet((String)attr.getAttribute (HTML.Attribute.HREF)); } } } } }
A subclass to add the AttributeSet to styles if the attributes contains an attribute for 'rel' with value 'stylesheet' or 'alternate stylesheet'.
/** * A subclass to add the AttributeSet to styles if the * attributes contains an attribute for 'rel' with value * 'stylesheet' or 'alternate stylesheet'. */
class LinkAction extends HiddenAction { public void start(HTML.Tag t, MutableAttributeSet a) { String rel = (String)a.getAttribute(HTML.Attribute.REL); if (rel != null) { rel = rel.toLowerCase(); if (rel.equals("stylesheet") || rel.equals("alternate stylesheet")) { if (styles == null) { styles = new Vector<Object>(3); } styles.addElement(t); styles.addElement(a.copyAttributes()); } } super.start(t, a); } } class MapAction extends TagAction { public void start(HTML.Tag t, MutableAttributeSet a) { lastMap = new Map((String)a.getAttribute(HTML.Attribute.NAME)); addMap(lastMap); } public void end(HTML.Tag t) { } } class AreaAction extends TagAction { public void start(HTML.Tag t, MutableAttributeSet a) { if (lastMap != null) { lastMap.addArea(a.copyAttributes()); } } public void end(HTML.Tag t) { } } class StyleAction extends TagAction { public void start(HTML.Tag t, MutableAttributeSet a) { if (inHead) { if (styles == null) { styles = new Vector<Object>(3); } styles.addElement(t); styles.addElement(a.getAttribute(HTML.Attribute.TYPE)); inStyle = true; } } public void end(HTML.Tag t) { inStyle = false; } boolean isEmpty(HTML.Tag t) { return false; } }
Action assigned by default to handle the Pre block task of the reader.
/** * Action assigned by default to handle the Pre block task of the reader. */
public class PreAction extends BlockAction { public void start(HTML.Tag t, MutableAttributeSet attr) { inPre = true; blockOpen(t, attr); attr.addAttribute(CSS.Attribute.WHITE_SPACE, "pre"); blockOpen(HTML.Tag.IMPLIED, attr); } public void end(HTML.Tag t) { blockClose(HTML.Tag.IMPLIED); // set inPre to false after closing, so that if a newline // is added it won't generate a blockOpen. inPre = false; blockClose(t); } }
Action assigned by default to handle the Character task of the reader.
/** * Action assigned by default to handle the Character task of the reader. */
public class CharacterAction extends TagAction { public void start(HTML.Tag t, MutableAttributeSet attr) { pushCharacterStyle(); if (!foundInsertTag) { // Note that the third argument should really be based off // inParagraph and impliedP. If we're wrong (that is // insertTagDepthDelta shouldn't be changed), we'll end up // removing an extra EndSpec, which won't matter anyway. boolean insert = canInsertTag(t, attr, false); if (foundInsertTag) { if (!inParagraph) { inParagraph = impliedP = true; } } if (!insert) { return; } } if (attr.isDefined(IMPLIED)) { attr.removeAttribute(IMPLIED); } charAttr.addAttribute(t, attr.copyAttributes()); if (styleAttributes != null) { charAttr.addAttributes(styleAttributes); } } public void end(HTML.Tag t) { popCharacterStyle(); } }
Provides conversion of HTML tag/attribute mappings that have a corresponding StyleConstants and CSS mapping. The conversion is to CSS attributes.
/** * Provides conversion of HTML tag/attribute * mappings that have a corresponding StyleConstants * and CSS mapping. The conversion is to CSS attributes. */
class ConvertAction extends TagAction { public void start(HTML.Tag t, MutableAttributeSet attr) { pushCharacterStyle(); if (!foundInsertTag) { // Note that the third argument should really be based off // inParagraph and impliedP. If we're wrong (that is // insertTagDepthDelta shouldn't be changed), we'll end up // removing an extra EndSpec, which won't matter anyway. boolean insert = canInsertTag(t, attr, false); if (foundInsertTag) { if (!inParagraph) { inParagraph = impliedP = true; } } if (!insert) { return; } } if (attr.isDefined(IMPLIED)) { attr.removeAttribute(IMPLIED); } if (styleAttributes != null) { charAttr.addAttributes(styleAttributes); } // We also need to add attr, otherwise we lose custom // attributes, including class/id for style lookups, and // further confuse style lookup (doesn't have tag). charAttr.addAttribute(t, attr.copyAttributes()); StyleSheet sheet = getStyleSheet(); if (t == HTML.Tag.B) { sheet.addCSSAttribute(charAttr, CSS.Attribute.FONT_WEIGHT, "bold"); } else if (t == HTML.Tag.I) { sheet.addCSSAttribute(charAttr, CSS.Attribute.FONT_STYLE, "italic"); } else if (t == HTML.Tag.U) { Object v = charAttr.getAttribute(CSS.Attribute.TEXT_DECORATION); String value = "underline"; value = (v != null) ? value + "," + v.toString() : value; sheet.addCSSAttribute(charAttr, CSS.Attribute.TEXT_DECORATION, value); } else if (t == HTML.Tag.STRIKE) { Object v = charAttr.getAttribute(CSS.Attribute.TEXT_DECORATION); String value = "line-through"; value = (v != null) ? value + "," + v.toString() : value; sheet.addCSSAttribute(charAttr, CSS.Attribute.TEXT_DECORATION, value); } else if (t == HTML.Tag.SUP) { Object v = charAttr.getAttribute(CSS.Attribute.VERTICAL_ALIGN); String value = "sup"; value = (v != null) ? value + "," + v.toString() : value; sheet.addCSSAttribute(charAttr, CSS.Attribute.VERTICAL_ALIGN, value); } else if (t == HTML.Tag.SUB) { Object v = charAttr.getAttribute(CSS.Attribute.VERTICAL_ALIGN); String value = "sub"; value = (v != null) ? value + "," + v.toString() : value; sheet.addCSSAttribute(charAttr, CSS.Attribute.VERTICAL_ALIGN, value); } else if (t == HTML.Tag.FONT) { String color = (String) attr.getAttribute(HTML.Attribute.COLOR); if (color != null) { sheet.addCSSAttribute(charAttr, CSS.Attribute.COLOR, color); } String face = (String) attr.getAttribute(HTML.Attribute.FACE); if (face != null) { sheet.addCSSAttribute(charAttr, CSS.Attribute.FONT_FAMILY, face); } String size = (String) attr.getAttribute(HTML.Attribute.SIZE); if (size != null) { sheet.addCSSAttributeFromHTML(charAttr, CSS.Attribute.FONT_SIZE, size); } } } public void end(HTML.Tag t) { popCharacterStyle(); } } class AnchorAction extends CharacterAction { public void start(HTML.Tag t, MutableAttributeSet attr) { // set flag to catch empty anchors emptyAnchor = true; super.start(t, attr); } public void end(HTML.Tag t) { if (emptyAnchor) { // if the anchor was empty it was probably a // named anchor point and we don't want to throw // it away. char[] one = new char[1]; one[0] = '\n'; addContent(one, 0, 1); } super.end(t); } } class TitleAction extends HiddenAction { public void start(HTML.Tag t, MutableAttributeSet attr) { inTitle = true; super.start(t, attr); } public void end(HTML.Tag t) { inTitle = false; super.end(t); } boolean isEmpty(HTML.Tag t) { return false; } } class BaseAction extends TagAction { public void start(HTML.Tag t, MutableAttributeSet attr) { String href = (String) attr.getAttribute(HTML.Attribute.HREF); if (href != null) { try { URL newBase = new URL(base, href); setBase(newBase); hasBaseTag = true; } catch (MalformedURLException ex) { } } baseTarget = (String) attr.getAttribute(HTML.Attribute.TARGET); } } class ObjectAction extends SpecialAction { public void start(HTML.Tag t, MutableAttributeSet a) { if (t == HTML.Tag.PARAM) { addParameter(a); } else { super.start(t, a); } } public void end(HTML.Tag t) { if (t != HTML.Tag.PARAM) { super.end(t); } } void addParameter(AttributeSet a) { String name = (String) a.getAttribute(HTML.Attribute.NAME); String value = (String) a.getAttribute(HTML.Attribute.VALUE); if ((name != null) && (value != null)) { ElementSpec objSpec = parseBuffer.lastElement(); MutableAttributeSet objAttr = (MutableAttributeSet) objSpec.getAttributes(); objAttr.addAttribute(name, value); } } }
Action to support forms by building all of the elements used to represent form controls. This will process the <INPUT>, <TEXTAREA>, <SELECT>, and <OPTION> tags. The element created by this action is expected to have the attribute StyleConstants.ModelAttribute set to the model that holds the state for the form control. This enables multiple views, and allows document to be iterated over picking up the data of the form. The following are the model assignments for the various type of form elements.
Model assignments for the various types of form elements
Element Type Model Type
input, type button DefaultButtonModel
input, type checkbox ToggleButtonModel
input, type image DefaultButtonModel
input, type password PlainDocument
input, type radio ToggleButtonModel
input, type reset DefaultButtonModel
input, type submit DefaultButtonModel
input, type text or type is null. PlainDocument
select DefaultComboBoxModel or an DefaultListModel, with an item type of Option
textarea PlainDocument
/** * Action to support forms by building all of the elements * used to represent form controls. This will process * the &lt;INPUT&gt;, &lt;TEXTAREA&gt;, &lt;SELECT&gt;, * and &lt;OPTION&gt; tags. The element created by * this action is expected to have the attribute * <code>StyleConstants.ModelAttribute</code> set to * the model that holds the state for the form control. * This enables multiple views, and allows document to * be iterated over picking up the data of the form. * The following are the model assignments for the * various type of form elements. * * <table class="striped"> * <caption>Model assignments for the various types of form elements * </caption> * <thead> * <tr> * <th scope="col">Element Type * <th scope="col">Model Type * </thead> * <tbody> * <tr> * <th scope="row">input, type button * <td>{@link DefaultButtonModel} * <tr> * <th scope="row">input, type checkbox * <td>{@link JToggleButton.ToggleButtonModel} * <tr> * <th scope="row">input, type image * <td>{@link DefaultButtonModel} * <tr> * <th scope="row">input, type password * <td>{@link PlainDocument} * <tr> * <th scope="row">input, type radio * <td>{@link JToggleButton.ToggleButtonModel} * <tr> * <th scope="row">input, type reset * <td>{@link DefaultButtonModel} * <tr> * <th scope="row">input, type submit * <td>{@link DefaultButtonModel} * <tr> * <th scope="row">input, type text or type is null. * <td>{@link PlainDocument} * <tr> * <th scope="row">select * <td>{@link DefaultComboBoxModel} or an {@link DefaultListModel}, * with an item type of Option * <tr> * <td>textarea * <td>{@link PlainDocument} * </tbody> * </table> */
public class FormAction extends SpecialAction { public void start(HTML.Tag t, MutableAttributeSet attr) { if (t == HTML.Tag.INPUT) { String type = (String) attr.getAttribute(HTML.Attribute.TYPE); /* * if type is not defined the default is * assumed to be text. */ if (type == null) { type = "text"; attr.addAttribute(HTML.Attribute.TYPE, "text"); } setModel(type, attr); } else if (t == HTML.Tag.TEXTAREA) { inTextArea = true; textAreaDocument = new TextAreaDocument(); attr.addAttribute(StyleConstants.ModelAttribute, textAreaDocument); } else if (t == HTML.Tag.SELECT) { int size = HTML.getIntegerAttributeValue(attr, HTML.Attribute.SIZE, 1); boolean multiple = attr.getAttribute(HTML.Attribute.MULTIPLE) != null; if ((size > 1) || multiple) { OptionListModel<Option> m = new OptionListModel<Option>(); if (multiple) { m.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); } selectModel = m; } else { selectModel = new OptionComboBoxModel<Option>(); } attr.addAttribute(StyleConstants.ModelAttribute, selectModel); } // build the element, unless this is an option. if (t == HTML.Tag.OPTION) { option = new Option(attr); if (selectModel instanceof OptionListModel) { @SuppressWarnings("unchecked") OptionListModel<Option> m = (OptionListModel<Option>) selectModel; m.addElement(option); if (option.isSelected()) { m.addSelectionInterval(optionCount, optionCount); m.setInitialSelection(optionCount); } } else if (selectModel instanceof OptionComboBoxModel) { @SuppressWarnings("unchecked") OptionComboBoxModel<Option> m = (OptionComboBoxModel<Option>) selectModel; m.addElement(option); if (option.isSelected()) { m.setSelectedItem(option); m.setInitialSelection(option); } } optionCount++; } else { super.start(t, attr); } } public void end(HTML.Tag t) { if (t == HTML.Tag.OPTION) { option = null; } else { if (t == HTML.Tag.SELECT) { selectModel = null; optionCount = 0; } else if (t == HTML.Tag.TEXTAREA) { inTextArea = false; /* Now that the textarea has ended, * store the entire initial text * of the text area. This will * enable us to restore the initial * state if a reset is requested. */ textAreaDocument.storeInitialText(); } super.end(t); } } void setModel(String type, MutableAttributeSet attr) { if (type.equals("submit") || type.equals("reset") || type.equals("image")) { // button model attr.addAttribute(StyleConstants.ModelAttribute, new DefaultButtonModel()); } else if (type.equals("text") || type.equals("password")) { // plain text model int maxLength = HTML.getIntegerAttributeValue( attr, HTML.Attribute.MAXLENGTH, -1); Document doc; if (maxLength > 0) { doc = new FixedLengthDocument(maxLength); } else { doc = new PlainDocument(); } String value = (String) attr.getAttribute(HTML.Attribute.VALUE); try { doc.insertString(0, value, null); } catch (BadLocationException e) { } attr.addAttribute(StyleConstants.ModelAttribute, doc); } else if (type.equals("file")) { // plain text model attr.addAttribute(StyleConstants.ModelAttribute, new PlainDocument()); } else if (type.equals("checkbox") || type.equals("radio")) { JToggleButton.ToggleButtonModel model = new JToggleButton.ToggleButtonModel(); if (type.equals("radio")) { String name = (String) attr.getAttribute(HTML.Attribute.NAME); if ( radioButtonGroupsMap == null ) { //fix for 4772743 radioButtonGroupsMap = new HashMap<String, ButtonGroup>(); } ButtonGroup radioButtonGroup = radioButtonGroupsMap.get(name); if (radioButtonGroup == null) { radioButtonGroup = new ButtonGroup(); radioButtonGroupsMap.put(name,radioButtonGroup); } model.setGroup(radioButtonGroup); } boolean checked = (attr.getAttribute(HTML.Attribute.CHECKED) != null); model.setSelected(checked); attr.addAttribute(StyleConstants.ModelAttribute, model); } }
If a <SELECT> tag is being processed, this model will be a reference to the model being filled with the <OPTION> elements (which produce objects of type Option.
/** * If a &lt;SELECT&gt; tag is being processed, this * model will be a reference to the model being filled * with the &lt;OPTION&gt; elements (which produce * objects of type <code>Option</code>. */
Object selectModel; int optionCount; } // --- utility methods used by the reader ------------------
Pushes the current character style on a stack in preparation for forming a new nested character style.
/** * Pushes the current character style on a stack in preparation * for forming a new nested character style. */
protected void pushCharacterStyle() { charAttrStack.push(charAttr.copyAttributes()); }
Pops a previously pushed character style off the stack to return to a previous style.
/** * Pops a previously pushed character style off the stack * to return to a previous style. */
protected void popCharacterStyle() { if (!charAttrStack.empty()) { charAttr = (MutableAttributeSet) charAttrStack.peek(); charAttrStack.pop(); } }
Adds the given content to the textarea document. This method gets called when we are in a textarea context. Therefore all text that is seen belongs to the text area and is hence added to the TextAreaDocument associated with the text area.
Params:
  • data – the given content
/** * Adds the given content to the textarea document. * This method gets called when we are in a textarea * context. Therefore all text that is seen belongs * to the text area and is hence added to the * TextAreaDocument associated with the text area. * * @param data the given content */
protected void textAreaContent(char[] data) { try { textAreaDocument.insertString(textAreaDocument.getLength(), new String(data), null); } catch (BadLocationException e) { // Should do something reasonable } }
Adds the given content that was encountered in a PRE element. This synthesizes lines to hold the runs of text, and makes calls to addContent to actually add the text.
Params:
  • data – the given content
/** * Adds the given content that was encountered in a * PRE element. This synthesizes lines to hold the * runs of text, and makes calls to addContent to * actually add the text. * * @param data the given content */
protected void preContent(char[] data) { int last = 0; for (int i = 0; i < data.length; i++) { if (data[i] == '\n') { addContent(data, last, i - last + 1); blockClose(HTML.Tag.IMPLIED); MutableAttributeSet a = new SimpleAttributeSet(); a.addAttribute(CSS.Attribute.WHITE_SPACE, "pre"); blockOpen(HTML.Tag.IMPLIED, a); last = i + 1; } } if (last < data.length) { addContent(data, last, data.length - last); } }
Adds an instruction to the parse buffer to create a block element with the given attributes.
Params:
  • t – an HTML tag
  • attr – the attribute set
/** * Adds an instruction to the parse buffer to create a * block element with the given attributes. * * @param t an HTML tag * @param attr the attribute set */
protected void blockOpen(HTML.Tag t, MutableAttributeSet attr) { if (impliedP) { blockClose(HTML.Tag.IMPLIED); } inBlock++; if (!canInsertTag(t, attr, true)) { return; } if (attr.isDefined(IMPLIED)) { attr.removeAttribute(IMPLIED); } lastWasNewline = false; attr.addAttribute(StyleConstants.NameAttribute, t); ElementSpec es = new ElementSpec( attr.copyAttributes(), ElementSpec.StartTagType); parseBuffer.addElement(es); }
Adds an instruction to the parse buffer to close out a block element of the given type.
Params:
  • t – the HTML tag
/** * Adds an instruction to the parse buffer to close out * a block element of the given type. * * @param t the HTML tag */
protected void blockClose(HTML.Tag t) { inBlock--; if (!foundInsertTag) { return; } // Add a new line, if the last character wasn't one. This is // needed for proper positioning of the cursor. addContent // with true will force an implied paragraph to be generated if // there isn't one. This may result in a rather bogus structure // (perhaps a table with a child pargraph), but the paragraph // is needed for proper positioning and display. if(!lastWasNewline) { pushCharacterStyle(); charAttr.addAttribute(IMPLIED_CR, Boolean.TRUE); addContent(NEWLINE, 0, 1, true); popCharacterStyle(); lastWasNewline = true; } if (impliedP) { impliedP = false; inParagraph = false; if (t != HTML.Tag.IMPLIED) { blockClose(HTML.Tag.IMPLIED); } } // an open/close with no content will be removed, so we // add a space of content to keep the element being formed. ElementSpec prev = (parseBuffer.size() > 0) ? parseBuffer.lastElement() : null; if (prev != null && prev.getType() == ElementSpec.StartTagType) { char[] one = new char[1]; one[0] = ' '; addContent(one, 0, 1); } ElementSpec es = new ElementSpec( null, ElementSpec.EndTagType); parseBuffer.addElement(es); }
Adds some text with the current character attributes.
Params:
  • data – the content to add
  • offs – the initial offset
  • length – the length
/** * Adds some text with the current character attributes. * * @param data the content to add * @param offs the initial offset * @param length the length */
protected void addContent(char[] data, int offs, int length) { addContent(data, offs, length, true); }
Adds some text with the current character attributes.
Params:
  • data – the content to add
  • offs – the initial offset
  • length – the length
  • generateImpliedPIfNecessary – whether to generate implied paragraphs
/** * Adds some text with the current character attributes. * * @param data the content to add * @param offs the initial offset * @param length the length * @param generateImpliedPIfNecessary whether to generate implied * paragraphs */
protected void addContent(char[] data, int offs, int length, boolean generateImpliedPIfNecessary) { if (!foundInsertTag) { return; } if (generateImpliedPIfNecessary && (! inParagraph) && (! inPre)) { blockOpen(HTML.Tag.IMPLIED, new SimpleAttributeSet()); inParagraph = true; impliedP = true; } emptyAnchor = false; charAttr.addAttribute(StyleConstants.NameAttribute, HTML.Tag.CONTENT); AttributeSet a = charAttr.copyAttributes(); ElementSpec es = new ElementSpec( a, ElementSpec.ContentType, data, offs, length); parseBuffer.addElement(es); if (parseBuffer.size() > threshold) { if ( threshold <= MaxThreshold ) { threshold *= StepThreshold; } try { flushBuffer(false); } catch (BadLocationException ble) { } } if(length > 0) { lastWasNewline = (data[offs + length - 1] == '\n'); } }
Adds content that is basically specified entirely in the attribute set.
Params:
  • t – an HTML tag
  • a – the attribute set
/** * Adds content that is basically specified entirely * in the attribute set. * * @param t an HTML tag * @param a the attribute set */
protected void addSpecialElement(HTML.Tag t, MutableAttributeSet a) { if ((t != HTML.Tag.FRAME) && (! inParagraph) && (! inPre)) { nextTagAfterPImplied = t; blockOpen(HTML.Tag.IMPLIED, new SimpleAttributeSet()); nextTagAfterPImplied = null; inParagraph = true; impliedP = true; } if (!canInsertTag(t, a, t.isBlock())) { return; } if (a.isDefined(IMPLIED)) { a.removeAttribute(IMPLIED); } emptyAnchor = false; a.addAttributes(charAttr); a.addAttribute(StyleConstants.NameAttribute, t); char[] one = new char[1]; one[0] = ' '; ElementSpec es = new ElementSpec( a.copyAttributes(), ElementSpec.ContentType, one, 0, 1); parseBuffer.addElement(es); // Set this to avoid generating a newline for frames, frames // shouldn't have any content, and shouldn't need a newline. if (t == HTML.Tag.FRAME) { lastWasNewline = true; } }
Flushes the current parse buffer into the document.
Params:
  • endOfStream – true if there is no more content to parser
/** * Flushes the current parse buffer into the document. * @param endOfStream true if there is no more content to parser */
void flushBuffer(boolean endOfStream) throws BadLocationException { int oldLength = HTMLDocument.this.getLength(); int size = parseBuffer.size(); if (endOfStream && (insertTag != null || insertAfterImplied) && size > 0) { adjustEndSpecsForPartialInsert(); size = parseBuffer.size(); } ElementSpec[] spec = new ElementSpec[size]; parseBuffer.copyInto(spec); if (oldLength == 0 && (insertTag == null && !insertAfterImplied)) { create(spec); } else { insert(offset, spec); } parseBuffer.removeAllElements(); offset += HTMLDocument.this.getLength() - oldLength; flushCount++; }
This will be invoked for the last flush, if insertTag is non null.
/** * This will be invoked for the last flush, if <code>insertTag</code> * is non null. */
private void adjustEndSpecsForPartialInsert() { int size = parseBuffer.size(); if (insertTagDepthDelta < 0) { // When inserting via an insertTag, the depths (of the tree // being read in, and existing hierarchy) may not match up. // This attemps to clean it up. int removeCounter = insertTagDepthDelta; while (removeCounter < 0 && size >= 0 && parseBuffer.elementAt(size - 1). getType() == ElementSpec.EndTagType) { parseBuffer.removeElementAt(--size); removeCounter++; } } if (flushCount == 0 && (!insertAfterImplied || !wantsTrailingNewline)) { // If this starts with content (or popDepth > 0 && // pushDepth > 0) and ends with EndTagTypes, make sure // the last content isn't a \n, otherwise will end up with // an extra \n in the middle of content. int index = 0; if (pushDepth > 0) { if (parseBuffer.elementAt(0).getType() == ElementSpec.ContentType) { index++; } } index += (popDepth + pushDepth); int cCount = 0; int cStart = index; while (index < size && parseBuffer.elementAt (index).getType() == ElementSpec.ContentType) { index++; cCount++; } if (cCount > 1) { while (index < size && parseBuffer.elementAt (index).getType() == ElementSpec.EndTagType) { index++; } if (index == size) { char[] lastText = parseBuffer.elementAt (cStart + cCount - 1).getArray(); if (lastText.length == 1 && lastText[0] == NEWLINE[0]){ index = cStart + cCount - 1; while (size > index) { parseBuffer.removeElementAt(--size); } } } } } if (wantsTrailingNewline) { // Make sure there is in fact a newline for (int counter = parseBuffer.size() - 1; counter >= 0; counter--) { ElementSpec spec = parseBuffer.elementAt(counter); if (spec.getType() == ElementSpec.ContentType) { if (spec.getArray()[spec.getLength() - 1] != '\n') { SimpleAttributeSet attrs =new SimpleAttributeSet(); attrs.addAttribute(StyleConstants.NameAttribute, HTML.Tag.CONTENT); parseBuffer.insertElementAt(new ElementSpec( attrs, ElementSpec.ContentType, NEWLINE, 0, 1), counter + 1); } break; } } } }
Adds the CSS rules in rules.
/** * Adds the CSS rules in <code>rules</code>. */
void addCSSRules(String rules) { StyleSheet ss = getStyleSheet(); ss.addRule(rules); }
Adds the CSS stylesheet at href to the known list of stylesheets.
/** * Adds the CSS stylesheet at <code>href</code> to the known list * of stylesheets. */
void linkCSSStyleSheet(String href) { URL url; try { url = new URL(base, href); } catch (MalformedURLException mfe) { try { url = new URL(href); } catch (MalformedURLException mfe2) { url = null; } } if (url != null) { getStyleSheet().importStyleSheet(url); } }
Returns true if can insert starting at t. This will return false if the insert tag is set, and hasn't been found yet.
/** * Returns true if can insert starting at <code>t</code>. This * will return false if the insert tag is set, and hasn't been found * yet. */
private boolean canInsertTag(HTML.Tag t, AttributeSet attr, boolean isBlockTag) { if (!foundInsertTag) { boolean needPImplied = ((t == HTML.Tag.IMPLIED) && (!inParagraph) && (!inPre)); if (needPImplied && (nextTagAfterPImplied != null)) { /* * If insertTag == null then just proceed to * foundInsertTag() call below and return true. */ if (insertTag != null) { boolean nextTagIsInsertTag = isInsertTag(nextTagAfterPImplied); if ( (! nextTagIsInsertTag) || (! insertInsertTag) ) { return false; } } /* * Proceed to foundInsertTag() call... */ } else if ((insertTag != null && !isInsertTag(t)) || (insertAfterImplied && (attr == null || attr.isDefined(IMPLIED) || t == HTML.Tag.IMPLIED ) ) ) { return false; } // Allow the insert if t matches the insert tag, or // insertAfterImplied is true and the element is implied. foundInsertTag(isBlockTag); if (!insertInsertTag) { return false; } } return true; } private boolean isInsertTag(HTML.Tag tag) { return (insertTag == tag); } private void foundInsertTag(boolean isBlockTag) { foundInsertTag = true; if (!insertAfterImplied && (popDepth > 0 || pushDepth > 0)) { try { if (offset == 0 || !getText(offset - 1, 1).equals("\n")) { // Need to insert a newline. AttributeSet newAttrs = null; boolean joinP = true; if (offset != 0) { // Determine if we can use JoinPrevious, we can't // if the Element has some attributes that are // not meant to be duplicated. Element charElement = getCharacterElement (offset - 1); AttributeSet attrs = charElement.getAttributes(); if (attrs.isDefined(StyleConstants. ComposedTextAttribute)) { joinP = false; } else { Object name = attrs.getAttribute (StyleConstants.NameAttribute); if (name instanceof HTML.Tag) { HTML.Tag tag = (HTML.Tag)name; if (tag == HTML.Tag.IMG || tag == HTML.Tag.HR || tag == HTML.Tag.COMMENT || (tag instanceof HTML.UnknownTag)) { joinP = false; } } } } if (!joinP) { // If not joining with the previous element, be // sure and set the name (otherwise it will be // inherited). newAttrs = new SimpleAttributeSet(); ((SimpleAttributeSet)newAttrs).addAttribute (StyleConstants.NameAttribute, HTML.Tag.CONTENT); } ElementSpec es = new ElementSpec(newAttrs, ElementSpec.ContentType, NEWLINE, 0, NEWLINE.length); if (joinP) { es.setDirection(ElementSpec. JoinPreviousDirection); } parseBuffer.addElement(es); } } catch (BadLocationException ble) {} } // pops for (int counter = 0; counter < popDepth; counter++) { parseBuffer.addElement(new ElementSpec(null, ElementSpec. EndTagType)); } // pushes for (int counter = 0; counter < pushDepth; counter++) { ElementSpec es = new ElementSpec(null, ElementSpec. StartTagType); es.setDirection(ElementSpec.JoinNextDirection); parseBuffer.addElement(es); } insertTagDepthDelta = depthTo(Math.max(0, offset - 1)) - popDepth + pushDepth - inBlock; if (isBlockTag) { // A start spec will be added (for this tag), so we account // for it here. insertTagDepthDelta++; } else { // An implied paragraph close (end spec) is going to be added, // so we account for it here. insertTagDepthDelta--; inParagraph = true; lastWasNewline = false; } }
This is set to true when and end is invoked for <html>.
/** * This is set to true when and end is invoked for {@literal <html>}. */
private boolean receivedEndHTML;
Number of times flushBuffer has been invoked.
/** Number of times <code>flushBuffer</code> has been invoked. */
private int flushCount;
If true, behavior is similar to insertTag, but instead of waiting for insertTag will wait for first Element without an 'implied' attribute and begin inserting then.
/** If true, behavior is similar to insertTag, but instead of * waiting for insertTag will wait for first Element without * an 'implied' attribute and begin inserting then. */
private boolean insertAfterImplied;
This is only used if insertAfterImplied is true. If false, only inserting content, and there is a trailing newline it is removed.
/** This is only used if insertAfterImplied is true. If false, only * inserting content, and there is a trailing newline it is removed. */
private boolean wantsTrailingNewline; int threshold; int offset; boolean inParagraph = false; boolean impliedP = false; boolean inPre = false; boolean inTextArea = false; TextAreaDocument textAreaDocument = null; boolean inTitle = false; boolean lastWasNewline = true; boolean emptyAnchor;
True if (!emptyDocument && insertTag == null), this is used so much it is cached.
/** True if (!emptyDocument &amp;&amp; insertTag == null), this is used so * much it is cached. */
boolean midInsert;
True when the body has been encountered.
/** True when the body has been encountered. */
boolean inBody;
If non null, gives parent Tag that insert is to happen at.
/** If non null, gives parent Tag that insert is to happen at. */
HTML.Tag insertTag;
If true, the insertTag is inserted, otherwise elements after the insertTag is found are inserted.
/** If true, the insertTag is inserted, otherwise elements after * the insertTag is found are inserted. */
boolean insertInsertTag;
Set to true when insertTag has been found.
/** Set to true when insertTag has been found. */
boolean foundInsertTag;
When foundInsertTag is set to true, this will be updated to reflect the delta between the two structures. That is, it will be the depth the inserts are happening at minus the depth of the tags being passed in. A value of 0 (the common case) indicates the structures match, a value greater than 0 indicates the insert is happening at a deeper depth than the stream is parsing, and a value less than 0 indicates the insert is happening earlier in the tree that the parser thinks and that we will need to remove EndTagType specs in the flushBuffer method.
/** When foundInsertTag is set to true, this will be updated to * reflect the delta between the two structures. That is, it * will be the depth the inserts are happening at minus the * depth of the tags being passed in. A value of 0 (the common * case) indicates the structures match, a value greater than 0 indicates * the insert is happening at a deeper depth than the stream is * parsing, and a value less than 0 indicates the insert is happening earlier * in the tree that the parser thinks and that we will need to remove * EndTagType specs in the flushBuffer method. */
int insertTagDepthDelta;
How many parents to ascend before insert new elements.
/** How many parents to ascend before insert new elements. */
int popDepth;
How many parents to descend (relative to popDepth) before inserting.
/** How many parents to descend (relative to popDepth) before * inserting. */
int pushDepth;
Last Map that was encountered.
/** Last Map that was encountered. */
Map lastMap;
Set to true when a style element is encountered.
/** Set to true when a style element is encountered. */
boolean inStyle = false;
Name of style to use. Obtained from Meta tag.
/** Name of style to use. Obtained from Meta tag. */
String defaultStyle;
Vector describing styles that should be include. Will consist of a bunch of HTML.Tags, which will either be:

LINK: in which case it is followed by an AttributeSet

STYLE: in which case the following element is a String indicating the type (may be null), and the elements following it until the next HTML.Tag are the rules as Strings.

/** Vector describing styles that should be include. Will consist * of a bunch of HTML.Tags, which will either be: * <p>LINK: in which case it is followed by an AttributeSet * <p>STYLE: in which case the following element is a String * indicating the type (may be null), and the elements following * it until the next HTML.Tag are the rules as Strings. */
Vector<Object> styles;
True if inside the head tag.
/** True if inside the head tag. */
boolean inHead = false;
Set to true if the style language is text/css. Since this is used alot, it is cached.
/** Set to true if the style language is text/css. Since this is * used alot, it is cached. */
boolean isStyleCSS;
True if inserting into an empty document.
/** True if inserting into an empty document. */
boolean emptyDocument;
Attributes from a style Attribute.
/** Attributes from a style Attribute. */
AttributeSet styleAttributes;
Current option, if in an option element (needed to load the label.
/** * Current option, if in an option element (needed to * load the label. */
Option option;
Buffer to keep building elements.
/** * Buffer to keep building elements. */
protected Vector<ElementSpec> parseBuffer = new Vector<ElementSpec>();
Current character attribute set.
/** * Current character attribute set. */
protected MutableAttributeSet charAttr = new TaggedAttributeSet(); Stack<AttributeSet> charAttrStack = new Stack<AttributeSet>(); Hashtable<HTML.Tag, TagAction> tagMap; int inBlock = 0;
This attribute is sometimes used to refer to next tag to be handled after p-implied when the latter is the current tag which is being handled.
/** * This attribute is sometimes used to refer to next tag * to be handled after p-implied when the latter is * the current tag which is being handled. */
private HTML.Tag nextTagAfterPImplied = null; }
Used by StyleSheet to determine when to avoid removing HTML.Tags matching StyleConstants.
/** * Used by StyleSheet to determine when to avoid removing HTML.Tags * matching StyleConstants. */
static class TaggedAttributeSet extends SimpleAttributeSet { TaggedAttributeSet() { super(); } }
An element that represents a chunk of text that has a set of HTML character level attributes assigned to it.
/** * An element that represents a chunk of text that has * a set of HTML character level attributes assigned to * it. */
public class RunElement extends LeafElement {
Constructs an element that represents content within the document (has no children).
Params:
  • parent – the parent element
  • a – the element attributes
  • offs0 – the start offset (must be at least 0)
  • offs1 – the end offset (must be at least offs0)
Since:1.4
/** * Constructs an element that represents content within the * document (has no children). * * @param parent the parent element * @param a the element attributes * @param offs0 the start offset (must be at least 0) * @param offs1 the end offset (must be at least offs0) * @since 1.4 */
public RunElement(Element parent, AttributeSet a, int offs0, int offs1) { super(parent, a, offs0, offs1); }
Gets the name of the element.
Returns:the name, null if none
/** * Gets the name of the element. * * @return the name, null if none */
public String getName() { Object o = getAttribute(StyleConstants.NameAttribute); if (o != null) { return o.toString(); } return super.getName(); }
Gets the resolving parent. HTML attributes are not inherited at the model level so we override this to return null.
See Also:
Returns:null, there are none
/** * Gets the resolving parent. HTML attributes are not inherited * at the model level so we override this to return null. * * @return null, there are none * @see AttributeSet#getResolveParent */
public AttributeSet getResolveParent() { return null; } }
An element that represents a structural block of HTML.
/** * An element that represents a structural <em>block</em> of * HTML. */
public class BlockElement extends BranchElement {
Constructs a composite element that initially contains no children.
Params:
  • parent – the parent element
  • a – the attributes for the element
Since:1.4
/** * Constructs a composite element that initially contains * no children. * * @param parent the parent element * @param a the attributes for the element * @since 1.4 */
public BlockElement(Element parent, AttributeSet a) { super(parent, a); }
Gets the name of the element.
Returns:the name, null if none
/** * Gets the name of the element. * * @return the name, null if none */
public String getName() { Object o = getAttribute(StyleConstants.NameAttribute); if (o != null) { return o.toString(); } return super.getName(); }
Gets the resolving parent. HTML attributes are not inherited at the model level so we override this to return null.
See Also:
Returns:null, there are none
/** * Gets the resolving parent. HTML attributes are not inherited * at the model level so we override this to return null. * * @return null, there are none * @see AttributeSet#getResolveParent */
public AttributeSet getResolveParent() { return null; } }
Document that allows you to set the maximum length of the text.
/** * Document that allows you to set the maximum length of the text. */
private static class FixedLengthDocument extends PlainDocument { private int maxLength; public FixedLengthDocument(int maxLength) { this.maxLength = maxLength; } public void insertString(int offset, String str, AttributeSet a) throws BadLocationException { if (str != null && str.length() + getLength() <= maxLength) { super.insertString(offset, str, a); } } } }