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

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


0001        /*
0002         * Copyright 1997-2006 Sun Microsystems, Inc.  All Rights Reserved.
0003         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004         *
0005         * This code is free software; you can redistribute it and/or modify it
0006         * under the terms of the GNU General Public License version 2 only, as
0007         * published by the Free Software Foundation.  Sun designates this
0008         * particular file as subject to the "Classpath" exception as provided
0009         * by Sun in the LICENSE file that accompanied this code.
0010         *
0011         * This code is distributed in the hope that it will be useful, but WITHOUT
0012         * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013         * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
0014         * version 2 for more details (a copy is included in the LICENSE file that
0015         * accompanied this code).
0016         *
0017         * You should have received a copy of the GNU General Public License version
0018         * 2 along with this work; if not, write to the Free Software Foundation,
0019         * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020         *
0021         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022         * CA 95054 USA or visit www.sun.com if you need additional information or
0023         * have any questions.
0024         */
0025        package javax.swing.text.html;
0026
0027        import java.awt.Color;
0028        import java.awt.Component;
0029        import java.awt.font.TextAttribute;
0030        import java.util.*;
0031        import java.net.URL;
0032        import java.net.URLEncoder;
0033        import java.net.MalformedURLException;
0034        import java.io.*;
0035        import javax.swing.*;
0036        import javax.swing.event.*;
0037        import javax.swing.text.*;
0038        import javax.swing.undo.*;
0039        import java.text.Bidi;
0040        import sun.swing.SwingUtilities2;
0041
0042        /**
0043         * A document that models HTML.  The purpose of this model is to
0044         * support both browsing and editing.  As a result, the structure
0045         * described by an HTML document is not exactly replicated by default.
0046         * The element structure that is modeled by default, is built by the
0047         * class <code>HTMLDocument.HTMLReader</code>, which implements the
0048         * <code>HTMLEditorKit.ParserCallback</code> protocol that the parser
0049         * expects.  To change the structure one can subclass
0050         * <code>HTMLReader</code>, and reimplement the method {@link
0051         * #getReader(int)} to return the new reader implementation.  The
0052         * documentation for <code>HTMLReader</code> should be consulted for
0053         * the details of the default structure created.  The intent is that
0054         * the document be non-lossy (although reproducing the HTML format may
0055         * result in a different format).
0056         *
0057         * <p>The document models only HTML, and makes no attempt to store
0058         * view attributes in it.  The elements are identified by the
0059         * <code>StyleContext.NameAttribute</code> attribute, which should
0060         * always have a value of type <code>HTML.Tag</code> that identifies
0061         * the kind of element.  Some of the elements (such as comments) are
0062         * synthesized.  The <code>HTMLFactory</code> uses this attribute to
0063         * determine what kind of view to build.</p>
0064         *
0065         * <p>This document supports incremental loading.  The
0066         * <code>TokenThreshold</code> property controls how much of the parse
0067         * is buffered before trying to update the element structure of the
0068         * document.  This property is set by the <code>EditorKit</code> so
0069         * that subclasses can disable it.</p>
0070         *
0071         * <p>The <code>Base</code> property determines the URL against which
0072         * relative URLs are resolved.  By default, this will be the
0073         * <code>Document.StreamDescriptionProperty</code> if the value of the
0074         * property is a URL.  If a &lt;BASE&gt; tag is encountered, the base
0075         * will become the URL specified by that tag.  Because the base URL is
0076         * a property, it can of course be set directly.</p>
0077         *
0078         * <p>The default content storage mechanism for this document is a gap
0079         * buffer (<code>GapContent</code>).  Alternatives can be supplied by
0080         * using the constructor that takes a <code>Content</code>
0081         * implementation.</p>
0082         *
0083         * <h2>Modifying HTMLDocument</h2>
0084         *
0085         * <p>In addition to the methods provided by Document and
0086         * StyledDocument for mutating an HTMLDocument, HTMLDocument provides
0087         * a number of convenience methods.  The following methods can be used
0088         * to insert HTML content into an existing document.</p>
0089         *
0090         * <ul>
0091         *   <li>{@link #setInnerHTML(Element, String)}</li>
0092         *   <li>{@link #setOuterHTML(Element, String)}</li>
0093         *   <li>{@link #insertBeforeStart(Element, String)}</li>
0094         *   <li>{@link #insertAfterStart(Element, String)}</li>
0095         *   <li>{@link #insertBeforeEnd(Element, String)}</li>
0096         *   <li>{@link #insertAfterEnd(Element, String)}</li>
0097         * </ul>
0098         *
0099         * <p>The following examples illustrate using these methods.  Each
0100         * example assumes the HTML document is initialized in the following
0101         * way:</p>
0102         *
0103         * <pre>
0104         * JEditorPane p = new JEditorPane();
0105         * p.setContentType("text/html");
0106         * p.setText("..."); // Document text is provided below.
0107         * HTMLDocument d = (HTMLDocument) p.getDocument();
0108         * </pre>
0109         *
0110         * <p>With the following HTML content:</p>
0111         *
0112         * <pre>
0113         * &lt;html>
0114         *   &lt;head>
0115         *     &lt;title>An example HTMLDocument&lt;/title>
0116         *     &lt;style type="text/css">
0117         *       div { background-color: silver; }
0118         *       ul { color: red; }
0119         *     &lt;/style>
0120         *   &lt;/head>
0121         *   &lt;body>
0122         *     &lt;div id="BOX">
0123         *       &lt;p>Paragraph 1&lt;/p>
0124         *       &lt;p>Paragraph 2&lt;/p>
0125         *     &lt;/div>
0126         *   &lt;/body>
0127         * &lt;/html>
0128         * </pre>
0129         *
0130         * <p>All the methods for modifying an HTML document require an {@link
0131         * Element}.  Elements can be obtained from an HTML document by using
0132         * the method {@link #getElement(Element e, Object attribute, Object
0133         * value)}.  It returns the first descendant element that contains the
0134         * specified attribute with the given value, in depth-first order.
0135         * For example, <code>d.getElement(d.getDefaultRootElement(),
0136         * StyleConstants.NameAttribute, HTML.Tag.P)</code> returns the first
0137         * paragraph element.</p>
0138         *
0139         * <p>A convenient shortcut for locating elements is the method {@link
0140         * #getElement(String)}; returns an element whose <code>ID</code>
0141         * attribute matches the specified value.  For example,
0142         * <code>d.getElement("BOX")</code> returns the <code>DIV</code>
0143         * element.</p>
0144         *
0145         * <p>The {@link #getIterator(HTML.Tag t)} method can also be used for
0146         * finding all occurrences of the specified HTML tag in the
0147         * document.</p>
0148         *
0149         * <h3>Inserting elements</h3>
0150         *
0151         * <p>Elements can be inserted before or after the existing children
0152         * of any non-leaf element by using the methods
0153         * <code>insertAfterStart</code> and <code>insertBeforeEnd</code>.
0154         * For example, if <code>e</code> is the <code>DIV</code> element,
0155         * <code>d.insertAfterStart(e, "&lt;ul>&lt;li>List
0156         * Item&lt;/li>&lt;/ul>")</code> inserts the list before the first
0157         * paragraph, and <code>d.insertBeforeEnd(e, "&lt;ul>&lt;li>List
0158         * Item&lt;/li>&lt;/ul>")</code> inserts the list after the last
0159         * paragraph.  The <code>DIV</code> block becomes the parent of the
0160         * newly inserted elements.</p>
0161         *
0162         * <p>Sibling elements can be inserted before or after any element by
0163         * using the methods <code>insertBeforeStart</code> and
0164         * <code>insertAfterEnd</code>.  For example, if <code>e</code> is the
0165         * <code>DIV</code> element, <code>d.insertBeforeStart(e,
0166         * "&lt;ul>&lt;li>List Item&lt;/li>&lt;/ul>")</code> inserts the list
0167         * before the <code>DIV</code> element, and <code>d.insertAfterEnd(e,
0168         * "&lt;ul>&lt;li>List Item&lt;/li>&lt;/ul>")</code> inserts the list
0169         * after the <code>DIV</code> element.  The newly inserted elements
0170         * become siblings of the <code>DIV</code> element.</p>
0171         *
0172         * <h3>Replacing elements</h3>
0173         *
0174         * <p>Elements and all their descendants can be replaced by using the
0175         * methods <code>setInnerHTML</code> and <code>setOuterHTML</code>.
0176         * For example, if <code>e</code> is the <code>DIV</code> element,
0177         * <code>d.setInnerHTML(e, "&lt;ul>&lt;li>List
0178         * Item&lt;/li>&lt;/ul>")</code> replaces all children paragraphs with
0179         * the list, and <code>d.setOuterHTML(e, "&lt;ul>&lt;li>List
0180         * Item&lt;/li>&lt;/ul>")</code> replaces the <code>DIV</code> element
0181         * itself.  In latter case the parent of the list is the
0182         * <code>BODY</code> element.
0183         *
0184         * <h3>Summary</h3>
0185         *
0186         * <p>The following table shows the example document and the results
0187         * of various methods described above.</p>
0188         *
0189         * <table border=1 cellspacing=0>
0190         *   <tr>
0191         *     <th>Example</th>
0192         *     <th><code>insertAfterStart</code></th>
0193         *     <th><code>insertBeforeEnd</code></th>
0194         *     <th><code>insertBeforeStart</code></th>
0195         *     <th><code>insertAfterEnd</code></th>
0196         *     <th><code>setInnerHTML</code></th>
0197         *     <th><code>setOuterHTML</code></th>
0198         *   </tr>
0199         *   <tr valign="top">
0200         *     <td nowrap="nowrap">
0201         *       <div style="background-color: silver;">
0202         *         <p>Paragraph 1</p>
0203         *         <p>Paragraph 2</p>
0204         *       </div>
0205         *     </td>
0206         * <!--insertAfterStart-->
0207         *     <td nowrap="nowrap">
0208         *       <div style="background-color: silver;">
0209         *         <ul style="color: red;">
0210         *           <li>List Item</li>
0211         *         </ul>
0212         *         <p>Paragraph 1</p>
0213         *         <p>Paragraph 2</p>
0214         *       </div>
0215         *     </td>
0216         * <!--insertBeforeEnd-->
0217         *     <td nowrap="nowrap">
0218         *       <div style="background-color: silver;">
0219         *         <p>Paragraph 1</p>
0220         *         <p>Paragraph 2</p>
0221         *         <ul style="color: red;">
0222         *           <li>List Item</li>
0223         *         </ul>
0224         *       </div>
0225         *     </td>
0226         * <!--insertBeforeStart-->
0227         *     <td nowrap="nowrap">
0228         *       <ul style="color: red;">
0229         *         <li>List Item</li>
0230         *       </ul>
0231         *       <div style="background-color: silver;">
0232         *         <p>Paragraph 1</p>
0233         *         <p>Paragraph 2</p>
0234         *       </div>
0235         *     </td>
0236         * <!--insertAfterEnd-->
0237         *     <td nowrap="nowrap">
0238         *       <div style="background-color: silver;">
0239         *         <p>Paragraph 1</p>
0240         *         <p>Paragraph 2</p>
0241         *       </div>
0242         *       <ul style="color: red;">
0243         *         <li>List Item</li>
0244         *       </ul>
0245         *     </td>
0246         * <!--setInnerHTML-->
0247         *     <td nowrap="nowrap">
0248         *       <div style="background-color: silver;">
0249         *         <ul style="color: red;">
0250         *           <li>List Item</li>
0251         *         </ul>
0252         *       </div>
0253         *     </td>
0254         * <!--setOuterHTML-->
0255         *     <td nowrap="nowrap">
0256         *       <ul style="color: red;">
0257         *         <li>List Item</li>
0258         *       </ul>
0259         *     </td>
0260         *   </tr>
0261         * </table>
0262         *
0263         * <p><strong>Warning:</strong> Serialized objects of this class will
0264         * not be compatible with future Swing releases. The current
0265         * serialization support is appropriate for short term storage or RMI
0266         * between applications running the same version of Swing.  As of 1.4,
0267         * support for long term storage of all JavaBeans<sup><font
0268         * size="-2">TM</font></sup> has been added to the
0269         * <code>java.beans</code> package.  Please see {@link
0270         * java.beans.XMLEncoder}.</p>
0271         *
0272         * @author  Timothy Prinzing
0273         * @author  Scott Violet
0274         * @author  Sunita Mani
0275         * @version 1.187 05/05/07
0276         */
0277        public class HTMLDocument extends DefaultStyledDocument {
0278            /**
0279             * Constructs an HTML document using the default buffer size
0280             * and a default <code>StyleSheet</code>.  This is a convenience
0281             * method for the constructor
0282             * <code>HTMLDocument(Content, StyleSheet)</code>.
0283             */
0284            public HTMLDocument() {
0285                this (new GapContent(BUFFER_SIZE_DEFAULT), new StyleSheet());
0286            }
0287
0288            /**
0289             * Constructs an HTML document with the default content
0290             * storage implementation and the specified style/attribute
0291             * storage mechanism.  This is a convenience method for the
0292             * constructor
0293             * <code>HTMLDocument(Content, StyleSheet)</code>.
0294             *
0295             * @param styles  the styles
0296             */
0297            public HTMLDocument(StyleSheet styles) {
0298                this (new GapContent(BUFFER_SIZE_DEFAULT), styles);
0299            }
0300
0301            /**
0302             * Constructs an HTML document with the given content
0303             * storage implementation and the given style/attribute
0304             * storage mechanism.
0305             *
0306             * @param c  the container for the content
0307             * @param styles the styles
0308             */
0309            public HTMLDocument(Content c, StyleSheet styles) {
0310                super (c, styles);
0311            }
0312
0313            /**
0314             * Fetches the reader for the parser to use when loading the document
0315             * with HTML.  This is implemented to return an instance of
0316             * <code>HTMLDocument.HTMLReader</code>. 
0317             * Subclasses can reimplement this
0318             * method to change how the document gets structured if desired.
0319             * (For example, to handle custom tags, or structurally represent character
0320             * style elements.)
0321             *
0322             * @param pos the starting position
0323             * @return the reader used by the parser to load the document
0324             */
0325            public HTMLEditorKit.ParserCallback getReader(int pos) {
0326                Object desc = getProperty(Document.StreamDescriptionProperty);
0327                if (desc instanceof  URL) {
0328                    setBase((URL) desc);
0329                }
0330                HTMLReader reader = new HTMLReader(pos);
0331                return reader;
0332            }
0333
0334            /**
0335             * Returns the reader for the parser to use to load the document
0336             * with HTML.  This is implemented to return an instance of
0337             * <code>HTMLDocument.HTMLReader</code>. 
0338             * Subclasses can reimplement this
0339             * method to change how the document gets structured if desired.
0340             * (For example, to handle custom tags, or structurally represent character
0341             * style elements.) 
0342             * <p>This is a convenience method for 
0343             * <code>getReader(int, int, int, HTML.Tag, TRUE)</code>.
0344             *
0345             * @param popDepth   the number of <code>ElementSpec.EndTagTypes</code>
0346             *		to generate before inserting
0347             * @param pushDepth  the number of <code>ElementSpec.StartTagTypes</code>
0348             *		with a direction of <code>ElementSpec.JoinNextDirection</code>
0349             *		that should be generated before inserting,
0350             *		but after the end tags have been generated
0351             * @param insertTag  the first tag to start inserting into document
0352             * @return the reader used by the parser to load the document
0353             */
0354            public HTMLEditorKit.ParserCallback getReader(int pos,
0355                    int popDepth, int pushDepth, HTML.Tag insertTag) {
0356                return getReader(pos, popDepth, pushDepth, insertTag, true);
0357            }
0358
0359            /**
0360             * Fetches the reader for the parser to use to load the document
0361             * with HTML.  This is implemented to return an instance of
0362             * HTMLDocument.HTMLReader.  Subclasses can reimplement this
0363             * method to change how the document get structured if desired
0364             * (e.g. to handle custom tags, structurally represent character
0365             * style elements, etc.).
0366             *
0367             * @param popDepth   the number of <code>ElementSpec.EndTagTypes</code>
0368             *		to generate before inserting
0369             * @param pushDepth  the number of <code>ElementSpec.StartTagTypes</code>
0370             *		with a direction of <code>ElementSpec.JoinNextDirection</code>
0371             *		that should be generated before inserting,
0372             *		but after the end tags have been generated
0373             * @param insertTag  the first tag to start inserting into document
0374             * @param insertInsertTag  false if all the Elements after insertTag should
0375             *        be inserted; otherwise insertTag will be inserted
0376             * @return the reader used by the parser to load the document
0377             */
0378            HTMLEditorKit.ParserCallback getReader(int pos, int popDepth,
0379                    int pushDepth, HTML.Tag insertTag, boolean insertInsertTag) {
0380                Object desc = getProperty(Document.StreamDescriptionProperty);
0381                if (desc instanceof  URL) {
0382                    setBase((URL) desc);
0383                }
0384                HTMLReader reader = new HTMLReader(pos, popDepth, pushDepth,
0385                        insertTag, insertInsertTag, false, true);
0386                return reader;
0387            }
0388
0389            /**
0390             * Returns the location to resolve relative URLs against.  By
0391             * default this will be the document's URL if the document
0392             * was loaded from a URL.  If a base tag is found and
0393             * can be parsed, it will be used as the base location.
0394             *
0395             * @return the base location
0396             */
0397            public URL getBase() {
0398                return base;
0399            }
0400
0401            /**
0402             * Sets the location to resolve relative URLs against.  By
0403             * default this will be the document's URL if the document
0404             * was loaded from a URL.  If a base tag is found and
0405             * can be parsed, it will be used as the base location.
0406             * <p>This also sets the base of the <code>StyleSheet</code>
0407             * to be <code>u</code> as well as the base of the document.
0408             *
0409             * @param u  the desired base URL
0410             */
0411            public void setBase(URL u) {
0412                base = u;
0413                getStyleSheet().setBase(u);
0414            }
0415
0416            /**
0417             * Inserts new elements in bulk.  This is how elements get created
0418             * in the document.  The parsing determines what structure is needed
0419             * and creates the specification as a set of tokens that describe the
0420             * edit while leaving the document free of a write-lock.  This method
0421             * can then be called in bursts by the reader to acquire a write-lock
0422             * for a shorter duration (i.e. while the document is actually being
0423             * altered).
0424             *
0425             * @param offset the starting offset
0426             * @param data the element data
0427             * @exception BadLocationException  if the given position does not 
0428             *   represent a valid location in the associated document.
0429             */
0430            protected void insert(int offset, ElementSpec[] data)
0431                    throws BadLocationException {
0432                super .insert(offset, data);
0433            }
0434
0435            /**
0436             * Updates document structure as a result of text insertion.  This
0437             * will happen within a write lock.  This implementation simply
0438             * parses the inserted content for line breaks and builds up a set
0439             * of instructions for the element buffer.
0440             *
0441             * @param chng a description of the document change
0442             * @param attr the attributes
0443             */
0444            protected void insertUpdate(DefaultDocumentEvent chng,
0445                    AttributeSet attr) {
0446                if (attr == null) {
0447                    attr = contentAttributeSet;
0448                }
0449
0450                // If this is the composed text element, merge the content attribute to it
0451                else if (attr.isDefined(StyleConstants.ComposedTextAttribute)) {
0452                    ((MutableAttributeSet) attr)
0453                            .addAttributes(contentAttributeSet);
0454                }
0455
0456                if (attr.isDefined(IMPLIED_CR)) {
0457                    ((MutableAttributeSet) attr).removeAttribute(IMPLIED_CR);
0458                }
0459
0460                super .insertUpdate(chng, attr);
0461            }
0462
0463            /**
0464             * Replaces the contents of the document with the given
0465             * element specifications.  This is called before insert if
0466             * the loading is done in bursts.  This is the only method called
0467             * if loading the document entirely in one burst.
0468             *
0469             * @param data  the new contents of the document
0470             */
0471            protected void create(ElementSpec[] data) {
0472                super .create(data);
0473            }
0474
0475            /**
0476             * Sets attributes for a paragraph.
0477             * <p>
0478             * This method is thread safe, although most Swing methods
0479             * are not. Please see 
0480             * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How
0481             * to Use Threads</A> for more information.     
0482             *
0483             * @param offset the offset into the paragraph (must be at least 0)
0484             * @param length the number of characters affected (must be at least 0)
0485             * @param s the attributes
0486             * @param replace whether to replace existing attributes, or merge them
0487             */
0488            public void setParagraphAttributes(int offset, int length,
0489                    AttributeSet s, boolean replace) {
0490                try {
0491                    writeLock();
0492                    // Make sure we send out a change for the length of the paragraph.
0493                    int end = Math.min(offset + length, getLength());
0494                    Element e = getParagraphElement(offset);
0495                    offset = e.getStartOffset();
0496                    e = getParagraphElement(end);
0497                    length = Math.max(0, e.getEndOffset() - offset);
0498                    DefaultDocumentEvent changes = new DefaultDocumentEvent(
0499                            offset, length, DocumentEvent.EventType.CHANGE);
0500                    AttributeSet sCopy = s.copyAttributes();
0501                    int lastEnd = Integer.MAX_VALUE;
0502                    for (int pos = offset; pos <= end; pos = lastEnd) {
0503                        Element paragraph = getParagraphElement(pos);
0504                        if (lastEnd == paragraph.getEndOffset()) {
0505                            lastEnd++;
0506                        } else {
0507                            lastEnd = paragraph.getEndOffset();
0508                        }
0509                        MutableAttributeSet attr = (MutableAttributeSet) paragraph
0510                                .getAttributes();
0511                        changes.addEdit(new AttributeUndoableEdit(paragraph,
0512                                sCopy, replace));
0513                        if (replace) {
0514                            attr.removeAttributes(attr);
0515                        }
0516                        attr.addAttributes(s);
0517                    }
0518                    changes.end();
0519                    fireChangedUpdate(changes);
0520                    fireUndoableEditUpdate(new UndoableEditEvent(this , changes));
0521                } finally {
0522                    writeUnlock();
0523                }
0524            }
0525
0526            /**
0527             * Fetches the <code>StyleSheet</code> with the document-specific display
0528             * rules (CSS) that were specified in the HTML document itself.
0529             *
0530             * @return the <code>StyleSheet</code>
0531             */
0532            public StyleSheet getStyleSheet() {
0533                return (StyleSheet) getAttributeContext();
0534            }
0535
0536            /**
0537             * Fetches an iterator for the specified HTML tag.
0538             * This can be used for things like iterating over the
0539             * set of anchors contained, or iterating over the input
0540             * elements.
0541             *
0542             * @param t the requested <code>HTML.Tag</code>
0543             * @return the <code>Iterator</code> for the given HTML tag
0544             * @see javax.swing.text.html.HTML.Tag
0545             */
0546            public Iterator getIterator(HTML.Tag t) {
0547                if (t.isBlock()) {
0548                    // TBD
0549                    return null;
0550                }
0551                return new LeafIterator(t, this );
0552            }
0553
0554            /**
0555             * Creates a document leaf element that directly represents
0556             * text (doesn't have any children).  This is implemented
0557             * to return an element of type 
0558             * <code>HTMLDocument.RunElement</code>.
0559             *
0560             * @param parent the parent element
0561             * @param a the attributes for the element
0562             * @param p0 the beginning of the range (must be at least 0)
0563             * @param p1 the end of the range (must be at least p0)
0564             * @return the new element
0565             */
0566            protected Element createLeafElement(Element parent, AttributeSet a,
0567                    int p0, int p1) {
0568                return new RunElement(parent, a, p0, p1);
0569            }
0570
0571            /**
0572             * Creates a document branch element, that can contain other elements.
0573             * This is implemented to return an element of type 
0574             * <code>HTMLDocument.BlockElement</code>.
0575             *
0576             * @param parent the parent element
0577             * @param a the attributes
0578             * @return the element
0579             */
0580            protected Element createBranchElement(Element parent, AttributeSet a) {
0581                return new BlockElement(parent, a);
0582            }
0583
0584            /**
0585             * Creates the root element to be used to represent the
0586             * default document structure.
0587             *
0588             * @return the element base
0589             */
0590            protected AbstractElement createDefaultRoot() {
0591                // grabs a write-lock for this initialization and
0592                // abandon it during initialization so in normal
0593                // operation we can detect an illegitimate attempt
0594                // to mutate attributes.
0595                writeLock();
0596                MutableAttributeSet a = new SimpleAttributeSet();
0597                a.addAttribute(StyleConstants.NameAttribute, HTML.Tag.HTML);
0598                BlockElement html = new BlockElement(null, a.copyAttributes());
0599                a.removeAttributes(a);
0600                a.addAttribute(StyleConstants.NameAttribute, HTML.Tag.BODY);
0601                BlockElement body = new BlockElement(html, a.copyAttributes());
0602                a.removeAttributes(a);
0603                a.addAttribute(StyleConstants.NameAttribute, HTML.Tag.P);
0604                getStyleSheet().addCSSAttributeFromHTML(a,
0605                        CSS.Attribute.MARGIN_TOP, "0");
0606                BlockElement paragraph = new BlockElement(body, a
0607                        .copyAttributes());
0608                a.removeAttributes(a);
0609                a.addAttribute(StyleConstants.NameAttribute, HTML.Tag.CONTENT);
0610                RunElement brk = new RunElement(paragraph, a, 0, 1);
0611                Element[] buff = new Element[1];
0612                buff[0] = brk;
0613                paragraph.replace(0, 0, buff);
0614                buff[0] = paragraph;
0615                body.replace(0, 0, buff);
0616                buff[0] = body;
0617                html.replace(0, 0, buff);
0618                writeUnlock();
0619                return html;
0620            }
0621
0622            /**
0623             * Sets the number of tokens to buffer before trying to update
0624             * the documents element structure.
0625             *
0626             * @param n  the number of tokens to buffer
0627             */
0628            public void setTokenThreshold(int n) {
0629                putProperty(TokenThreshold, new Integer(n));
0630            }
0631
0632            /**
0633             * Gets the number of tokens to buffer before trying to update
0634             * the documents element structure.  The default value is
0635             * <code>Integer.MAX_VALUE</code>.
0636             *
0637             * @return the number of tokens to buffer
0638             */
0639            public int getTokenThreshold() {
0640                Integer i = (Integer) getProperty(TokenThreshold);
0641                if (i != null) {
0642                    return i.intValue();
0643                }
0644                return Integer.MAX_VALUE;
0645            }
0646
0647            /**
0648             * Determines how unknown tags are handled by the parser.
0649             * If set to true, unknown
0650             * tags are put in the model, otherwise they are dropped.
0651             *
0652             * @param preservesTags  true if unknown tags should be
0653             *		saved in the model, otherwise tags are dropped
0654             * @see javax.swing.text.html.HTML.Tag
0655             */
0656            public void setPreservesUnknownTags(boolean preservesTags) {
0657                preservesUnknownTags = preservesTags;
0658            }
0659
0660            /**
0661             * Returns the behavior the parser observes when encountering
0662             * unknown tags.
0663             *
0664             * @see javax.swing.text.html.HTML.Tag
0665             * @return true if unknown tags are to be preserved when parsing
0666             */
0667            public boolean getPreservesUnknownTags() {
0668                return preservesUnknownTags;
0669            }
0670
0671            /**
0672             * Processes <code>HyperlinkEvents</code> that
0673             * are generated by documents in an HTML frame. 
0674             * The <code>HyperlinkEvent</code> type, as the parameter suggests,
0675             * is <code>HTMLFrameHyperlinkEvent</code>.
0676             * In addition to the typical information contained in a
0677             * <code>HyperlinkEvent</code>,
0678             * this event contains the element that corresponds to the frame in
0679             * which the click happened (the source element) and the
0680             * target name.  The target name has 4 possible values:
0681             * <ul>
0682             * <li>  _self
0683             * <li>  _parent
0684             * <li>  _top
0685             * <li>  a named frame
0686             * </ul>
0687             *
0688             * If target is _self, the action is to change the value of the
0689             * <code>HTML.Attribute.SRC</code> attribute and fires a
0690             * <code>ChangedUpdate</code> event.
0691             *<p>
0692             * If the target is _parent, then it deletes the parent element,
0693             * which is a &lt;FRAMESET&gt; element, and inserts a new &lt;FRAME&gt;
0694             * element, and sets its <code>HTML.Attribute.SRC</code> attribute
0695             * to have a value equal to the destination URL and fire a
0696             * <code>RemovedUpdate</code> and <code>InsertUpdate</code>.
0697             *<p>
0698             * If the target is _top, this method does nothing. In the implementation
0699             * of the view for a frame, namely the <code>FrameView</code>,
0700             * the processing of _top is handled.  Given that _top implies
0701             * replacing the entire document, it made sense to handle this outside
0702             * of the document that it will replace.
0703             *<p>
0704             * If the target is a named frame, then the element hierarchy is searched
0705             * for an element with a name equal to the target, its
0706             * <code>HTML.Attribute.SRC</code> attribute is updated and a
0707             * <code>ChangedUpdate</code> event is fired.
0708             *
0709             * @param e the event
0710             */
0711            public void processHTMLFrameHyperlinkEvent(HTMLFrameHyperlinkEvent e) {
0712                String frameName = e.getTarget();
0713                Element element = e.getSourceElement();
0714                String urlStr = e.getURL().toString();
0715
0716                if (frameName.equals("_self")) {
0717                    /*
0718                      The source and destination elements
0719                      are the same.
0720                     */
0721                    updateFrame(element, urlStr);
0722                } else if (frameName.equals("_parent")) {
0723                    /*
0724                      The destination is the parent of the frame.
0725                     */
0726                    updateFrameSet(element.getParentElement(), urlStr);
0727                } else {
0728                    /*
0729                      locate a named frame
0730                     */
0731                    Element targetElement = findFrame(frameName);
0732                    if (targetElement != null) {
0733                        updateFrame(targetElement, urlStr);
0734                    }
0735                }
0736            }
0737
0738            /**
0739             * Searches the element hierarchy for an FRAME element
0740             * that has its name attribute equal to the <code>frameName</code>.
0741             *
0742             * @param frameName
0743             * @return the element whose NAME attribute has a value of
0744             *          <code>frameName</code>; returns <code>null</code>
0745             *		if not found
0746             */
0747            private Element findFrame(String frameName) {
0748                ElementIterator it = new ElementIterator(this );
0749                Element next = null;
0750
0751                while ((next = it.next()) != null) {
0752                    AttributeSet attr = next.getAttributes();
0753                    if (matchNameAttribute(attr, HTML.Tag.FRAME)) {
0754                        String frameTarget = (String) attr
0755                                .getAttribute(HTML.Attribute.NAME);
0756                        if (frameTarget != null
0757                                && frameTarget.equals(frameName)) {
0758                            break;
0759                        }
0760                    }
0761                }
0762                return next;
0763            }
0764
0765            /**
0766             * Returns true if <code>StyleConstants.NameAttribute</code> is
0767             * equal to the tag that is passed in as a parameter.
0768             *
0769             * @param attr the attributes to be matched
0770             * @param tag the value to be matched
0771             * @return true if there is a match, false otherwise
0772             * @see javax.swing.text.html.HTML.Attribute
0773             */
0774            static boolean matchNameAttribute(AttributeSet attr, HTML.Tag tag) {
0775                Object o = attr.getAttribute(StyleConstants.NameAttribute);
0776                if (o instanceof  HTML.Tag) {
0777                    HTML.Tag name = (HTML.Tag) o;
0778                    if (name == tag) {
0779                        return true;
0780                    }
0781                }
0782                return false;
0783            }
0784
0785            /**
0786             * Replaces a frameset branch Element with a frame leaf element.
0787             *
0788             * @param element the frameset element to remove
0789             * @param url     the value for the SRC attribute for the
0790             *                new frame that will replace the frameset
0791             */
0792            private void updateFrameSet(Element element, String url) {
0793                try {
0794                    int startOffset = element.getStartOffset();
0795                    int endOffset = Math.min(getLength(), element
0796                            .getEndOffset());
0797                    String html = "<frame";
0798                    if (url != null) {
0799                        html += " src=\"" + url + "\"";
0800                    }
0801                    html += ">";
0802                    installParserIfNecessary();
0803                    setOuterHTML(element, html);
0804                } catch (BadLocationException e1) {
0805                    // Should handle this better
0806                } catch (IOException ioe) {
0807                    // Should handle this better
0808                }
0809            }
0810
0811            /**
0812             * Updates the Frame elements <code>HTML.Attribute.SRC attribute</code>
0813             * and fires a <code>ChangedUpdate</code> event.
0814             *
0815             * @param element a FRAME element whose SRC attribute will be updated
0816             * @param url     a string specifying the new value for the SRC attribute
0817             */
0818            private void updateFrame(Element element, String url) {
0819
0820                try {
0821                    writeLock();
0822                    DefaultDocumentEvent changes = new DefaultDocumentEvent(
0823                            element.getStartOffset(), 1,
0824                            DocumentEvent.EventType.CHANGE);
0825                    AttributeSet sCopy = element.getAttributes()
0826                            .copyAttributes();
0827                    MutableAttributeSet attr = (MutableAttributeSet) element
0828                            .getAttributes();
0829                    changes.addEdit(new AttributeUndoableEdit(element, sCopy,
0830                            false));
0831                    attr.removeAttribute(HTML.Attribute.SRC);
0832                    attr.addAttribute(HTML.Attribute.SRC, url);
0833                    changes.end();
0834                    fireChangedUpdate(changes);
0835                    fireUndoableEditUpdate(new UndoableEditEvent(this , changes));
0836                } finally {
0837                    writeUnlock();
0838                }
0839            }
0840
0841            /**
0842             * Returns true if the document will be viewed in a frame.
0843             * @return true if document will be viewed in a frame, otherwise false
0844             */
0845            boolean isFrameDocument() {
0846                return frameDocument;
0847            }
0848
0849            /**
0850             * Sets a boolean state about whether the document will be
0851             * viewed in a frame.
0852             * @param frameDoc  true if the document will be viewed in a frame,
0853             *		otherwise false
0854             */
0855            void setFrameDocumentState(boolean frameDoc) {
0856                this .frameDocument = frameDoc;
0857            }
0858
0859            /**
0860             * Adds the specified map, this will remove a Map that has been
0861             * previously registered with the same name.
0862             *
0863             * @param map  the <code>Map</code> to be registered
0864             */
0865            void addMap(Map map) {
0866                String name = map.getName();
0867
0868                if (name != null) {
0869                    Object maps = getProperty(MAP_PROPERTY);
0870
0871                    if (maps == null) {
0872                        maps = new Hashtable(11);
0873                        putProperty(MAP_PROPERTY, maps);
0874                    }
0875                    if (maps instanceof  Hashtable) {
0876                        ((Hashtable) maps).put("#" + name, map);
0877                    }
0878                }
0879            }
0880
0881            /**
0882             * Removes a previously registered map.
0883             * @param map the <code>Map</code> to be removed
0884             */
0885            void removeMap(Map map) {
0886                String name = map.getName();
0887
0888                if (name != null) {
0889                    Object maps = getProperty(MAP_PROPERTY);
0890
0891                    if (maps instanceof  Hashtable) {
0892                        ((Hashtable) maps).remove("#" + name);
0893                    }
0894                }
0895            }
0896
0897            /**
0898             * Returns the Map associated with the given name.
0899             * @param the name of the desired <code>Map</code>
0900             * @return the <code>Map</code> or <code>null</code> if it can't
0901             *		be found, or if <code>name</code> is <code>null</code>
0902             */
0903            Map getMap(String name) {
0904                if (name != null) {
0905                    Object maps = getProperty(MAP_PROPERTY);
0906
0907                    if (maps != null && (maps instanceof  Hashtable)) {
0908                        return (Map) ((Hashtable) maps).get(name);
0909                    }
0910                }
0911                return null;
0912            }
0913
0914            /**
0915             * Returns an <code>Enumeration</code> of the possible Maps.
0916             * @return the enumerated list of maps, or <code>null</code>
0917             *		if the maps are not an instance of <code>Hashtable</code>
0918             */
0919            Enumeration getMaps() {
0920                Object maps = getProperty(MAP_PROPERTY);
0921
0922                if (maps instanceof  Hashtable) {
0923                    return ((Hashtable) maps).elements();
0924                }
0925                return null;
0926            }
0927
0928            /**
0929             * Sets the content type language used for style sheets that do not
0930             * explicitly specify the type. The default is text/css.
0931             * @param contentType  the content type language for the style sheets
0932             */
0933            /* public */
0934            void setDefaultStyleSheetType(String contentType) {
0935                putProperty(StyleType, contentType);
0936            }
0937
0938            /**
0939             * Returns the content type language used for style sheets. The default
0940             * is text/css.
0941             * @return the content type language used for the style sheets
0942             */
0943            /* public */
0944            String getDefaultStyleSheetType() {
0945                String retValue = (String) getProperty(StyleType);
0946                if (retValue == null) {
0947                    return "text/css";
0948                }
0949                return retValue;
0950            }
0951
0952            /**
0953             * Sets the parser that is used by the methods that insert html
0954             * into the existing document, such as <code>setInnerHTML</code>, 
0955             * and <code>setOuterHTML</code>.
0956             * <p>
0957             * <code>HTMLEditorKit.createDefaultDocument</code> will set the parser
0958             * for you. If you create an <code>HTMLDocument</code> by hand,
0959             * be sure and set the parser accordingly.
0960             * @param parser the parser to be used for text insertion
0961             *
0962             * @since 1.3
0963             */
0964            public void setParser(HTMLEditorKit.Parser parser) {
0965                this .parser = parser;
0966                putProperty("__PARSER__", null);
0967            }
0968
0969            /**
0970             * Returns the parser that is used when inserting HTML into the existing
0971             * document.
0972             * @return the parser used for text insertion
0973             *
0974             * @since 1.3
0975             */
0976            public HTMLEditorKit.Parser getParser() {
0977                Object p = getProperty("__PARSER__");
0978
0979                if (p instanceof  HTMLEditorKit.Parser) {
0980                    return (HTMLEditorKit.Parser) p;
0981                }
0982                return parser;
0983            }
0984
0985            /** 
0986             * Replaces the children of the given element with the contents 
0987             * specified as an HTML string.
0988             *
0989             * <p>This will be seen as at least two events, n inserts followed by
0990             * a remove.</p>
0991             *
0992             * <p>Consider the following structure (the <code>elem</code>
0993             * parameter is <b>in bold</b>).</p>
0994             *
0995             * <pre>
0996             *     &lt;body>
0997             *       |
0998             *     <b>&lt;div></b>
0999             *      /  \
1000             *    &lt;p>   &lt;p>
1001             * </pre>
1002             *
1003             * <p>Invoking <code>setInnerHTML(elem, "&lt;ul>&lt;li>")</code>
1004             * results in the following structure (new elements are <font
1005             * color="red">in red</font>).</p>
1006             *
1007             * <pre>
1008             *     &lt;body>
1009             *       |
1010             *     <b>&lt;div></b>
1011             *         \
1012             *         <font color="red">&lt;ul></font>
1013             *           \
1014             *           <font color="red">&lt;li></font>
1015             * </pre>
1016             *
1017             * <p>Parameter <code>elem</code> must not be a leaf element,
1018             * otherwise an <code>IllegalArgumentException</code> is thrown.
1019             * If either <code>elem</code> or <code>htmlText</code> parameter
1020             * is <code>null</code>, no changes are made to the document.</p>
1021             *
1022             * <p>For this to work correcty, the document must have an
1023             * <code>HTMLEditorKit.Parser</code> set. This will be the case
1024             * if the document was created from an HTMLEditorKit via the
1025             * <code>createDefaultDocument</code> method.</p>
1026             *
1027             * @param elem the branch element whose children will be replaced
1028             * @param htmlText the string to be parsed and assigned to <code>elem</code>
1029             * @throws IllegalArgumentException if <code>elem</code> is a leaf
1030             * @throws IllegalStateException if an <code>HTMLEditorKit.Parser</code>
1031             *         has not been defined
1032             * @since 1.3
1033             */
1034            public void setInnerHTML(Element elem, String htmlText)
1035                    throws BadLocationException, IOException {
1036                verifyParser();
1037                if (elem != null && elem.isLeaf()) {
1038                    throw new IllegalArgumentException(
1039                            "Can not set inner HTML of a leaf");
1040                }
1041                if (elem != null && htmlText != null) {
1042                    int oldCount = elem.getElementCount();
1043                    int insertPosition = elem.getStartOffset();
1044                    insertHTML(elem, elem.getStartOffset(), htmlText, true);
1045                    if (elem.getElementCount() > oldCount) {
1046                        // Elements were inserted, do the cleanup.
1047                        removeElements(elem, elem.getElementCount() - oldCount,
1048                                oldCount);
1049                    }
1050                }
1051            }
1052
1053            /** 
1054             * Replaces the given element in the parent with the contents 
1055             * specified as an HTML string. 
1056             *
1057             * <p>This will be seen as at least two events, n inserts followed by
1058             * a remove.</p>
1059             *
1060             * <p>When replacing a leaf this will attempt to make sure there is
1061             * a newline present if one is needed. This may result in an additional
1062             * element being inserted. Consider, if you were to replace a character
1063             * element that contained a newline with &lt;img&gt; this would create
1064             * two elements, one for the image, ane one for the newline.</p>
1065             *
1066             * <p>If you try to replace the element at length you will most
1067             * likely end up with two elements, eg
1068             * <code>setOuterHTML(getCharacterElement (getLength()),
1069             * "blah")</code> will result in two leaf elements at the end, one
1070             * representing 'blah', and the other representing the end
1071             * element.</p>
1072             *
1073             * <p>Consider the following structure (the <code>elem</code>
1074             * parameter is <b>in bold</b>).</p>
1075             *
1076             * <pre>
1077             *     &lt;body>
1078             *       |
1079             *     <b>&lt;div></b>
1080             *      /  \
1081             *    &lt;p>   &lt;p>
1082             * </pre>
1083             *
1084             * <p>Invoking <code>setOuterHTML(elem, "&lt;ul>&lt;li>")</code>
1085             * results in the following structure (new elements are <font
1086             * color="red">in red</font>).</p>
1087             *
1088             * <pre>
1089             *    &lt;body>
1090             *      |
1091             *     <font color="red">&lt;ul></font>
1092             *       \
1093             *       <font color="red">&lt;li></font>
1094             * </pre>
1095             *
1096             * <p>If either <code>elem</code> or <code>htmlText</code>
1097             * parameter is <code>null</code>, no changes are made to the
1098             * document.</p>
1099             *
1100             * <p>For this to work correcty, the document must have an
1101             * HTMLEditorKit.Parser set. This will be the case if the document
1102             * was created from an HTMLEditorKit via the
1103             * <code>createDefaultDocument</code> method.</p>
1104             *
1105             * @param elem the element to replace
1106             * @param htmlText the string to be parsed and inserted in place of <code>elem</code>
1107             * @throws IllegalStateException if an HTMLEditorKit.Parser has not
1108             *         been set
1109             * @since 1.3
1110             */
1111            public void setOuterHTML(Element elem, String htmlText)
1112                    throws BadLocationException, IOException {
1113                verifyParser();
1114                if (elem != null && elem.getParentElement() != null
1115                        && htmlText != null) {
1116                    int start = elem.getStartOffset();
1117                    int end = elem.getEndOffset();
1118                    int startLength = getLength();
1119                    // We don't want a newline if elem is a leaf, and doesn't contain
1120                    // a newline.
1121                    boolean wantsNewline = !elem.isLeaf();
1122                    if (!wantsNewline
1123                            && (end > startLength || getText(end - 1, 1)
1124                                    .charAt(0) == NEWLINE[0])) {
1125                        wantsNewline = true;
1126                    }
1127                    Element parent = elem.getParentElement();
1128                    int oldCount = parent.getElementCount();
1129                    insertHTML(parent, start, htmlText, wantsNewline);
1130                    // Remove old.
1131                    int newLength = getLength();
1132                    if (oldCount != parent.getElementCount()) {
1133                        int removeIndex = parent.getElementIndex(start
1134                                + newLength - startLength);
1135                        removeElements(parent, removeIndex, 1);
1136                    }
1137                }
1138            }
1139
1140            /** 
1141             * Inserts the HTML specified as a string at the start 
1142             * of the element.
1143             *
1144             * <p>Consider the following structure (the <code>elem</code>
1145             * parameter is <b>in bold</b>).</p>
1146             *
1147             * <pre>
1148             *     &lt;body>
1149             *       |
1150             *     <b>&lt;div></b>
1151             *      /  \
1152             *    &lt;p>   &lt;p>
1153             * </pre>
1154             *
1155             * <p>Invoking <code>insertAfterStart(elem,
1156             * "&lt;ul>&lt;li>")</code> results in the following structure
1157             * (new elements are <font color="red">in red</font>).</p>
1158             *
1159             * <pre>
1160             *        &lt;body>
1161             *          |
1162             *        <b>&lt;div></b>
1163             *       /  |  \
1164             *    <font color="red">&lt;ul></font> &lt;p> &lt;p>
1165             *     /
1166             *  <font color="red">&lt;li></font>
1167             * </pre>
1168             *
1169             * <p>Unlike the <code>insertBeforeStart</code> method, new
1170             *  elements become <em>children</em> of the specified element,
1171             *  not siblings.</p>
1172             *
1173             * <p>Parameter <code>elem</code> must not be a leaf element,
1174             * otherwise an <code>IllegalArgumentException</code> is thrown.
1175             * If either <code>elem</code> or <code>htmlText</code> parameter
1176             * is <code>null</code>, no changes are made to the document.</p>
1177             *
1178             * <p>For this to work correcty, the document must have an
1179             * <code>HTMLEditorKit.Parser</code> set. This will be the case
1180             * if the document was created from an HTMLEditorKit via the
1181             * <code>createDefaultDocument</code> method.</p>
1182             *
1183             * @param elem the branch element to be the root for the new text
1184             * @param htmlText the string to be parsed and assigned to <code>elem</code>
1185             * @throws IllegalArgumentException if <code>elem</code> is a leaf
1186             * @throws IllegalStateException if an HTMLEditorKit.Parser has not
1187             *         been set on the document 
1188             * @since 1.3
1189             */
1190            public void insertAfterStart(Element elem, String htmlText)
1191                    throws BadLocationException, IOException {
1192                verifyParser();
1193                if (elem != null && elem.isLeaf()) {
1194                    throw new IllegalArgumentException(
1195                            "Can not insert HTML after start of a leaf");
1196                }
1197                insertHTML(elem, elem.getStartOffset(), htmlText, false);
1198            }
1199
1200            /** 
1201             * Inserts the HTML specified as a string at the end of 
1202             * the element. 
1203             *
1204             * <p> If <code>elem</code>'s children are leaves, and the 
1205             * character at a <code>elem.getEndOffset() - 1</code> is a newline,
1206             * this will insert before the newline so that there isn't text after
1207             * the newline.</p>
1208             *
1209             * <p>Consider the following structure (the <code>elem</code>
1210             * parameter is <b>in bold</b>).</p>
1211             *
1212             * <pre>
1213             *     &lt;body>
1214             *       |
1215             *     <b>&lt;div></b>
1216             *      /  \
1217             *    &lt;p>   &lt;p>
1218             * </pre>
1219             *
1220             * <p>Invoking <code>insertBeforeEnd(elem, "&lt;ul>&lt;li>")</code>
1221             * results in the following structure (new elements are <font
1222             * color="red">in red</font>).</p>
1223             *
1224             * <pre>
1225             *        &lt;body>
1226             *          |
1227             *        <b>&lt;div></b>
1228             *       /  |  \
1229             *     &lt;p> &lt;p> <font color="red">&lt;ul></font>
1230             *               \
1231             *               <font color="red">&lt;li></font>
1232             * </pre>
1233             *
1234             * <p>Unlike the <code>insertAfterEnd</code> method, new elements
1235             * become <em>children</em> of the specified element, not
1236             * siblings.</p>
1237             *
1238             * <p>Parameter <code>elem</code> must not be a leaf element,
1239             * otherwise an <code>IllegalArgumentException</code> is thrown.
1240             * If either <code>elem</code> or <code>htmlText</code> parameter
1241             * is <code>null</code>, no changes are made to the document.</p>
1242             *
1243             * <p>For this to work correcty, the document must have an
1244             * <code>HTMLEditorKit.Parser</code> set. This will be the case
1245             * if the document was created from an HTMLEditorKit via the
1246             * <code>createDefaultDocument</code> method.</p>
1247             *
1248             * @param elem the element to be the root for the new text
1249             * @param htmlText the string to be parsed and assigned to <code>elem</code>
1250             * @throws IllegalArgumentException if <code>elem</code> is a leaf
1251             * @throws IllegalStateException if an HTMLEditorKit.Parser has not
1252             *         been set on the document
1253             * @since 1.3
1254             */
1255            public void insertBeforeEnd(Element elem, String htmlText)
1256                    throws BadLocationException, IOException {
1257                verifyParser();
1258                if (elem != null && elem.isLeaf()) {
1259                    throw new IllegalArgumentException(
1260                            "Can not set inner HTML before end of leaf");
1261                }
1262                if (elem != null) {
1263                    int offset = elem.getEndOffset();
1264                    if (elem.getElement(elem.getElementIndex(offset - 1))
1265                            .isLeaf()
1266                            && getText(offset - 1, 1).charAt(0) == NEWLINE[0]) {
1267                        offset--;
1268                    }
1269                    insertHTML(elem, offset, htmlText, false);
1270                }
1271            }
1272
1273            /** 
1274             * Inserts the HTML specified as a string before the start of 
1275             * the given element. 
1276             *
1277             * <p>Consider the following structure (the <code>elem</code>
1278             * parameter is <b>in bold</b>).</p>
1279             *
1280             * <pre>
1281             *     &lt;body>
1282             *       |
1283             *     <b>&lt;div></b>
1284             *      /  \
1285             *    &lt;p>   &lt;p>
1286             * </pre>
1287             *
1288             * <p>Invoking <code>insertBeforeStart(elem,
1289             * "&lt;ul>&lt;li>")</code> results in the following structure
1290             * (new elements are <font color="red">in red</font>).</p>
1291             *
1292             * <pre>
1293             *        &lt;body>
1294             *         /  \
1295             *      <font color="red">&lt;ul></font> <b>&lt;div></b>
1296             *       /    /  \
1297             *     <font color="red">&lt;li></font> &lt;p>  &lt;p>
1298             * </pre>
1299             *
1300             * <p>Unlike the <code>insertAfterStart</code> method, new
1301             * elements become <em>siblings</em> of the specified element, not
1302             * children.</p>
1303             *
1304             * <p>If either <code>elem</code> or <code>htmlText</code>
1305             * parameter is <code>null</code>, no changes are made to the
1306             * document.</p>
1307             *
1308             * <p>For this to work correcty, the document must have an
1309             * <code>HTMLEditorKit.Parser</code> set. This will be the case
1310             * if the document was created from an HTMLEditorKit via the
1311             * <code>createDefaultDocument</code> method.</p>
1312             *
1313             * @param elem the element the content is inserted before
1314             * @param htmlText the string to be parsed and inserted before <code>elem</code>
1315             * @throws IllegalStateException if an HTMLEditorKit.Parser has not
1316             *         been set on the document
1317             * @since 1.3
1318             */
1319            public void insertBeforeStart(Element elem, String htmlText)
1320                    throws BadLocationException, IOException {
1321                verifyParser();
1322                if (elem != null) {
1323                    Element parent = elem.getParentElement();
1324
1325                    if (parent != null) {
1326                        insertHTML(parent, elem.getStartOffset(), htmlText,
1327                                false);
1328                    }
1329                }
1330            }
1331
1332            /** 
1333             * Inserts the HTML specified as a string after the the end of the
1334             * given element.
1335             *
1336             * <p>Consider the following structure (the <code>elem</code>
1337             * parameter is <b>in bold</b>).</p>
1338             *
1339             * <pre>
1340             *     &lt;body>
1341             *       |
1342             *     <b>&lt;div></b>
1343             *      /  \
1344             *    &lt;p>   &lt;p>
1345             * </pre>
1346             *
1347             * <p>Invoking <code>insertAfterEnd(elem, "&lt;ul>&lt;li>")</code>
1348             * results in the following structure (new elements are <font
1349             * color="red">in red</font>).</p>
1350             *
1351             * <pre>
1352             *        &lt;body>
1353             *         /  \
1354             *      <b>&lt;div></b> <font color="red">&lt;ul></font>
1355             *       / \    \
1356             *     &lt;p> &lt;p>  <font color="red">&lt;li></font>
1357             * </pre>
1358             *
1359             * <p>Unlike the <code>insertBeforeEnd</code> method, new elements
1360             * become <em>siblings</em> of the specified element, not
1361             * children.</p>
1362             *
1363             * <p>If either <code>elem</code> or <code>htmlText</code>
1364             * parameter is <code>null</code>, no changes are made to the
1365             * document.</p>
1366             *
1367             * <p>For this to work correcty, the document must have an
1368             * <code>HTMLEditorKit.Parser</code> set. This will be the case
1369             * if the document was created from an HTMLEditorKit via the
1370             * <code>createDefaultDocument</code> method.</p>
1371             *
1372             * @param elem the element the content is inserted after
1373             * @param htmlText the string to be parsed and inserted after <code>elem</code>
1374             * @throws IllegalStateException if an HTMLEditorKit.Parser has not
1375             *         been set on the document
1376             * @since 1.3
1377             */
1378            public void insertAfterEnd(Element elem, String htmlText)
1379                    throws BadLocationException, IOException {
1380                verifyParser();
1381                if (elem != null) {
1382                    Element parent = elem.getParentElement();
1383
1384                    if (parent != null) {
1385                        int offset = elem.getEndOffset();
1386                        if (offset > getLength()) {
1387                            offset--;
1388                        } else if (elem.isLeaf()
1389                                && getText(offset - 1, 1).charAt(0) == NEWLINE[0]) {
1390                            offset--;
1391                        }
1392                        insertHTML(parent, offset, htmlText, false);
1393                    }
1394                }
1395            }
1396
1397            /** 
1398             * Returns the element that has the given id <code>Attribute</code>. 
1399             * If the element can't be found, <code>null</code> is returned.
1400             * Note that this method works on an <code>Attribute</code>,
1401             * <i>not</i> a character tag.  In the following HTML snippet:
1402             * <code>&lt;a id="HelloThere"&gt;</code> the attribute is
1403             * 'id' and the character tag is 'a'.
1404             * This is a convenience method for 
1405             * <code>getElement(RootElement, HTML.Attribute.id, id)</code>.
1406             * This is not thread-safe.
1407             *
1408             * @param id  the string representing the desired <code>Attribute</code>
1409             * @return the element with the specified <code>Attribute</code>
1410             *		or <code>null</code> if it can't be found,
1411             *		or <code>null</code> if <code>id</code> is <code>null</code>
1412             * @see javax.swing.text.html.HTML.Attribute
1413             * @since 1.3
1414             */
1415            public Element getElement(String id) {
1416                if (id == null) {
1417                    return null;
1418                }
1419                return getElement(getDefaultRootElement(), HTML.Attribute.ID,
1420                        id, true);
1421            }
1422
1423            /**
1424             * Returns the child element of <code>e</code> that contains the
1425             * attribute, <code>attribute</code> with value <code>value</code>, or
1426             * <code>null</code> if one isn't found. This is not thread-safe.
1427             *
1428             * @param e the root element where the search begins
1429             * @param attribute the desired <code>Attribute</code>
1430             * @param value the values for the specified <code>Attribute</code>
1431             * @return the element with the specified <code>Attribute</code>
1432             * 		and the specified <code>value</code>, or <code>null</code>
1433             *		if it can't be found
1434             * @see javax.swing.text.html.HTML.Attribute
1435             * @since 1.3
1436             */
1437            public Element getElement(Element e, Object attribute, Object value) {
1438                return getElement(e, attribute, value, true);
1439            }
1440
1441            /**
1442             * Returns the child element of <code>e</code> that contains the
1443             * attribute, <code>attribute</code> with value <code>value</code>, or
1444             * <code>null</code> if one isn't found. This is not thread-safe.
1445             * <p>
1446             * If <code>searchLeafAttributes</code> is true, and <code>e</code> is
1447             * a leaf, any attributes that are instances of <code>HTML.Tag</code>
1448             * with a value that is an <code>AttributeSet</code> will also be checked.
1449             *
1450             * @param e the root element where the search begins
1451             * @param attribute the desired <code>Attribute</code>
1452             * @param value the values for the specified <code>Attribute</code>
1453             * @return the element with the specified <code>Attribute</code>
1454             * 		and the specified <code>value</code>, or <code>null</code>
1455             *		if it can't be found
1456             * @see javax.swing.text.html.HTML.Attribute
1457             */
1458            private Element getElement(Element e, Object attribute,
1459                    Object value, boolean searchLeafAttributes) {
1460                AttributeSet attr = e.getAttributes();
1461
1462                if (attr != null && attr.isDefined(attribute)) {
1463                    if (value.equals(attr.getAttribute(attribute))) {
1464                        return e;
1465                    }
1466                }
1467                if (!e.isLeaf()) {
1468                    for (int counter = 0, maxCounter = e.getElementCount(); counter < maxCounter; counter++) {
1469                        Element retValue = getElement(e.getElement(counter),
1470                                attribute, value, searchLeafAttributes);
1471
1472                        if (retValue != null) {
1473                            return retValue;
1474                        }
1475                    }
1476                } else if (searchLeafAttributes && attr != null) {
1477                    // For some leaf elements we store the actual attributes inside
1478                    // the AttributeSet of the Element (such as anchors).
1479                    Enumeration names = attr.getAttributeNames();
1480                    if (names != null) {
1481                        while (names.hasMoreElements()) {
1482                            Object name = names.nextElement();
1483                            if ((name instanceof  HTML.Tag)
1484                                    && (attr.getAttribute(name) instanceof  AttributeSet)) {
1485
1486                                AttributeSet check = (AttributeSet) attr
1487                                        .getAttribute(name);
1488                                if (check.isDefined(attribute)
1489                                        && value.equals(check
1490                                                .getAttribute(attribute))) {
1491                                    return e;
1492                                }
1493                            }
1494                        }
1495                    }
1496                }
1497                return null;
1498            }
1499
1500            /**
1501             * Verifies the document has an <code>HTMLEditorKit.Parser</code> set.
1502             * If <code>getParser</code> returns <code>null</code>, this will throw an 
1503             * IllegalStateException.
1504             *
1505             * @throws IllegalStateException if the document does not have a Parser
1506             */
1507            private void verifyParser() {
1508                if (getParser() == null) {
1509                    throw new IllegalStateException("No HTMLEditorKit.Parser");
1510                }
1511            }
1512
1513            /**
1514             * Installs a default Parser if one has not been installed yet.
1515             */
1516            private void installParserIfNecessary() {
1517                if (getParser() == null) {
1518                    setParser(new HTMLEditorKit().getParser());
1519                }
1520            }
1521
1522            /**
1523             * Inserts a string of HTML into the document at the given position.
1524             * <code>parent</code> is used to identify the location to insert the
1525             * <code>html</code>. If <code>parent</code> is a leaf this can have
1526             * unexpected results.
1527             */
1528            private void insertHTML(Element parent, int offset, String html,
1529                    boolean wantsTrailingNewline) throws BadLocationException,
1530                    IOException {
1531                if (parent != null && html != null) {
1532                    HTMLEditorKit.Parser parser = getParser();
1533                    if (parser != null) {
1534                        int lastOffset = Math.max(0, offset - 1);
1535                        Element charElement = getCharacterElement(lastOffset);
1536                        Element commonParent = parent;
1537                        int pop = 0;
1538                        int push = 0;
1539
1540                        if (parent.getStartOffset() > lastOffset) {
1541                            while (commonParent != null
1542                                    && commonParent.getStartOffset() > lastOffset) {
1543                                commonParent = commonParent.getParentElement();
1544                                push++;
1545                            }
1546                            if (commonParent == null) {
1547                                throw new BadLocationException(
1548                                        "No common parent", offset);
1549                            }
1550                        }
1551                        while (charElement != null
1552                                && charElement != commonParent) {
1553                            pop++;
1554                            charElement = charElement.getParentElement();
1555                        }
1556                        if (charElement != null) {
1557                            // Found it, do the insert.
1558                            HTMLReader reader = new HTMLReader(offset, pop - 1,
1559                                    push, null, false, true,
1560                                    wantsTrailingNewline);
1561
1562                            parser.parse(new StringReader(html), reader, true);
1563                            reader.flush();
1564                        }
1565                    }
1566                }
1567            }
1568
1569            /**
1570             * Removes child Elements of the passed in Element <code>e</code>. This
1571             * will do the necessary cleanup to ensure the element representing the
1572             * end character is correctly created.
1573             * <p>This is not a general purpose method, it assumes that <code>e</code>
1574             * will still have at least one child after the remove, and it assumes
1575             * the character at <code>e.getStartOffset() - 1</code> is a newline and
1576             * is of length 1.
1577             */
1578            private void removeElements(Element e, int index, int count)
1579                    throws BadLocationException {
1580                writeLock();
1581                try {
1582                    int start = e.getElement(index).getStartOffset();
1583                    int end = e.getElement(index + count - 1).getEndOffset();
1584                    if (end > getLength()) {
1585                        removeElementsAtEnd(e, index, count, start, end);
1586                    } else {
1587                        removeElements(e, index, count, start, end);
1588                    }
1589                } finally {
1590                    writeUnlock();
1591                }
1592            }
1593
1594            /**
1595             * Called to remove child elements of <code>e</code> when one of the
1596             * elements to remove is representing the end character.
1597             * <p>Since the Content will not allow a removal to the end character
1598             * this will do a remove from <code>start - 1</code> to <code>end</code>.
1599             * The end Element(s) will be removed, and the element representing
1600             * <code>start - 1</code> to <code>start</code> will be recreated. This
1601             * Element has to be recreated as after the content removal its offsets
1602             * become <code>start - 1</code> to <code>start - 1</code>.
1603             */
1604            private void removeElementsAtEnd(Element e, int index, int count,
1605                    int start, int end) throws BadLocationException {
1606                // index must be > 0 otherwise no insert would have happened.
1607                boolean isLeaf = (e.getElement(index - 1).isLeaf());
1608                DefaultDocumentEvent dde = new DefaultDocumentEvent(start - 1,
1609                        end - start + 1, DocumentEvent.EventType.REMOVE);
1610
1611                if (isLeaf) {
1612                    Element endE = getCharacterElement(getLength());
1613                    // e.getElement(index - 1) should represent the newline.
1614                    index--;
1615                    if (endE.getParentElement() != e) {
1616                        // The hiearchies don't match, we'll have to manually
1617                        // recreate the leaf at e.getElement(index - 1)
1618                        replace(dde, e, index, ++count, start, end, true, true);
1619                    } else {
1620                        // The hierarchies for the end Element and
1621                        // e.getElement(index - 1), match, we can safely remove
1622                        // the Elements and the end content will be aligned
1623                        // appropriately.
1624                        replace(dde, e, index, count, start, end, true, false);
1625                    }
1626                } else {
1627                    // Not a leaf, descend until we find the leaf representing
1628                    // start - 1 and remove it.
1629                    Element newLineE = e.getElement(index - 1);
1630                    while (!newLineE.isLeaf()) {
1631                        newLineE = newLineE.getElement(newLineE
1632                                .getElementCount() - 1);
1633                    }
1634                    newLineE = newLineE.getParentElement();
1635                    replace(dde, e, index, count, start, end, false, false);
1636                    replace(dde, newLineE, newLineE.getElementCount() - 1, 1,
1637                            start, end, true, true);
1638                }
1639                postRemoveUpdate(dde);
1640                dde.end();
1641                fireRemoveUpdate(dde);
1642                fireUndoableEditUpdate(new UndoableEditEvent(this , dde));
1643            }
1644
1645            /**
1646             * This is used by <code>removeElementsAtEnd</code>, it removes
1647             * <code>count</code> elements starting at <code>start</code> from
1648             * <code>e</code>.  If <code>remove</code> is true text of length
1649             * <code>start - 1</code> to <code>end - 1</code> is removed.  If
1650             * <code>create</code> is true a new leaf is created of length 1.
1651             */
1652            private void replace(DefaultDocumentEvent dde, Element e,
1653                    int index, int count, int start, int end, boolean remove,
1654                    boolean create) throws BadLocationException {
1655                Element[] added;
1656                AttributeSet attrs = e.getElement(index).getAttributes();
1657                Element[] removed = new Element[count];
1658
1659                for (int counter = 0; counter < count; counter++) {
1660                    removed[counter] = e.getElement(counter + index);
1661                }
1662                if (remove) {
1663                    UndoableEdit u = getContent()
1664                            .remove(start - 1, end - start);
1665                    if (u != null) {
1666                        dde.addEdit(u);
1667                    }
1668                }
1669                if (create) {
1670                    added = new Element[1];
1671                    added[0] = createLeafElement(e, attrs, start - 1, start);
1672                } else {
1673                    added = new Element[0];
1674                }
1675                dde.addEdit(new ElementEdit(e, index, removed, added));
1676                ((AbstractDocument.BranchElement) e).replace(index,
1677                        removed.length, added);
1678            }
1679
1680            /**
1681             * Called to remove child Elements when the end is not touched.
1682             */
1683            private void removeElements(Element e, int index, int count,
1684                    int start, int end) throws BadLocationException {
1685                Element[] removed = new Element[count];
1686                Element[] added = new Element[0];
1687                for (int counter = 0; counter < count; counter++) {
1688                    removed[counter] = e.getElement(counter + index);
1689                }
1690                DefaultDocumentEvent dde = new DefaultDocumentEvent(start, end
1691                        - start, DocumentEvent.EventType.REMOVE);
1692                ((AbstractDocument.BranchElement) e).replace(index,
1693                        removed.length, added);
1694                dde.addEdit(new ElementEdit(e, index, removed, added));
1695                UndoableEdit u = getContent().remove(start, end - start);
1696                if (u != null) {
1697                    dde.addEdit(u);
1698                }
1699                postRemoveUpdate(dde);
1700                dde.end();
1701                fireRemoveUpdate(dde);
1702                if (u != null) {
1703                    fireUndoableEditUpdate(new UndoableEditEvent(this , dde));
1704                }
1705            }
1706
1707            // These two are provided for inner class access. The are named different
1708            // than the super class as the super class implementations are final.
1709            void obtainLock() {
1710                writeLock();
1711            }
1712
1713            void releaseLock() {
1714                writeUnlock();
1715            }
1716
1717            //
1718            // Provided for inner class access.
1719            //
1720
1721            /**
1722             * Notifies all listeners that have registered interest for
1723             * notification on this event type.  The event instance 
1724             * is lazily created using the parameters passed into 
1725             * the fire method.
1726             *
1727             * @param e the event
1728             * @see EventListenerList
1729             */
1730            protected void fireChangedUpdate(DocumentEvent e) {
1731                super .fireChangedUpdate(e);
1732            }
1733
1734            /**
1735             * Notifies all listeners that have registered interest for
1736             * notification on this event type.  The event instance 
1737             * is lazily created using the parameters passed into 
1738             * the fire method.
1739             *
1740             * @param e the event
1741             * @see EventListenerList
1742             */
1743            protected void fireUndoableEditUpdate(UndoableEditEvent e) {
1744                super .fireUndoableEditUpdate(e);
1745            }
1746
1747            boolean hasBaseTag() {
1748                return hasBaseTag;
1749            }
1750
1751            String getBaseTarget() {
1752                return baseTarget;
1753            }
1754
1755            /*
1756             * state defines whether the document is a frame document
1757             * or not.
1758             */
1759            private boolean frameDocument = false;
1760            private boolean preservesUnknownTags = true;
1761
1762            /*
1763             * Used to store button groups for radio buttons in
1764             * a form.
1765             */
1766            private HashMap radioButtonGroupsMap;
1767
1768            /**
1769             * Document property for the number of tokens to buffer 
1770             * before building an element subtree to represent them.
1771             */
1772            static final String TokenThreshold = "token threshold";
1773
1774            private static final int MaxThreshold = 10000;
1775
1776            private static final int StepThreshold = 5;
1777
1778            /**
1779             * Document property key value. The value for the key will be a Vector
1780             * of Strings that are comments not found in the body.
1781             */
1782            public static final String AdditionalComments = "AdditionalComments";
1783
1784            /**
1785             * Document property key value. The value for the key will be a 
1786             * String indicating the default type of stylesheet links.
1787             */
1788            /* public */static final String StyleType = "StyleType";
1789
1790            /**
1791             * The location to resolve relative URLs against.  By
1792             * default this will be the document's URL if the document
1793             * was loaded from a URL.  If a base tag is found and
1794             * can be parsed, it will be used as the base location.
1795             */
1796            URL base;
1797
1798            /**
1799             * does the document have base tag
1800             */
1801            boolean hasBaseTag = false;
1802
1803            /**
1804             * BASE tag's TARGET attribute value
1805             */
1806            private String baseTarget = null;
1807
1808            /**
1809             * The parser that is used when inserting html into the existing
1810             * document.
1811             */
1812            private HTMLEditorKit.Parser parser;
1813
1814            /**
1815             * Used for inserts when a null AttributeSet is supplied.
1816             */
1817            private static AttributeSet contentAttributeSet;
1818
1819            /**
1820             * Property Maps are registered under, will be a Hashtable.
1821             */
1822            static String MAP_PROPERTY = "__MAP__";
1823
1824            private static char[] NEWLINE;
1825            private static final String IMPLIED_CR = "CR";
1826
1827            /**
1828             * I18N property key.  
1829             *
1830             * @see AbstractDocument.I18NProperty
1831             */
1832            private static final String I18NProperty = "i18n";
1833
1834            static {
1835                contentAttributeSet = new SimpleAttributeSet();
1836                ((MutableAttributeSet) contentAttributeSet).addAttribute(
1837                        StyleConstants.NameAttribute, HTML.Tag.CONTENT);
1838                NEWLINE = new char[1];
1839                NEWLINE[0] = '\n';
1840            }
1841
1842            /**
1843             * An iterator to iterate over a particular type of
1844             * tag.  The iterator is not thread safe.  If reliable
1845             * access to the document is not already ensured by
1846             * the context under which the iterator is being used,
1847             * its use should be performed under the protection of
1848             * Document.render. 
1849             */
1850            public static abstract class Iterator {
1851
1852                /**
1853                 * Return the attributes for this tag.
1854                 * @return the <code>AttributeSet</code> for this tag, or
1855                 *	<code>null</code> if none can be found
1856                 */
1857                public abstract AttributeSet getAttributes();
1858
1859                /**
1860                 * Returns the start of the range for which the current occurrence of
1861                 * the tag is defined and has the same attributes.
1862                 *
1863                 * @return the start of the range, or -1 if it can't be found
1864                 */
1865                public abstract int getStartOffset();
1866
1867                /**
1868                 * Returns the end of the range for which the current occurrence of
1869                 * the tag is defined and has the same attributes.
1870                 *
1871                 * @return the end of the range
1872                 */
1873                public abstract int getEndOffset();
1874
1875                /**
1876                 * Move the iterator forward to the next occurrence
1877                 * of the tag it represents.
1878                 */
1879                public abstract void next();
1880
1881                /**
1882                 * Indicates if the iterator is currently
1883                 * representing an occurrence of a tag.  If
1884                 * false there are no more tags for this iterator.
1885                 * @return true if the iterator is currently representing an
1886                 *		occurrence of a tag, otherwise returns false
1887                 */
1888                public abstract boolean isValid();
1889
1890                /**
1891                 * Type of tag this iterator represents.
1892                 */
1893                public abstract HTML.Tag getTag();
1894            }
1895
1896            /**
1897             * An iterator to iterate over a particular type of tag.
1898             */
1899            static class LeafIterator extends Iterator {
1900
1901                LeafIterator(HTML.Tag t, Document doc) {
1902                    tag = t;
1903                    pos = new ElementIterator(doc);
1904                    endOffset = 0;
1905                    next();
1906                }
1907
1908                /**
1909                 * Returns the attributes for this tag.
1910                 * @return the <code>AttributeSet</code> for this tag,
1911                 *		or <code>null</code> if none can be found
1912                 */
1913                public AttributeSet getAttributes() {
1914                    Element elem = pos.current();
1915                    if (elem != null) {
1916                        AttributeSet a = (AttributeSet) elem.getAttributes()
1917                                .getAttribute(tag);
1918                        if (a == null) {
1919                            a = (AttributeSet) elem.getAttributes();
1920                        }
1921                        return a;
1922                    }
1923                    return null;
1924                }
1925
1926                /**
1927                 * Returns the start of the range for which the current occurrence of
1928                 * the tag is defined and has the same attributes.
1929                 *
1930                 * @return the start of the range, or -1 if it can't be found
1931                 */
1932                public int getStartOffset() {
1933                    Element elem = pos.current();
1934                    if (elem != null) {
1935                        return elem.getStartOffset();
1936                    }
1937                    return -1;
1938                }
1939
1940                /**
1941                 * Returns the end of the range for which the current occurrence of
1942                 * the tag is defined and has the same attributes.
1943                 *
1944                 * @return the end of the range
1945                 */
1946                public int getEndOffset() {
1947                    return endOffset;
1948                }
1949
1950                /**
1951                 * Moves the iterator forward to the next occurrence
1952                 * of the tag it represents.
1953                 */
1954                public void next() {
1955                    for (nextLeaf(pos); isValid(); nextLeaf(pos)) {
1956                        Element elem = pos.current();
1957                        if (elem.getStartOffset() >= endOffset) {
1958                            AttributeSet a = pos.current().getAttributes();
1959
1960                            if (a.isDefined(tag)
1961                                    || a
1962                                            .getAttribute(StyleConstants.NameAttribute) == tag) {
1963
1964                                // we found the next one
1965                                setEndOffset();
1966                                break;
1967                            }
1968                        }
1969                    }
1970                }
1971
1972                /**
1973                 * Returns the type of tag this iterator represents.
1974                 *
1975                 * @return the <code>HTML.Tag</code> that this iterator represents.
1976                 * @see javax.swing.text.html.HTML.Tag
1977                 */
1978                public HTML.Tag getTag() {
1979                    return tag;
1980                }
1981
1982                /**
1983                 * Returns true if the current position is not <code>null</code>.
1984                 * @return true if current position is not <code>null</code>,
1985                 *		otherwise returns false
1986                 */
1987                public boolean isValid() {
1988                    return (pos.current() != null);
1989                }
1990
1991                /**
1992                 * Moves the given iterator to the next leaf element.
1993                 * @param iter  the iterator to be scanned
1994                 */
1995                void nextLeaf(ElementIterator iter) {
1996                    for (iter.next(); iter.current() != null; iter.next()) {
1997                        Element e = iter.current();
1998                        if (e.isLeaf()) {
1999                            break;
2000                        }
2001                    }
2002                }
2003
2004                /**
2005                 * Marches a cloned iterator forward to locate the end
2006                 * of the run.  This sets the value of <code>endOffset</code>.
2007                 */
2008                void setEndOffset() {
2009                    AttributeSet a0 = getAttributes();
2010                    endOffset = pos.current().getEndOffset();
2011                    ElementIterator fwd = (ElementIterator) pos.clone();
2012                    for (nextLeaf(fwd); fwd.current() != null; nextLeaf(fwd)) {
2013                        Element e = fwd.current();
2014                        AttributeSet a1 = (AttributeSet) e.getAttributes()
2015                                .getAttribute(tag);
2016                        if ((a1 == null) || (!a1.equals(a0))) {
2017                            break;
2018                        }
2019                        endOffset = e.getEndOffset();
2020                    }
2021                }
2022
2023                private int endOffset;
2024                private HTML.Tag tag;
2025                private ElementIterator pos;
2026
2027            }
2028
2029            /**
2030             * An HTML reader to load an HTML document with an HTML
2031             * element structure.  This is a set of callbacks from
2032             * the parser, implemented to create a set of elements
2033             * tagged with attributes.  The parse builds up tokens
2034             * (ElementSpec) that describe the element subtree desired,
2035             * and burst it into the document under the protection of
2036             * a write lock using the insert method on the document
2037             * outer class.
2038             * <p>
2039             * The reader can be configured by registering actions
2040             * (of type <code>HTMLDocument.HTMLReader.TagAction</code>)
2041             * that describe how to handle the action.  The idea behind
2042             * the actions provided is that the most natural text editing
2043             * operations can be provided if the element structure boils
2044             * down to paragraphs with runs of some kind of style 
2045             * in them.  Some things are more naturally specified 
2046             * structurally, so arbitrary structure should be allowed 
2047             * above the paragraphs, but will need to be edited with structural
2048             * actions.  The implication of this is that some of the
2049             * HTML elements specified in the stream being parsed will
2050             * be collapsed into attributes, and in some cases paragraphs
2051             * will be synthesized.  When HTML elements have been
2052             * converted to attributes, the attribute key will be of
2053             * type HTML.Tag, and the value will be of type AttributeSet
2054             * so that no information is lost.  This enables many of the
2055             * existing actions to work so that the user can type input,
2056             * hit the return key, backspace, delete, etc and have a 
2057             * reasonable result.  Selections can be created, and attributes
2058             * applied or removed, etc.  With this in mind, the work done
2059             * by the reader can be categorized into the following kinds
2060             * of tasks:
2061             * <dl>
2062             * <dt>Block
2063             * <dd>Build the structure like it's specified in the stream.
2064             * This produces elements that contain other elements.
2065             * <dt>Paragraph
2066             * <dd>Like block except that it's expected that the element
2067             * will be used with a paragraph view so a paragraph element
2068             * won't need to be synthesized.
2069             * <dt>Character
2070             * <dd>Contribute the element as an attribute that will start
2071             * and stop at arbitrary text locations.  This will ultimately
2072             * be mixed into a run of text, with all of the currently 
2073             * flattened HTML character elements.
2074             * <dt>Special
2075             * <dd>Produce an embedded graphical element.
2076             * <dt>Form
2077             * <dd>Produce an element that is like the embedded graphical
2078             * element, except that it also has a component model associated
2079             * with it.
2080             * <dt>Hidden
2081             * <dd>Create an element that is hidden from view when the
2082             * document is being viewed read-only, and visible when the
2083             * document is being edited.  This is useful to keep the
2084             * model from losing information, and used to store things
2085             * like comments and unrecognized tags.
2086             *
2087             * </dl>
2088             * <p>
2089             * Currently, &lt;APPLET&gt;, &lt;PARAM&gt;, &lt;MAP&gt;, &lt;AREA&gt;, &lt;LINK&gt;,
2090             * &lt;SCRIPT&gt; and &lt;STYLE&gt; are unsupported.
2091             *
2092             * <p>
2093             * The assignment of the actions described is shown in the
2094             * following table for the tags defined in <code>HTML.Tag</code>.<P>
2095             * <table border=1 summary="HTML tags and assigned actions">
2096             * <tr><th>Tag</th><th>Action</th></tr>
2097             * <tr><td><code>HTML.Tag.A</code>         <td>CharacterAction
2098             * <tr><td><code>HTML.Tag.ADDRESS</code>   <td>CharacterAction
2099             * <tr><td><code>HTML.Tag.APPLET</code>    <td>HiddenAction
2100             * <tr><td><code>HTML.Tag.AREA</code>      <td>AreaAction
2101             * <tr><td><code>HTML.Tag.B</code>         <td>CharacterAction
2102             * <tr><td><code>HTML.Tag.BASE</code>      <td>BaseAction
2103             * <tr><td><code>HTML.Tag.BASEFONT</code>  <td>CharacterAction
2104             * <tr><td><code>HTML.Tag.BIG</code>       <td>CharacterAction
2105             * <tr><td><code>HTML.Tag.BLOCKQUOTE</code><td>BlockAction
2106             * <tr><td><code>HTML.Tag.BODY</code>      <td>BlockAction
2107             * <tr><td><code>HTML.Tag.BR</code>        <td>SpecialAction
2108             * <tr><td><code>HTML.Tag.CAPTION</code>   <td>BlockAction
2109             * <tr><td><code>HTML.Tag.CENTER</code>    <td>BlockAction
2110             * <tr><td><code>HTML.Tag.CITE</code>      <td>CharacterAction
2111             * <tr><td><code>HTML.Tag.CODE</code>      <td>CharacterAction
2112             * <tr><td><code>HTML.Tag.DD</code>        <td>BlockAction
2113             * <tr><td><code>HTML.Tag.DFN</code>       <td>CharacterAction
2114             * <tr><td><code>HTML.Tag.DIR</code>       <td>BlockAction
2115             * <tr><td><code>HTML.Tag.DIV</code>       <td>BlockAction
2116             * <tr><td><code>HTML.Tag.DL</code>        <td>BlockAction
2117             * <tr><td><code>HTML.Tag.DT</code>        <td>ParagraphAction
2118             * <tr><td><code>HTML.Tag.EM</code>        <td>CharacterAction
2119             * <tr><td><code>HTML.Tag.FONT</code>      <td>CharacterAction
2120             * <tr><td><code>HTML.Tag.FORM</code>      <td>As of 1.4 a BlockAction
2121             * <tr><td><code>HTML.Tag.FRAME</code>     <td>SpecialAction
2122             * <tr><td><code>HTML.Tag.FRAMESET</code>  <td>BlockAction
2123             * <tr><td><code>HTML.Tag.H1</code>        <td>ParagraphAction
2124             * <tr><td><code>HTML.Tag.H2</code>        <td>ParagraphAction
2125             * <tr><td><code>HTML.Tag.H3</code>        <td>ParagraphAction
2126             * <tr><td><code>HTML.Tag.H4</code>        <td>ParagraphAction
2127             * <tr><td><code>HTML.Tag.H5</code>        <td>ParagraphAction
2128             * <tr><td><code>HTML.Tag.H6</code>        <td>ParagraphAction
2129             * <tr><td><code>HTML.Tag.HEAD</code>      <td>HeadAction
2130             * <tr><td><code>HTML.Tag.HR</code>        <td>SpecialAction
2131             * <tr><td><code>HTML.Tag.HTML</code>      <td>BlockAction
2132             * <tr><td><code>HTML.Tag.I</code>         <td>CharacterAction
2133             * <tr><td><code>HTML.Tag.IMG</code>       <td>SpecialAction
2134             * <tr><td><code>HTML.Tag.INPUT</code>     <td>FormAction
2135             * <tr><td><code>HTML.Tag.ISINDEX</code>   <td>IsndexAction
2136             * <tr><td><code>HTML.Tag.KBD</code>       <td>CharacterAction
2137             * <tr><td><code>HTML.Tag.LI</code>        <td>BlockAction
2138             * <tr><td><code>HTML.Tag.LINK</code>      <td>LinkAction
2139             * <tr><td><code>HTML.Tag.MAP</code>       <td>MapAction
2140             * <tr><td><code>HTML.Tag.MENU</code>      <td>BlockAction
2141             * <tr><td><code>HTML.Tag.META</code>      <td>MetaAction
2142             * <tr><td><code>HTML.Tag.NOFRAMES</code>  <td>BlockAction
2143             * <tr><td><code>HTML.Tag.OBJECT</code>    <td>SpecialAction
2144             * <tr><td><code>HTML.Tag.OL</code>        <td>BlockAction
2145             * <tr><td><code>HTML.Tag.OPTION</code>    <td>FormAction
2146             * <tr><td><code>HTML.Tag.P</code>         <td>ParagraphAction
2147             * <tr><td><code>HTML.Tag.PARAM</code>     <td>HiddenAction
2148             * <tr><td><code>HTML.Tag.PRE</code>       <td>PreAction
2149             * <tr><td><code>HTML.Tag.SAMP</code>      <td>CharacterAction
2150             * <tr><td><code>HTML.Tag.SCRIPT</code>    <td>HiddenAction
2151             * <tr><td><code>HTML.Tag.SELECT</code>    <td>FormAction
2152             * <tr><td><code>HTML.Tag.SMALL</code>     <td>CharacterAction
2153             * <tr><td><code>HTML.Tag.STRIKE</code>    <td>CharacterAction
2154             * <tr><td><code>HTML.Tag.S</code>         <td>CharacterAction
2155             * <tr><td><code>HTML.Tag.STRONG</code>    <td>CharacterAction
2156             * <tr><td><code>HTML.Tag.STYLE</code>     <td>StyleAction
2157             * <tr><td><code>HTML.Tag.SUB</code>       <td>CharacterAction
2158             * <tr><td><code>HTML.Tag.SUP</code>       <td>CharacterAction
2159             * <tr><td><code>HTML.Tag.TABLE</code>     <td>BlockAction
2160             * <tr><td><code>HTML.Tag.TD</code>        <td>BlockAction
2161             * <tr><td><code>HTML.Tag.TEXTAREA</code>  <td>FormAction
2162             * <tr><td><code>HTML.Tag.TH</code>        <td>BlockAction
2163             * <tr><td><code>HTML.Tag.TITLE</code>     <td>TitleAction
2164             * <tr><td><code>HTML.Tag.TR</code>        <td>BlockAction
2165             * <tr><td><code>HTML.Tag.TT</code>        <td>CharacterAction
2166             * <tr><td><code>HTML.Tag.U</code>         <td>CharacterAction
2167             * <tr><td><code>HTML.Tag.UL</code>        <td>BlockAction
2168             * <tr><td><code>HTML.Tag.VAR</code>       <td>CharacterAction
2169             * </table>
2170             * <p>
2171             * Once &lt;/html> is encountered, the Actions are no longer notified.
2172             */
2173            public class HTMLReader extends HTMLEditorKit.ParserCallback {
2174
2175                public HTMLReader(int offset) {
2176                    this (offset, 0, 0, null);
2177                }
2178
2179                public HTMLReader(int offset, int popDepth, int pushDepth,
2180                        HTML.Tag insertTag) {
2181                    this (offset, popDepth, pushDepth, insertTag, true, false,
2182                            true);
2183                }
2184
2185                /**
2186                 * Generates a RuntimeException (will eventually generate
2187                 * a BadLocationException when API changes are alloced) if inserting
2188                 * into non empty document, <code>insertTag</code> is
2189                 * non-<code>null</code>, and <code>offset</code> is not in the body.
2190                 */
2191                // PENDING(sky): Add throws BadLocationException and remove
2192                // RuntimeException
2193                HTMLReader(int offset, int popDepth, int pushDepth,
2194                        HTML.Tag insertTag, boolean insertInsertTag,
2195                        boolean insertAfterImplied, boolean wantsTrailingNewline) {
2196                    emptyDocument = (getLength() == 0);
2197                    isStyleCSS = "text/css".equals(getDefaultStyleSheetType());
2198                    this .offset = offset;
2199                    threshold = HTMLDocument.this .getTokenThreshold();
2200                    tagMap = new Hashtable(57);
2201                    TagAction na = new TagAction();
2202                    TagAction ba = new BlockAction();
2203                    TagAction pa = new ParagraphAction();
2204                    TagAction ca = new CharacterAction();
2205                    TagAction sa = new SpecialAction();
2206                    TagAction fa = new FormAction();
2207                    TagAction ha = new HiddenAction();
2208                    TagAction conv = new ConvertAction();
2209
2210                    // register handlers for the well known tags
2211                    tagMap.put(HTML.Tag.A, new AnchorAction());
2212                    tagMap.put(HTML.Tag.ADDRESS, ca);
2213                    tagMap.put(HTML.Tag.APPLET, ha);
2214                    tagMap.put(HTML.Tag.AREA, new AreaAction());
2215                    tagMap.put(HTML.Tag.B, conv);
2216                    tagMap.put(HTML.Tag.BASE, new BaseAction());
2217                    tagMap.put(HTML.Tag.BASEFONT, ca);
2218                    tagMap.put(HTML.Tag.BIG, ca);
2219                    tagMap.put(HTML.Tag.BLOCKQUOTE, ba);
2220                    tagMap.put(HTML.Tag.BODY, ba);
2221                    tagMap.put(HTML.Tag.BR, sa);
2222                    tagMap.put(HTML.Tag.CAPTION, ba);
2223                    tagMap.put(HTML.Tag.CENTER, ba);
2224                    tagMap.put(HTML.Tag.CITE, ca);
2225                    tagMap.put(HTML.Tag.CODE, ca);
2226                    tagMap.put(HTML.Tag.DD, ba);
2227                    tagMap.put(HTML.Tag.DFN, ca);
2228                    tagMap.put(HTML.Tag.DIR, ba);
2229                    tagMap.put(HTML.Tag.DIV, ba);
2230                    tagMap.put(HTML.Tag.DL, ba);
2231                    tagMap.put(HTML.Tag.DT, pa);
2232                    tagMap.put(HTML.Tag.EM, ca);
2233                    tagMap.put(HTML.Tag.FONT, conv);
2234                    tagMap.put(HTML.Tag.FORM, new FormTagAction());
2235                    tagMap.put(HTML.Tag.FRAME, sa);
2236                    tagMap.put(HTML.Tag.FRAMESET, ba);
2237                    tagMap.put(HTML.Tag.H1, pa);
2238                    tagMap.put(HTML.Tag.H2, pa);
2239                    tagMap.put(HTML.Tag.H3, pa);
2240                    tagMap.put(HTML.Tag.H4, pa);
2241                    tagMap.put(HTML.Tag.H5, pa);
2242                    tagMap.put(HTML.Tag.H6, pa);
2243                    tagMap.put(HTML.Tag.HEAD, new HeadAction());
2244                    tagMap.put(HTML.Tag.HR, sa);
2245                    tagMap.put(HTML.Tag.HTML, ba);
2246                    tagMap.put(HTML.Tag.I, conv);
2247                    tagMap.put(HTML.Tag.IMG, sa);
2248                    tagMap.put(HTML.Tag.INPUT, fa);
2249                    tagMap.put(HTML.Tag.ISINDEX, new IsindexAction());
2250                    tagMap.put(HTML.Tag.KBD, ca);
2251                    tagMap.put(HTML.Tag.LI, ba);
2252                    tagMap.put(HTML.Tag.LINK, new LinkAction());
2253                    tagMap.put(HTML.Tag.MAP, new MapAction());
2254                    tagMap.put(HTML.Tag.MENU, ba);
2255                    tagMap.put(HTML.Tag.META, new MetaAction());
2256                    tagMap.put(HTML.Tag.NOBR, ca);
2257                    tagMap.put(HTML.Tag.NOFRAMES, ba);
2258                    tagMap.put(HTML.Tag.OBJECT, sa);
2259                    tagMap.put(HTML.Tag.OL, ba);
2260                    tagMap.put(HTML.Tag.OPTION, fa);
2261                    tagMap.put(HTML.Tag.P, pa);
2262                    tagMap.put(HTML.Tag.PARAM, new ObjectAction());
2263                    tagMap.put(HTML.Tag.PRE, new PreAction());
2264                    tagMap.put(HTML.Tag.SAMP, ca);
2265                    tagMap.put(HTML.Tag.SCRIPT, ha);
2266                    tagMap.put(HTML.Tag.SELECT, fa);
2267                    tagMap.put(HTML.Tag.SMALL, ca);
2268                    tagMap.put(HTML.Tag.SPAN, ca);
2269                    tagMap.put(HTML.Tag.STRIKE, conv);
2270                    tagMap.put(HTML.Tag.S, ca);
2271                    tagMap.put(HTML.Tag.STRONG, ca);
2272                    tagMap.put(HTML.Tag.STYLE, new StyleAction());
2273                    tagMap.put(HTML.Tag.SUB, conv);
2274                    tagMap.put(HTML.Tag.SUP, conv);
2275                    tagMap.put(HTML.Tag.TABLE, ba);
2276                    tagMap.put(HTML.Tag.TD, ba);
2277                    tagMap.put(HTML.Tag.TEXTAREA, fa);
2278                    tagMap.put(HTML.Tag.TH, ba);
2279                    tagMap.put(HTML.Tag.TITLE, new TitleAction());
2280                    tagMap.put(HTML.Tag.TR, ba);
2281                    tagMap.put(HTML.Tag.TT, ca);
2282                    tagMap.put(HTML.Tag.U, conv);
2283                    tagMap.put(HTML.Tag.UL, ba);
2284                    tagMap.put(HTML.Tag.VAR, ca);
2285
2286                    if (insertTag != null) {
2287                        this .insertTag = insertTag;
2288                        this .popDepth = popDepth;
2289                        this .pushDepth = pushDepth;
2290                        this .insertInsertTag = insertInsertTag;
2291                        foundInsertTag = false;
2292                    } else {
2293                        foundInsertTag = true;
2294                    }
2295                    if (insertAfterImplied) {
2296                        this .popDepth = popDepth;
2297                        this .pushDepth = pushDepth;
2298                        this .insertAfterImplied = true;
2299                        foundInsertTag = false;
2300                        midInsert = false;
2301                        this .insertInsertTag = true;
2302                        this .wantsTrailingNewline = wantsTrailingNewline;
2303                    } else {
2304                        midInsert = (!emptyDocument && insertTag == null);
2305                        if (midInsert) {
2306                            generateEndsSpecsForMidInsert();
2307                        }
2308                    }
2309
2310                    /** 
2311                     * This block initializes the <code>inParagraph</code> flag. 
2312                     * It is left in <code>false</code> value automatically  
2313                     * if the target document is empty or future inserts  
2314                     * were positioned into the 'body' tag. 
2315                     */
2316                    if (!emptyDocument && !midInsert) {
2317                        int targetOffset = Math.max(this .offset - 1, 0);
2318                        Element elem = HTMLDocument.this 
2319                                .getCharacterElement(targetOffset);
2320                        /* Going up by the left document structure path */
2321                        for (int i = 0; i <= this .popDepth; i++) {
2322                            elem = elem.getParentElement();
2323                        }
2324                        /* Going down by the right document structure path */
2325                        for (int i = 0; i < this .pushDepth; i++) {
2326                            int index = elem.getElementIndex(this .offset);
2327                            elem = elem.getElement(index);
2328                        }
2329                        AttributeSet attrs = elem.getAttributes();
2330                        if (attrs != null) {
2331                            HTML.Tag tagToInsertInto = (HTML.Tag) attrs
2332                                    .getAttribute(StyleConstants.NameAttribute);
2333                            if (tagToInsertInto != null) {
2334                                this .inParagraph = tagToInsertInto
2335                                        .isParagraph();
2336                            }
2337                        }
2338                    }
2339                }
2340
2341                /**
2342                 * Generates an initial batch of end <code>ElementSpecs</code>
2343                 * in parseBuffer to position future inserts into the body.
2344                 */
2345                private void generateEndsSpecsForMidInsert() {
2346                    int count = heightToElementWithName(HTML.Tag.BODY, Math
2347                            .max(0, offset - 1));
2348                    boolean joinNext = false;
2349
2350                    if (count == -1 && offset > 0) {
2351                        count = heightToElementWithName(HTML.Tag.BODY, offset);
2352                        if (count != -1) {
2353                            // Previous isn't in body, but current is. Have to
2354                            // do some end specs, followed by join next.
2355                            count = depthTo(offset - 1) - 1;
2356                            joinNext = true;
2357                        }
2358                    }
2359                    if (count == -1) {
2360                        throw new RuntimeException(
2361                                "Must insert new content into body element-");
2362                    }
2363                    if (count != -1) {
2364                        // Insert a newline, if necessary.
2365                        try {
2366                            if (!joinNext && offset > 0
2367                                    && !getText(offset - 1, 1).equals("\n")) {
2368                                SimpleAttributeSet newAttrs = new SimpleAttributeSet();
2369                                newAttrs.addAttribute(
2370                                        StyleConstants.NameAttribute,
2371                                        HTML.Tag.CONTENT);
2372                                ElementSpec spec = new ElementSpec(newAttrs,
2373                                        ElementSpec.ContentType, NEWLINE, 0, 1);
2374                                parseBuffer.addElement(spec);
2375                            }
2376                            // Should never throw, but will catch anyway.
2377                        } catch (BadLocationException ble) {
2378                        }
2379                        while (count-- > 0) {
2380                            parseBuffer.addElement(new ElementSpec(null,
2381                                    ElementSpec.EndTagType));
2382                        }
2383                        if (joinNext) {
2384                            ElementSpec spec = new ElementSpec(null,
2385                                    ElementSpec.StartTagType);
2386
2387                            spec.setDirection(ElementSpec.JoinNextDirection);
2388                            parseBuffer.addElement(spec);
2389                        }
2390                    }
2391                    // We should probably throw an exception if (count == -1)
2392                    // Or look for the body and reset the offset.
2393                }
2394
2395                /**
2396                 * @return number of parents to reach the child at offset.
2397                 */
2398                private int depthTo(int offset) {
2399                    Element e = getDefaultRootElement();
2400                    int count = 0;
2401
2402                    while (!e.isLeaf()) {
2403                        count++;
2404                        e = e.getElement(e.getElementIndex(offset));
2405                    }
2406                    return count;
2407                }
2408
2409                /**
2410                 * @return number of parents of the leaf at <code>offset</code>
2411                 *         until a parent with name, <code>name</code> has been
2412                 *         found. -1 indicates no matching parent with
2413                 *         <code>name</code>.
2414                 */
2415                private int heightToElementWithName(Object name, int offset) {
2416                    Element e = getCharacterElement(offset).getParentElement();
2417                    int count = 0;
2418
2419                    while (e != null
2420                            && e.getAttributes().getAttribute(
2421                                    StyleConstants.NameAttribute) != name) {
2422                        count++;
2423                        e = e.getParentElement();
2424                    }
2425                    return (e == null) ? -1 : count;
2426                }
2427
2428                /**
2429                 * This will make sure there aren't two BODYs (the second is
2430                 * typically created when you do a remove all, and then an insert).
2431                 */
2432                private void adjustEndElement() {
2433                    int length = getLength();
2434                    if (length == 0) {
2435                        return;
2436                    }
2437                    obtainLock();
2438                    try {
2439                        Element[] pPath = getPathTo(length - 1);
2440                        int pLength = pPath.length;
2441                        if (pLength > 1
2442                                && pPath[1].getAttributes().getAttribute(
2443                                        StyleConstants.NameAttribute) == HTML.Tag.BODY
2444                                && pPath[1].getEndOffset() == length) {
2445                            String lastText = getText(length - 1, 1);
2446                            DefaultDocumentEvent event = null;
2447                            Element[] added;
2448                            Element[] removed;
2449                            int index;
2450                            // Remove the fake second body.
2451                            added = new Element[0];
2452                            removed = new Element[1];
2453                            index = pPath[0].getElementIndex(length);
2454                            removed[0] = pPath[0].getElement(index);
2455                            ((BranchElement) pPath[0]).replace(index, 1, added);
2456                            ElementEdit firstEdit = new ElementEdit(pPath[0],
2457                                    index, removed, added);
2458
2459                            // Insert a new element to represent the end that the
2460                            // second body was representing.
2461                            SimpleAttributeSet sas = new SimpleAttributeSet();
2462                            sas.addAttribute(StyleConstants.NameAttribute,
2463                                    HTML.Tag.CONTENT);
2464                            sas.addAttribute(IMPLIED_CR, Boolean.TRUE);
2465                            added = new Element[1];
2466                            added[0] = createLeafElement(pPath[pLength - 1],
2467                                    sas, length, length + 1);
2468                            index = pPath[pLength - 1].getElementCount();
2469                            ((BranchElement) pPath[pLength - 1]).replace(index,
2470                                    0, added);
2471                            event = new DefaultDocumentEvent(length, 1,
2472                                    DocumentEvent.EventType.CHANGE);
2473                            event.addEdit(new ElementEdit(pPath[pLength - 1],
2474                                    index, new Element[0], added));
2475                            event.addEdit(firstEdit);
2476                            event.end();
2477                            fireChangedUpdate(event);
2478                            fireUndoableEditUpdate(new UndoableEditEvent(this ,
2479                                    event));
2480
2481                            if (lastText.equals("\n")) {
2482                                // We now have two \n's, one part of the Document.
2483                                // We need to remove one
2484                                event = new DefaultDocumentEvent(length - 1, 1,
2485                                        DocumentEvent.EventType.REMOVE);
2486                                removeUpdate(event);
2487                                UndoableEdit u = getContent().remove(
2488                                        length - 1, 1);
2489                                if (u != null) {
2490                                    event.addEdit(u);
2491                                }
2492                                postRemoveUpdate(event);
2493                                // Mark the edit as done.
2494                                event.end();
2495                                fireRemoveUpdate(event);
2496                                fireUndoableEditUpdate(new UndoableEditEvent(
2497                                        this , event));
2498                            }
2499                        }
2500                    } catch (BadLocationException ble) {
2501                    } finally {
2502                        releaseLock();
2503                    }
2504                }
2505
2506                private Element[] getPathTo(int offset) {
2507                    Stack elements = new Stack();
2508                    Element e = getDefaultRootElement();
2509                    int index;
2510                    while (!e.isLeaf()) {
2511                        elements.push(e);
2512                        e = e.getElement(e.getElementIndex(offset));
2513                    }
2514                    Element[] retValue = new Element[elements.size()];
2515                    elements.copyInto(retValue);
2516                    return retValue;
2517                }
2518
2519                // -- HTMLEditorKit.ParserCallback methods --------------------
2520
2521                /**
2522                 * The last method called on the reader.  It allows
2523                 * any pending changes to be flushed into the document.  
2524                 * Since this is currently loading synchronously, the entire
2525                 * set of changes are pushed in at this point.
2526                 */
2527                public void flush() throws BadLocationException {
2528                    if (emptyDocument && !insertAfterImplied) {
2529                        if (HTMLDocument.this .getLength() > 0
2530                                || parseBuffer.size() > 0) {
2531                            flushBuffer(true);
2532                            adjustEndElement();
2533                        }
2534                        // We won't insert when 
2535                    } else {
2536                        flushBuffer(true);
2537                    }
2538                }
2539
2540                /**
2541                 * Called by the parser to indicate a block of text was
2542                 * encountered.
2543                 */
2544                public void handleText(char[] data, int pos) {
2545                    if (receivedEndHTML || (midInsert && !inBody)) {
2546                        return;
2547                    }
2548
2549                    // see if complex glyph layout support is needed
2550                    if (HTMLDocument.this .getProperty(I18NProperty).equals(
2551                            Boolean.FALSE)) {
2552                        // if a default direction of right-to-left has been specified,
2553                        // we want complex layout even if the text is all left to right.
2554                        Object d = getProperty(TextAttribute.RUN_DIRECTION);
2555                        if ((d != null)
2556                                && (d.equals(TextAttribute.RUN_DIRECTION_RTL))) {
2557                            HTMLDocument.this .putProperty(I18NProperty,
2558                                    Boolean.TRUE);
2559                        } else {
2560                            if (SwingUtilities2.isComplexLayout(data, 0,
2561                                    data.length)) {
2562                                HTMLDocument.this .putProperty(I18NProperty,
2563                                        Boolean.TRUE);
2564                            }
2565                        }
2566                    }
2567
2568                    if (inTextArea) {
2569                        textAreaContent(data);
2570                    } else if (inPre) {
2571                        preContent(data);
2572                    } else if (inTitle) {
2573                        putProperty(Document.TitleProperty, new String(data));
2574                    } else if (option != null) {
2575                        option.setLabel(new String(data));
2576                    } else if (inStyle) {
2577                        if (styles != null) {
2578                            styles.addElement(new String(data));
2579                        }
2580                    } else if (inBlock > 0) {
2581                        if (!foundInsertTag && insertAfterImplied) {
2582                            // Assume content should be added.
2583                            foundInsertTag(false);
2584                            foundInsertTag = true;
2585                            inParagraph = impliedP = true;
2586                        }
2587                        if (data.length >= 1) {
2588                            addContent(data, 0, data.length);
2589                        }
2590                    }
2591                }
2592
2593                /**
2594                 * Callback from the parser.  Route to the appropriate
2595                 * handler for the tag.
2596                 */
2597                public void handleStartTag(HTML.Tag t, MutableAttributeSet a,
2598                        int pos) {
2599                    if (receivedEndHTML) {
2600                        return;
2601                    }
2602                    if (midInsert && !inBody) {
2603                        if (t == HTML.Tag.BODY) {
2604                            inBody = true;
2605                            // Increment inBlock since we know we are in the body,
2606                            // this is needed incase an implied-p is needed. If
2607                            // inBlock isn't incremented, and an implied-p is
2608                            // encountered, addContent won't be called!
2609                            inBlock++;
2610                        }
2611                        return;
2612                    }
2613                    if (!inBody && t == HTML.Tag.BODY) {
2614                        inBody = true;
2615                    }
2616                    if (isStyleCSS && a.isDefined(HTML.Attribute.STYLE)) {
2617                        // Map the style attributes.
2618                        String decl = (String) a
2619                                .getAttribute(HTML.Attribute.STYLE);
2620                        a.removeAttribute(HTML.Attribute.STYLE);
2621                        styleAttributes = getStyleSheet().getDeclaration(decl);
2622                        a.addAttributes(styleAttributes);
2623                    } else {
2624                        styleAttributes = null;
2625                    }
2626                    TagAction action = (TagAction) tagMap.get(t);
2627
2628                    if (action != null) {
2629                        action.start(t, a);
2630                    }
2631                }
2632
2633                public void handleComment(char[] data, int pos) {
2634                    if (receivedEndHTML) {
2635                        addExternalComment(new String(data));
2636                        return;
2637                    }
2638                    if (inStyle) {
2639                        if (styles != null) {
2640                            styles.addElement(new String(data));
2641                        }
2642                    } else if (getPreservesUnknownTags()) {
2643                        if (inBlock == 0
2644                                && (foundInsertTag || insertTag != HTML.Tag.COMMENT)) {
2645                            // Comment outside of body, will not be able to show it,
2646                            // but can add it as a property on the Document.
2647                            addExternalComment(new String(data));
2648                            return;
2649                        }
2650                        SimpleAttributeSet sas = new SimpleAttributeSet();
2651                        sas.addAttribute(HTML.Attribute.COMMENT, new String(
2652                                data));
2653                        addSpecialElement(HTML.Tag.COMMENT, sas);
2654                    }
2655
2656                    TagAction action = (TagAction) tagMap.get(HTML.Tag.COMMENT);
2657                    if (action != null) {
2658                        action
2659                                .start(HTML.Tag.COMMENT,
2660                                        new SimpleAttributeSet());
2661                        action.end(HTML.Tag.COMMENT);
2662                    }
2663                }
2664
2665                /**
2666                 * Adds the comment <code>comment</code> to the set of comments
2667                 * maintained outside of the scope of elements.
2668                 */
2669                private void addExternalComment(String comment) {
2670                    Object comments = getProperty(AdditionalComments);
2671                    if (comments != null && !(comments instanceof  Vector)) {
2672                        // No place to put comment.
2673                        return;
2674                    }
2675                    if (comments == null) {
2676                        comments = new Vector();
2677                        putProperty(AdditionalComments, comments);
2678                    }
2679                    ((Vector) comments).addElement(comment);
2680                }
2681
2682                /**
2683                 * Callback from the parser.  Route to the appropriate
2684                 * handler for the tag.
2685                 */
2686                public void handleEndTag(HTML.Tag t, int pos) {
2687                    if (receivedEndHTML || (midInsert && !inBody)) {
2688                        return;
2689                    }
2690                    if (t == HTML.Tag.HTML) {
2691                        receivedEndHTML = true;
2692                    }
2693                    if (t == HTML.Tag.BODY) {
2694                        inBody = false;
2695                        if (midInsert) {
2696                            inBlock--;
2697                        }
2698                    }
2699                    TagAction action = (TagAction) tagMap.get(t);
2700                    if (action != null) {
2701                        action.end(t);
2702                    }
2703                }
2704
2705                /**
2706                 * Callback from the parser.  Route to the appropriate
2707                 * handler for the tag.
2708                 */
2709                public void handleSimpleTag(HTML.Tag t, MutableAttributeSet a,
2710                        int pos) {
2711                    if (receivedEndHTML || (midInsert && !inBody)) {
2712                        return;
2713                    }
2714
2715                    if (isStyleCSS && a.isDefined(HTML.Attribute.STYLE)) {
2716                        // Map the style attributes.
2717                        String decl = (String) a
2718                                .getAttribute(HTML.Attribute.STYLE);
2719                        a.removeAttribute(HTML.Attribute.STYLE);
2720                        styleAttributes = getStyleSheet().getDeclaration(decl);
2721                        a.addAttributes(styleAttributes);
2722                    } else {
2723                        styleAttributes = null;
2724                    }
2725
2726                    TagAction action = (TagAction) tagMap.get(t);
2727                    if (action != null) {
2728                        action.start(t, a);
2729                        action.end(t);
2730                    } else if (getPreservesUnknownTags()) {
2731                        // unknown tag, only add if should preserve it.
2732                        addSpecialElement(t, a);
2733                    }
2734                }
2735
2736                /**
2737                 * This is invoked after the stream has been parsed, but before
2738                 * <code>flush</code>. <code>eol</code> will be one of \n, \r
2739                 * or \r\n, which ever is encountered the most in parsing the
2740                 * stream.
2741                 *
2742                 * @since 1.3
2743                 */
2744                public void handleEndOfLineString(String eol) {
2745                    if (emptyDocument && eol != null) {
2746                        putProperty(DefaultEditorKit.EndOfLineStringProperty,
2747                                eol);
2748                    }
2749                }
2750
2751                // ---- tag handling support ------------------------------
2752
2753                /**
2754                 * Registers a handler for the given tag.  By default
2755                 * all of the well-known tags will have been registered.
2756                 * This can be used to change the handling of a particular
2757                 * tag or to add support for custom tags.
2758                 */
2759                protected void registerTag(HTML.Tag t, TagAction a) {
2760                    tagMap.put(t, a);
2761                }
2762
2763                /**
2764                 * An action to be performed in response
2765                 * to parsing a tag.  This allows customization
2766                 * of how each tag is handled and avoids a large
2767                 * switch statement.
2768                 */
2769                public class TagAction {
2770
2771                    /**
2772                     * Called when a start tag is seen for the
2773                     * type of tag this action was registered
2774                     * to.  The tag argument indicates the actual
2775                     * tag for those actions that are shared across
2776                     * many tags.  By default this does nothing and
2777                     * completely ignores the tag.
2778                     */
2779                    public void start(HTML.Tag t, MutableAttributeSet a) {
2780                    }
2781
2782                    /**
2783                     * Called when an end tag is seen for the
2784                     * type of tag this action was registered
2785                     * to.  The tag argument indicates the actual
2786                     * tag for those actions that are shared across
2787                     * many tags.  By default this does nothing and
2788                     * completely ignores the tag.
2789                     */
2790                    public void end(HTML.Tag t) {
2791                    }
2792
2793                }
2794
2795                public class BlockAction extends TagAction {
2796
2797                    public void start(HTML.Tag t, MutableAttributeSet attr) {
2798                        blockOpen(t, attr);
2799                    }
2800
2801                    public void end(HTML.Tag t) {
2802                        blockClose(t);
2803                    }
2804                }
2805
2806                /**
2807                 * Action used for the actual element form tag. This is named such
2808                 * as there was already a public class named FormAction.
2809                 */
2810                private class FormTagAction extends BlockAction {
2811                    public void start(HTML.Tag t, MutableAttributeSet attr) {
2812                        super .start(t, attr);
2813                        // initialize a ButtonGroupsMap when
2814                        // FORM tag is encountered.  This will
2815                        // be used for any radio buttons that
2816                        // might be defined in the FORM.
2817                        // for new group new ButtonGroup will be created (fix for 4529702)
2818                        // group name is a key in radioButtonGroupsMap
2819                        radioButtonGroupsMap = new HashMap();
2820                    }
2821
2822                    public void end(HTML.Tag t) {
2823                        super .end(t);
2824                        // reset the button group to null since
2825                        // the form has ended.
2826                        radioButtonGroupsMap = null;
2827                    }
2828                }
2829
2830                public class ParagraphAction extends BlockAction {
2831
2832                    public void start(HTML.Tag t, MutableAttributeSet a) {
2833                        super .start(t, a);
2834                        inParagraph = true;
2835                    }
2836
2837                    public void end(HTML.Tag t) {
2838                        super .end(t);
2839                        inParagraph = false;
2840                    }
2841                }
2842
2843                public class SpecialAction extends TagAction {
2844
2845                    public void start(HTML.Tag t, MutableAttributeSet a) {
2846                        addSpecialElement(t, a);
2847                    }
2848
2849                }
2850
2851                public class IsindexAction extends TagAction {
2852
2853                    public void start(HTML.Tag t, MutableAttributeSet a) {
2854                        blockOpen(HTML.Tag.IMPLIED, new SimpleAttributeSet());
2855                        addSpecialElement(t, a);
2856                        blockClose(HTML.Tag.IMPLIED);
2857                    }
2858
2859                }
2860
2861                public class HiddenAction extends TagAction {
2862
2863                    public void start(HTML.Tag t, MutableAttributeSet a) {
2864                        addSpecialElement(t, a);
2865                    }
2866
2867                    public void end(HTML.Tag t) {
2868                        if (!isEmpty(t)) {
2869                            MutableAttributeSet a = new SimpleAttributeSet();
2870                            a.addAttribute(HTML.Attribute.ENDTAG, "true");
2871                            addSpecialElement(t, a);
2872                        }
2873                    }
2874
2875                    boolean isEmpty(HTML.Tag t) {
2876                        if (t == HTML.Tag.APPLET || t == HTML.Tag.SCRIPT) {
2877                            return false;
2878                        }
2879                        return true;
2880                    }
2881                }
2882
2883                /**
2884                 * Subclass of HiddenAction to set the content type for style sheets,
2885                 * and to set the name of the default style sheet.
2886                 */
2887                class MetaAction extends HiddenAction {
2888
2889                    public void start(HTML.Tag t, MutableAttributeSet a) {
2890                        Object equiv = a.getAttribute(HTML.Attribute.HTTPEQUIV);
2891                        if (equiv != null) {
2892                            equiv = ((String) equiv).toLowerCase();
2893                            if (equiv.equals("content-style-type")) {
2894                                String value = (String) a
2895                                        .getAttribute(HTML.Attribute.CONTENT);
2896                                setDefaultStyleSheetType(value);
2897                                isStyleCSS = "text/css"
2898                                        .equals(getDefaultStyleSheetType());
2899                            } else if (equiv.equals("default-style")) {
2900                                defaultStyle = (String) a
2901                                        .getAttribute(HTML.Attribute.CONTENT);
2902                            }
2903                        }
2904                        super .start(t, a);
2905                    }
2906
2907                    boolean isEmpty(HTML.Tag t) {
2908                        return true;
2909                    }
2910                }
2911
2912                /**
2913                 * End if overridden to create the necessary stylesheets that
2914                 * are referenced via the link tag. It is done in this manner
2915                 * as the meta tag can be used to specify an alternate style sheet,
2916                 * and is not guaranteed to come before the link tags.
2917                 */
2918                class HeadAction extends BlockAction {
2919
2920                    public void start(HTML.Tag t, MutableAttributeSet a) {
2921                        inHead = true;
2922                        // This check of the insertTag is put in to avoid considering
2923                        // the implied-p that is generated for the head. This allows
2924                        // inserts for HR to work correctly.
2925                        if ((insertTag == null && !insertAfterImplied)
2926                                || (insertTag == HTML.Tag.HEAD)
2927                                || (insertAfterImplied && (foundInsertTag || !a
2928                                        .isDefined(IMPLIED)))) {
2929                            super .start(t, a);
2930                        }
2931                    }
2932
2933                    public void end(HTML.Tag t) {
2934                        inHead = inStyle = false;
2935                        // See if there is a StyleSheet to link to.
2936                        if (styles != null) {
2937                            boolean isDefaultCSS = isStyleCSS;
2938                            for (int counter = 0, maxCounter = styles.size(); counter < maxCounter;) {
2939                                Object value = styles.elementAt(counter);
2940                                if (value == HTML.Tag.LINK) {
2941                                    handleLink((AttributeSet) styles
2942                                            .elementAt(++counter));
2943                                    counter++;
2944                                } else {
2945                                    // Rule.
2946                                    // First element gives type.
2947                                    String type = (String) styles
2948                                            .elementAt(++counter);
2949                                    boolean isCSS = (type == null) ? isDefaultCSS
2950                                            : type.equals("text/css");
2951                                    while (++counter < maxCounter
2952                                            && (styles.elementAt(counter) instanceof  String)) {
2953                                        if (isCSS) {
2954                                            addCSSRules((String) styles
2955                                                    .elementAt(counter));
2956                                        }
2957                                    }
2958                                }
2959                            }
2960                        }
2961                        if ((insertTag == null && !insertAfterImplied)
2962                                || insertTag == HTML.Tag.HEAD
2963                                || (insertAfterImplied && foundInsertTag)) {
2964                            super .end(t);
2965                        }
2966                    }
2967
2968                    boolean isEmpty(HTML.Tag t) {
2969                        return false;
2970                    }
2971
2972                    private void handleLink(AttributeSet attr) {
2973                        // Link.
2974                        String type = (String) attr
2975                                .getAttribute(HTML.Attribute.TYPE);
2976                        if (type == null) {
2977                            type = getDefaultStyleSheetType();
2978                        }
2979                        // Only choose if type==text/css
2980                        // Select link if rel==stylesheet.
2981                        // Otherwise if rel==alternate stylesheet and
2982                        //   title matches default style.
2983                        if (type.equals("text/css")) {
2984                            String rel = (String) attr
2985                                    .getAttribute(HTML.Attribute.REL);
2986                            String title = (String) attr
2987                                    .getAttribute(HTML.Attribute.TITLE);
2988                            String media = (String) attr
2989                                    .getAttribute(HTML.Attribute.MEDIA);
2990                            if (media == null) {
2991                                media = "all";
2992                            } else {
2993                                media = media.toLowerCase();
2994                            }
2995                            if (rel != null) {
2996                                rel = rel.toLowerCase();
2997                                if ((media.indexOf("all") != -1 || media
2998                                        .indexOf("screen") != -1)
2999                                        && (rel.equals("stylesheet") || (rel
3000                                                .equals("alternate stylesheet") && title
3001                                                .equals(defaultStyle)))) {
3002                                    linkCSSStyleSheet((String) attr
3003                                            .getAttribute(HTML.Attribute.HREF));
3004                                }
3005                            }
3006                        }
3007                    }
3008                }
3009
3010                /**
3011                 * A subclass to add the AttributeSet to styles if the
3012                 * attributes contains an attribute for 'rel' with value
3013                 * 'stylesheet' or 'alternate stylesheet'.
3014                 */
3015                class LinkAction extends HiddenAction {
3016
3017                    public void start(HTML.Tag t, MutableAttributeSet a) {
3018                        String rel = (String) a
3019                                .getAttribute(HTML.Attribute.REL);
3020                        if (rel != null) {
3021                            rel = rel.toLowerCase();
3022                            if (rel.equals("stylesheet")
3023                                    || rel.equals("alternate stylesheet")) {
3024                                if (styles == null) {
3025                                    styles = new Vector(3);
3026                                }
3027                                styles.addElement(t);
3028                                styles.addElement(a.copyAttributes());
3029                            }
3030                        }
3031                        super .start(t, a);
3032                    }
3033                }
3034
3035                class MapAction extends TagAction {
3036
3037                    public void start(HTML.Tag t, MutableAttributeSet a) {
3038                        lastMap = new Map((String) a
3039                                .getAttribute(HTML.Attribute.NAME));
3040                        addMap(lastMap);
3041                    }
3042
3043                    public void end(HTML.Tag t) {
3044                    }
3045                }
3046
3047                class AreaAction extends TagAction {
3048
3049                    public void start(HTML.Tag t, MutableAttributeSet a) {
3050                        if (lastMap != null) {
3051                            lastMap.addArea(a.copyAttributes());
3052                        }
3053                    }
3054
3055                    public void end(HTML.Tag t) {
3056                    }
3057                }
3058
3059                class StyleAction extends TagAction {
3060
3061                    public void start(HTML.Tag t, MutableAttributeSet a) {
3062                        if (inHead) {
3063                            if (styles == null) {
3064                                styles = new Vector(3);
3065                            }
3066                            styles.addElement(t);
3067                            styles.addElement(a
3068                                    .getAttribute(HTML.Attribute.TYPE));
3069                            inStyle = true;
3070                        }
3071                    }
3072
3073                    public void end(HTML.Tag t) {
3074                        inStyle = false;
3075                    }
3076
3077                    boolean isEmpty(HTML.Tag t) {
3078                        return false;
3079                    }
3080                }
3081
3082                public class PreAction extends BlockAction {
3083
3084                    public void start(HTML.Tag t, MutableAttributeSet attr) {
3085                        inPre = true;
3086                        blockOpen(t, attr);
3087                        attr.addAttribute(CSS.Attribute.WHITE_SPACE, "pre");
3088                        blockOpen(HTML.Tag.IMPLIED, attr);
3089                    }
3090
3091                    public void end(HTML.Tag t) {
3092                        blockClose(HTML.Tag.IMPLIED);
3093                        // set inPre to false after closing, so that if a newline
3094                        // is added it won't generate a blockOpen.
3095                        inPre = false;
3096                        blockClose(t);
3097                    }
3098                }
3099
3100                public class CharacterAction extends TagAction {
3101
3102                    public void start(HTML.Tag t, MutableAttributeSet attr) {
3103                        pushCharacterStyle();
3104                        if (!foundInsertTag) {
3105                            // Note that the third argument should really be based off
3106                            // inParagraph and impliedP. If we're wrong (that is
3107                            // insertTagDepthDelta shouldn't be changed), we'll end up
3108                            // removing an extra EndSpec, which won't matter anyway.
3109                            boolean insert = canInsertTag(t, attr, false);
3110                            if (foundInsertTag) {
3111                                if (!inParagraph) {
3112                                    inParagraph = impliedP = true;
3113                                }
3114                            }
3115                            if (!insert) {
3116                                return;
3117                            }
3118                        }
3119                        if (attr.isDefined(IMPLIED)) {
3120                            attr.removeAttribute(IMPLIED);
3121                        }
3122                        charAttr.addAttribute(t, attr.copyAttributes());
3123                        if (styleAttributes != null) {
3124                            charAttr.addAttributes(styleAttributes);
3125                        }
3126                    }
3127
3128                    public void end(HTML.Tag t) {
3129                        popCharacterStyle();
3130                    }
3131                }
3132
3133                /**
3134                 * Provides conversion of HTML tag/attribute
3135                 * mappings that have a corresponding StyleConstants
3136                 * and CSS mapping.  The conversion is to CSS attributes.
3137                 */
3138                class ConvertAction extends TagAction {
3139
3140                    public void start(HTML.Tag t, MutableAttributeSet attr) {
3141                        pushCharacterStyle();
3142                        if (!foundInsertTag) {
3143                            // Note that the third argument should really be based off
3144                            // inParagraph and impliedP. If we're wrong (that is
3145                            // insertTagDepthDelta shouldn't be changed), we'll end up
3146                            // removing an extra EndSpec, which won't matter anyway.
3147                            boolean insert = canInsertTag(t, attr, false);
3148                            if (foundInsertTag) {
3149                                if (!inParagraph) {
3150                                    inParagraph = impliedP = true;
3151                                }
3152                            }
3153                            if (!insert) {
3154                                return;
3155                            }
3156                        }
3157                        if (attr.isDefined(IMPLIED)) {
3158                            attr.removeAttribute(IMPLIED);
3159                        }
3160                        if (styleAttributes != null) {
3161                            charAttr.addAttributes(styleAttributes);
3162                        }
3163                        // We also need to add attr, otherwise we lose custom
3164                        // attributes, including class/id for style lookups, and
3165                        // further confuse style lookup (doesn't have tag).
3166                        charAttr.addAttribute(t, attr.copyAttributes());
3167                        StyleSheet sheet = getStyleSheet();
3168                        if (t == HTML.Tag.B) {
3169                            sheet.addCSSAttribute(charAttr,
3170                                    CSS.Attribute.FONT_WEIGHT, "bold");
3171                        } else if (t == HTML.Tag.I) {
3172                            sheet.addCSSAttribute(charAttr,
3173                                    CSS.Attribute.FONT_STYLE, "italic");
3174                        } else if (t == HTML.Tag.U) {
3175                            Object v = charAttr
3176                                    .getAttribute(CSS.Attribute.TEXT_DECORATION);
3177                            String value = "underline";
3178                            value = (v != null) ? value + "," + v.toString()
3179                                    : value;
3180                            sheet.addCSSAttribute(charAttr,
3181                                    CSS.Attribute.TEXT_DECORATION, value);
3182                        } else if (t == HTML.Tag.STRIKE) {
3183                            Object v = charAttr
3184                                    .getAttribute(CSS.Attribute.TEXT_DECORATION);
3185                            String value = "line-through";
3186                            value = (v != null) ? value + "," + v.toString()
3187                                    : value;
3188                            sheet.addCSSAttribute(charAttr,
3189                                    CSS.Attribute.TEXT_DECORATION, value);
3190                        } else if (t == HTML.Tag.SUP) {
3191                            Object v = charAttr
3192                                    .getAttribute(CSS.Attribute.VERTICAL_ALIGN);
3193                            String value = "sup";
3194                            value = (v != null) ? value + "," + v.toString()
3195                                    : value;
3196                            sheet.addCSSAttribute(charAttr,
3197                                    CSS.Attribute.VERTICAL_ALIGN, value);
3198                        } else if (t == HTML.Tag.SUB) {
3199                            Object v = charAttr
3200                                    .getAttribute(CSS.Attribute.VERTICAL_ALIGN);
3201                            String value = "sub";
3202                            value = (v != null) ? value + "," + v.toString()
3203                                    : value;
3204                            sheet.addCSSAttribute(charAttr,
3205                                    CSS.Attribute.VERTICAL_ALIGN, value);
3206                        } else if (t == HTML.Tag.FONT) {
3207                            String color = (String) attr
3208                                    .getAttribute(HTML.Attribute.COLOR);
3209                            if (color != null) {
3210                                sheet.addCSSAttribute(charAttr,
3211                                        CSS.Attribute.COLOR, color);
3212                            }
3213                            String face = (String) attr
3214                                    .getAttribute(HTML.Attribute.FACE);
3215                            if (face != null) {
3216                                sheet.addCSSAttribute(charAttr,
3217                                        CSS.Attribute.FONT_FAMILY, face);
3218                            }
3219                            String size = (String) attr
3220                                    .getAttribute(HTML.Attribute.SIZE);
3221                            if (size != null) {
3222                                sheet.addCSSAttributeFromHTML(charAttr,
3223                                        CSS.Attribute.FONT_SIZE, size);
3224                            }
3225                        }
3226                    }
3227
3228                    public void end(HTML.Tag t) {
3229                        popCharacterStyle();
3230                    }
3231
3232                }
3233
3234                class AnchorAction extends CharacterAction {
3235
3236                    public void start(HTML.Tag t, MutableAttributeSet attr) {
3237                        // set flag to catch empty anchors
3238                        emptyAnchor = true;
3239                        super .start(t, attr);
3240                    }
3241
3242                    public void end(HTML.Tag t) {
3243                        if (emptyAnchor) {
3244                            // if the anchor was empty it was probably a
3245                            // named anchor point and we don't want to throw
3246                            // it away.
3247                            char[] one = new char[1];
3248                            one[0] = '\n';
3249                            addContent(one, 0, 1);
3250                        }
3251                        super .end(t);
3252                    }
3253                }
3254
3255                class TitleAction extends HiddenAction {
3256
3257                    public void start(HTML.Tag t, MutableAttributeSet attr) {
3258                        inTitle = true;
3259                        super .start(t, attr);
3260                    }
3261
3262                    public void end(HTML.Tag t) {
3263                        inTitle = false;
3264                        super .end(t);
3265                    }
3266
3267                    boolean isEmpty(HTML.Tag t) {
3268                        return false;
3269                    }
3270                }
3271
3272                class BaseAction extends TagAction {
3273
3274                    public void start(HTML.Tag t, MutableAttributeSet attr) {
3275                        String href = (String) attr
3276                                .getAttribute(HTML.Attribute.HREF);
3277                        if (href != null) {
3278                            try {
3279                                URL newBase = new URL(base, href);
3280                                setBase(newBase);
3281                                hasBaseTag = true;
3282                            } catch (MalformedURLException ex) {
3283                            }
3284                        }
3285                        baseTarget = (String) attr
3286                                .getAttribute(HTML.Attribute.TARGET);
3287                    }
3288                }
3289
3290                class ObjectAction extends SpecialAction {
3291
3292                    public void start(HTML.Tag t, MutableAttributeSet a) {
3293                        if (t == HTML.Tag.PARAM) {
3294                            addParameter(a);
3295                        } else {
3296                            super .start(t, a);
3297                        }
3298                    }
3299
3300                    public void end(HTML.Tag t) {
3301                        if (t != HTML.Tag.PARAM) {
3302                            super .end(t);
3303                        }
3304                    }
3305
3306                    void addParameter(AttributeSet a) {
3307                        String name = (String) a
3308                                .getAttribute(HTML.Attribute.NAME);
3309                        String value = (String) a
3310                                .getAttribute(HTML.Attribute.VALUE);
3311                        if ((name != null) && (value != null)) {
3312                            ElementSpec objSpec = (ElementSpec) parseBuffer
3313                                    .lastElement();
3314                            MutableAttributeSet objAttr = (MutableAttributeSet) objSpec
3315                                    .getAttributes();
3316                            objAttr.addAttribute(name, value);
3317                        }
3318                    }
3319                }
3320
3321                /**
3322                 * Action to support forms by building all of the elements
3323                 * used to represent form controls.  This will process
3324                 * the &lt;INPUT&gt;, &lt;TEXTAREA&gt;, &lt;SELECT&gt;,
3325                 * and &lt;OPTION&gt; tags.  The element created by
3326                 * this action is expected to have the attribute
3327                 * <code>StyleConstants.ModelAttribute</code> set to
3328                 * the model that holds the state for the form control.
3329                 * This enables multiple views, and allows document to
3330                 * be iterated over picking up the data of the form.
3331                 * The following are the model assignments for the
3332                 * various type of form elements.
3333                 * <table summary="model assignments for the various types of form elements">
3334                 * <tr>
3335                 *   <th>Element Type
3336                 *   <th>Model Type
3337                 * <tr>
3338                 *   <td>input, type button
3339                 *   <td>{@link DefaultButtonModel}
3340                 * <tr>
3341                 *   <td>input, type checkbox
3342                 *   <td>{@link javax.swing.JToggleButton.ToggleButtonModel}
3343                 * <tr>
3344                 *   <td>input, type image
3345                 *   <td>{@link DefaultButtonModel}
3346                 * <tr>
3347                 *   <td>input, type password
3348                 *   <td>{@link PlainDocument}
3349                 * <tr>
3350                 *   <td>input, type radio
3351                 *   <td>{@link javax.swing.JToggleButton.ToggleButtonModel}
3352                 * <tr>
3353                 *   <td>input, type reset
3354                 *   <td>{@link DefaultButtonModel}
3355                 * <tr>
3356                 *   <td>input, type submit
3357                 *   <td>{@link DefaultButtonModel}
3358                 * <tr>
3359                 *   <td>input, type text or type is null.
3360                 *   <td>{@link PlainDocument}
3361                 * <tr>
3362                 *   <td>select
3363                 *   <td>{@link DefaultComboBoxModel} or an {@link DefaultListModel}, with an item type of Option
3364                 * <tr>
3365                 *   <td>textarea
3366                 *   <td>{@link PlainDocument}
3367                 * </table>
3368                 *
3369                 */
3370                public class FormAction extends SpecialAction {
3371
3372                    public void start(HTML.Tag t, MutableAttributeSet attr) {
3373                        if (t == HTML.Tag.INPUT) {
3374                            String type = (String) attr
3375                                    .getAttribute(HTML.Attribute.TYPE);
3376                            /*
3377                             * if type is not defined teh default is
3378                             * assumed to be text.
3379                             */
3380                            if (type == null) {
3381                                type = "text";
3382                                attr.addAttribute(HTML.Attribute.TYPE, "text");
3383                            }
3384                            setModel(type, attr);
3385                        } else if (t == HTML.Tag.TEXTAREA) {
3386                            inTextArea = true;
3387                            textAreaDocument = new TextAreaDocument();
3388                            attr.addAttribute(StyleConstants.ModelAttribute,
3389                                    textAreaDocument);
3390                        } else if (t == HTML.Tag.SELECT) {
3391                            int size = HTML.getIntegerAttributeValue(attr,
3392                                    HTML.Attribute.SIZE, 1);
3393                            boolean multiple = ((String) attr
3394                                    .getAttribute(HTML.Attribute.MULTIPLE) != null);
3395                            if ((size > 1) || multiple) {
3396                                OptionListModel m = new OptionListModel();
3397                                if (multiple) {
3398                                    m
3399                                            .setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
3400                                }
3401                                selectModel = m;
3402                            } else {
3403                                selectModel = new OptionComboBoxModel();
3404                            }
3405                            attr.addAttribute(StyleConstants.ModelAttribute,
3406                                    selectModel);
3407
3408                        }
3409
3410                        // build the element, unless this is an option.
3411                        if (t == HTML.Tag.OPTION) {
3412                            option = new Option(attr);
3413
3414                            if (selectModel instanceof  OptionListModel) {
3415                                OptionListModel m = (OptionListModel) selectModel;
3416                                m.addElement(option);
3417                                if (option.isSelected()) {
3418                                    m.addSelectionInterval(optionCount,
3419                                            optionCount);
3420                                    m.setInitialSelection(optionCount);
3421                                }
3422                            } else if (selectModel instanceof  OptionComboBoxModel) {
3423                                OptionComboBoxModel m = (OptionComboBoxModel) selectModel;
3424                                m.addElement(option);
3425                                if (option.isSelected()) {
3426                                    m.setSelectedItem(option);
3427                                    m.setInitialSelection(option);
3428                                }
3429                            }
3430                            optionCount++;
3431                        } else {
3432                            super .start(t, attr);
3433                        }
3434                    }
3435
3436                    public void end(HTML.Tag t) {
3437                        if (t == HTML.Tag.OPTION) {
3438                            option = null;
3439                        } else {
3440                            if (t == HTML.Tag.SELECT) {
3441                                selectModel = null;
3442                                optionCount = 0;
3443                            } else if (t == HTML.Tag.TEXTAREA) {
3444                                inTextArea = false;
3445
3446                                /* Now that the textarea has ended,
3447                                 * store the entire initial text
3448                                 * of the text area.  This will
3449                                 * enable us to restore the initial
3450                                 * state if a reset is requested.
3451                                 */
3452                                textAreaDocument.storeInitialText();
3453                            }
3454                            super .end(t);
3455                        }
3456                    }
3457
3458                    void setModel(String type, MutableAttributeSet attr) {
3459                        if (type.equals("submit") || type.equals("reset")
3460                                || type.equals("image")) {
3461
3462                            // button model
3463                            attr.addAttribute(StyleConstants.ModelAttribute,
3464                                    new DefaultButtonModel());
3465                        } else if (type.equals("text")
3466                                || type.equals("password")) {
3467                            // plain text model
3468                            int maxLength = HTML.getIntegerAttributeValue(attr,
3469                                    HTML.Attribute.MAXLENGTH, -1);
3470                            Document doc;
3471
3472                            if (maxLength > 0) {
3473                                doc = new FixedLengthDocument(maxLength);
3474                            } else {
3475                                doc = new PlainDocument();
3476                            }
3477                            String value = (String) attr
3478                                    .getAttribute(HTML.Attribute.VALUE);
3479                            try {
3480                                doc.insertString(0, value, null);
3481                            } catch (BadLocationException e) {
3482                            }
3483                            attr.addAttribute(StyleConstants.ModelAttribute,
3484                                    doc);
3485                        } else if (type.equals("file")) {
3486                            // plain text model
3487                            attr.addAttribute(StyleConstants.ModelAttribute,
3488                                    new PlainDocument());
3489                        } else if (type.equals("checkbox")
3490                                || type.equals("radio")) {
3491                            JToggleButton.ToggleButtonModel model = new JToggleButton.ToggleButtonModel();
3492                            if (type.equals("radio")) {
3493                                String name = (String) attr
3494                                        .getAttribute(HTML.Attribute.NAME);
3495                                if (radioButtonGroupsMap == null) { //fix for 4772743
3496                                    radioButtonGroupsMap = new HashMap();
3497                                }
3498                                ButtonGroup radioButtonGroup = (ButtonGroup) radioButtonGroupsMap
3499                                        .get(name);
3500                                if (radioButtonGroup == null) {
3501                                    radioButtonGroup = new ButtonGroup();
3502                                    radioButtonGroupsMap.put(name,
3503                                            radioButtonGroup);
3504                                }
3505                                model.setGroup(radioButtonGroup);
3506                            }
3507                            boolean checked = (attr
3508                                    .getAttribute(HTML.Attribute.CHECKED) != null);
3509                            model.setSelected(checked);
3510                            attr.addAttribute(StyleConstants.ModelAttribute,
3511                                    model);
3512                        }
3513                    }
3514
3515                    /**
3516                     * If a &lt;SELECT&gt; tag is being processed, this
3517                     * model will be a reference to the model being filled
3518                     * with the &lt;OPTION&gt; elements (which produce
3519                     * objects of type <code>Option</code>.
3520                     */
3521                    Object selectModel;
3522                    int optionCount;
3523                }
3524
3525                // --- utility methods used by the reader ------------------
3526
3527                /**
3528                 * Pushes the current character style on a stack in preparation
3529                 * for forming a new nested character style.
3530                 */
3531                protected void pushCharacterStyle() {
3532                    charAttrStack.push(charAttr.copyAttributes());
3533                }
3534
3535                /**
3536                 * Pops a previously pushed character style off the stack
3537                 * to return to a previous style.
3538                 */
3539                protected void popCharacterStyle() {
3540                    if (!charAttrStack.empty()) {
3541                        charAttr = (MutableAttributeSet) charAttrStack.peek();
3542                        charAttrStack.pop();
3543                    }
3544                }
3545
3546                /**
3547                 * Adds the given content to the textarea document.
3548                 * This method gets called when we are in a textarea
3549                 * context.  Therefore all text that is seen belongs
3550                 * to the text area and is hence added to the
3551                 * TextAreaDocument associated with the text area.
3552                 */
3553                protected void textAreaContent(char[] data) {
3554                    try {
3555                        textAreaDocument.insertString(textAreaDocument
3556                                .getLength(), new String(data), null);
3557                    } catch (BadLocationException e) {
3558                        // Should do something reasonable
3559                    }
3560                }
3561
3562                /**
3563                 * Adds the given content that was encountered in a 
3564                 * PRE element.  This synthesizes lines to hold the
3565                 * runs of text, and makes calls to addContent to
3566                 * actually add the text.
3567                 */
3568                protected void preContent(char[] data) {
3569                    int last = 0;
3570                    for (int i = 0; i < data.length; i++) {
3571                        if (data[i] == '\n') {
3572                            addContent(data, last, i - last + 1);
3573                            blockClose(HTML.Tag.IMPLIED);
3574                            MutableAttributeSet a = new SimpleAttributeSet();
3575                            a.addAttribute(CSS.Attribute.WHITE_SPACE, "pre");
3576                            blockOpen(HTML.Tag.IMPLIED, a);
3577                            last = i + 1;
3578                        }
3579                    }
3580                    if (last < data.length) {
3581                        addContent(data, last, data.length - last);
3582                    }
3583                }
3584
3585                /**
3586                 * Adds an instruction to the parse buffer to create a 
3587                 * block element with the given attributes.
3588                 */
3589                protected void blockOpen(HTML.Tag t, MutableAttributeSet attr) {
3590                    if (impliedP) {
3591                        blockClose(HTML.Tag.IMPLIED);
3592                    }
3593
3594                    inBlock++;
3595
3596                    if (!canInsertTag(t, attr, true)) {
3597                        return;
3598                    }
3599                    if (attr.isDefined(IMPLIED)) {
3600                        attr.removeAttribute(IMPLIED);
3601                    }
3602                    lastWasNewline = false;
3603                    attr.addAttribute(StyleConstants.NameAttribute, t);
3604                    ElementSpec es = new ElementSpec(attr.copyAttributes(),
3605                            ElementSpec.StartTagType);
3606                    parseBuffer.addElement(es);
3607                }
3608
3609                /**
3610                 * Adds an instruction to the parse buffer to close out
3611                 * a block element of the given type.
3612                 */
3613                protected void blockClose(HTML.Tag t) {
3614                    inBlock--;
3615
3616                    if (!foundInsertTag) {
3617                        return;
3618                    }
3619
3620                    // Add a new line, if the last character wasn't one. This is
3621                    // needed for proper positioning of the cursor. addContent
3622                    // with true will force an implied paragraph to be generated if
3623                    // there isn't one. This may result in a rather bogus structure
3624                    // (perhaps a table with a child pargraph), but the paragraph
3625                    // is needed for proper positioning and display.
3626                    if (!lastWasNewline) {
3627                        pushCharacterStyle();
3628                        charAttr.addAttribute(IMPLIED_CR, Boolean.TRUE);
3629                        addContent(NEWLINE, 0, 1, true);
3630                        popCharacterStyle();
3631                        lastWasNewline = true;
3632                    }
3633
3634                    if (impliedP) {
3635                        impliedP = false;
3636                        inParagraph = false;
3637                        if (t != HTML.Tag.IMPLIED) {
3638                            blockClose(HTML.Tag.IMPLIED);
3639                        }
3640                    }
3641                    // an open/close with no content will be removed, so we
3642                    // add a space of content to keep the element being formed.
3643                    ElementSpec prev = (parseBuffer.size() > 0) ? (ElementSpec) parseBuffer
3644                            .lastElement()
3645                            : null;
3646                    if (prev != null
3647                            && prev.getType() == ElementSpec.StartTagType) {
3648                        char[] one = new char[1];
3649                        one[0] = ' ';
3650                        addContent(one, 0, 1);
3651                    }
3652                    ElementSpec es = new ElementSpec(null,
3653                            ElementSpec.EndTagType);
3654                    parseBuffer.addElement(es);
3655                }
3656
3657                /**
3658                 * Adds some text with the current character attributes.
3659                 *
3660                 * @param data the content to add
3661                 * @param offs the initial offset
3662                 * @param length the length
3663                 */
3664                protected void addContent(char[] data, int offs, int length) {
3665                    addContent(data, offs, length, true);
3666                }
3667
3668                /**
3669                 * Adds some text with the current character attributes.
3670                 *
3671                 * @param data the content to add
3672                 * @param offs the initial offset
3673                 * @param length the length
3674                 * @param generateImpliedPIfNecessary whether to generate implied 
3675                 * paragraphs
3676                 */
3677                protected void addContent(char[] data, int offs, int length,
3678                        boolean generateImpliedPIfNecessary) {
3679                    if (!foundInsertTag) {
3680                        return;
3681                    }
3682
3683                    if (generateImpliedPIfNecessary && (!inParagraph)
3684                            && (!inPre)) {
3685                        blockOpen(HTML.Tag.IMPLIED, new SimpleAttributeSet());
3686                        inParagraph = true;
3687                        impliedP = true;
3688                    }
3689                    emptyAnchor = false;
3690                    charAttr.addAttribute(StyleConstants.NameAttribute,
3691                            HTML.Tag.CONTENT);
3692                    AttributeSet a = charAttr.copyAttributes();
3693                    ElementSpec es = new ElementSpec(a,
3694                            ElementSpec.ContentType, data, offs, length);
3695                    parseBuffer.addElement(es);
3696
3697                    if (parseBuffer.size() > threshold) {
3698                        if (threshold <= MaxThreshold) {
3699                            threshold *= StepThreshold;
3700                        }
3701                        try {
3702                            flushBuffer(false);
3703                        } catch (BadLocationException ble) {
3704                        }
3705                    }
3706                    if (length > 0) {
3707                        lastWasNewline = (data[offs + length - 1] == '\n');
3708                    }
3709                }
3710
3711                /**
3712                 * Adds content that is basically specified entirely
3713                 * in the attribute set.
3714                 */
3715                protected void addSpecialElement(HTML.Tag t,
3716                        MutableAttributeSet a) {
3717                    if ((t != HTML.Tag.FRAME) && (!inParagraph) && (!inPre)) {
3718                        nextTagAfterPImplied = t;
3719                        blockOpen(HTML.Tag.IMPLIED, new SimpleAttributeSet());
3720                        nextTagAfterPImplied = null;
3721                        inParagraph = true;
3722                        impliedP = true;
3723                    }
3724                    if (!canInsertTag(t, a, t.isBlock())) {
3725                        return;
3726                    }
3727                    if (a.isDefined(IMPLIED)) {
3728                        a.removeAttribute(IMPLIED);
3729                    }
3730                    emptyAnchor = false;
3731                    a.addAttributes(charAttr);
3732                    a.addAttribute(StyleConstants.NameAttribute, t);
3733                    char[] one = new char[1];
3734                    one[0] = ' ';
3735                    ElementSpec es = new ElementSpec(a.copyAttributes(),
3736                            ElementSpec.ContentType, one, 0, 1);
3737                    parseBuffer.addElement(es);
3738                    // Set this to avoid generating a newline for frames, frames
3739                    // shouldn't have any content, and shouldn't need a newline.
3740                    if (t == HTML.Tag.FRAME) {
3741                        lastWasNewline = true;
3742                    }
3743                }
3744
3745                /**
3746                 * Flushes the current parse buffer into the document.
3747                 * @param endOfStream true if there is no more content to parser
3748                 */
3749                void flushBuffer(boolean endOfStream)
3750                        throws BadLocationException {
3751                    int oldLength = HTMLDocument.this .getLength();
3752                    int size = parseBuffer.size();
3753                    if (endOfStream
3754                            && (insertTag != null || insertAfterImplied)
3755                            && size > 0) {
3756                        adjustEndSpecsForPartialInsert();
3757                        size = parseBuffer.size();
3758                    }
3759                    ElementSpec[] spec = new ElementSpec[size];
3760                    parseBuffer.copyInto(spec);
3761
3762                    if (oldLength == 0
3763                            && (insertTag == null && !insertAfterImplied)) {
3764                        create(spec);
3765                    } else {
3766                        insert(offset, spec);
3767                    }
3768                    parseBuffer.removeAllElements();
3769                    offset += HTMLDocument.this .getLength() - oldLength;
3770                    flushCount++;
3771                }
3772
3773                /**
3774                 * This will be invoked for the last flush, if <code>insertTag</code>
3775                 * is non null.
3776                 */
3777                private void adjustEndSpecsForPartialInsert() {
3778                    int size = parseBuffer.size();
3779                    if (insertTagDepthDelta < 0) {
3780                        // When inserting via an insertTag, the depths (of the tree
3781                        // being read in, and existing hiearchy) may not match up.
3782                        // This attemps to clean it up.
3783                        int removeCounter = insertTagDepthDelta;
3784                        while (removeCounter < 0
3785                                && size >= 0
3786                                && ((ElementSpec) parseBuffer
3787                                        .elementAt(size - 1)).getType() == ElementSpec.EndTagType) {
3788                            parseBuffer.removeElementAt(--size);
3789                            removeCounter++;
3790                        }
3791                    }
3792                    if (flushCount == 0
3793                            && (!insertAfterImplied || !wantsTrailingNewline)) {
3794                        // If this starts with content (or popDepth > 0 &&
3795                        // pushDepth > 0) and ends with EndTagTypes, make sure
3796                        // the last content isn't a \n, otherwise will end up with
3797                        // an extra \n in the middle of content.
3798                        int index = 0;
3799                        if (pushDepth > 0) {
3800                            if (((ElementSpec) parseBuffer.elementAt(0))
3801                                    .getType() == ElementSpec.ContentType) {
3802                                index++;
3803                            }
3804                        }
3805                        index += (popDepth + pushDepth);
3806                        int cCount = 0;
3807                        int cStart = index;
3808                        while (index < size
3809                                && ((ElementSpec) parseBuffer.elementAt(index))
3810                                        .getType() == ElementSpec.ContentType) {
3811                            index++;
3812                            cCount++;
3813                        }
3814                        if (cCount > 1) {
3815                            while (index < size
3816                                    && ((ElementSpec) parseBuffer
3817                                            .elementAt(index)).getType() == ElementSpec.EndTagType) {
3818                                index++;
3819                            }
3820                            if (index == size) {
3821                                char[] lastText = ((ElementSpec) parseBuffer
3822                                        .elementAt(cStart + cCount - 1))
3823                                        .getArray();
3824                                if (lastText.length == 1
3825                                        && lastText[0] == NEWLINE[0]) {
3826                                    index = cStart + cCount - 1;
3827                                    while (size > index) {
3828                                        parseBuffer.removeElementAt(--size);
3829                                    }
3830                                }
3831                            }
3832                        }
3833                    }
3834                    if (wantsTrailingNewline) {
3835                        // Make sure there is in fact a newline
3836                        for (int counter = parseBuffer.size() - 1; counter >= 0; counter--) {
3837                            ElementSpec spec = (ElementSpec) parseBuffer
3838                                    .elementAt(counter);
3839                            if (spec.getType() == ElementSpec.ContentType) {
3840                                if (spec.getArray()[spec.getLength() - 1] != '\n') {
3841                                    SimpleAttributeSet attrs = new SimpleAttributeSet();
3842
3843                                    attrs.addAttribute(
3844                                            StyleConstants.NameAttribute,
3845                                            HTML.Tag.CONTENT);
3846                                    parseBuffer
3847                                            .insertElementAt(new ElementSpec(
3848                                                    attrs,
3849                                                    ElementSpec.ContentType,
3850                                                    NEWLINE, 0, 1), counter + 1);
3851                                }
3852                                break;
3853                            }
3854                        }
3855                    }
3856                }
3857
3858                /**
3859                 * Adds the CSS rules in <code>rules</code>.
3860                 */
3861                void addCSSRules(String rules) {
3862                    StyleSheet ss = getStyleSheet();
3863                    ss.addRule(rules);
3864                }
3865
3866                /**
3867                 * Adds the CSS stylesheet at <code>href</code> to the known list
3868                 * of stylesheets.
3869                 */
3870                void linkCSSStyleSheet(String href) {
3871                    URL url = null;
3872                    try {
3873                        url = new URL(base, href);
3874                    } catch (MalformedURLException mfe) {
3875                        try {
3876                            url = new URL(href);
3877                        } catch (MalformedURLException mfe2) {
3878                            url = null;
3879                        }
3880                    }
3881                    if (url != null) {
3882                        getStyleSheet().importStyleSheet(url);
3883                    }
3884                }
3885
3886                /**
3887                 * Returns true if can insert starting at <code>t</code>. This 
3888                 * will return false if the insert tag is set, and hasn't been found
3889                 * yet.
3890                 */
3891                private boolean canInsertTag(HTML.Tag t, AttributeSet attr,
3892                        boolean isBlockTag) {
3893                    if (!foundInsertTag) {
3894                        boolean needPImplied = ((t == HTML.Tag.IMPLIED)
3895                                && (!inParagraph) && (!inPre));
3896                        if (needPImplied && (nextTagAfterPImplied != null)) {
3897
3898                            /*
3899                             * If insertTag == null then just proceed to 
3900                             * foundInsertTag() call below and return true. 
3901                             */
3902                            if (insertTag != null) {
3903                                boolean nextTagIsInsertTag = isInsertTag(nextTagAfterPImplied);
3904                                if ((!nextTagIsInsertTag) || (!insertInsertTag)) {
3905                                    return false;
3906                                }
3907                            }
3908                            /*
3909                             *  Proceed to foundInsertTag() call...
3910                             */
3911                        } else if ((insertTag != null && !isInsertTag(t))
3912                                || (insertAfterImplied && (attr == null
3913                                        || attr.isDefined(IMPLIED) || t == HTML.Tag.IMPLIED))) {
3914                            return false;
3915                        }
3916
3917                        // Allow the insert if t matches the insert tag, or
3918                        // insertAfterImplied is true and the element is implied.
3919                        foundInsertTag(isBlockTag);
3920                        if (!insertInsertTag) {
3921                            return false;
3922                        }
3923                    }
3924                    return true;
3925                }
3926
3927                private boolean isInsertTag(HTML.Tag tag) {
3928                    return (insertTag == tag);
3929                }
3930
3931                private void foundInsertTag(boolean isBlockTag) {
3932                    foundInsertTag = true;
3933                    if (!insertAfterImplied && (popDepth > 0 || pushDepth > 0)) {
3934                        try {
3935                            if (offset == 0
3936                                    || !getText(offset - 1, 1).equals("\n")) {
3937                                // Need to insert a newline.
3938                                AttributeSet newAttrs = null;
3939                                boolean joinP = true;
3940
3941                                if (offset != 0) {
3942                                    // Determine if we can use JoinPrevious, we can't
3943                                    // if the Element has some attributes that are
3944                                    // not meant to be duplicated.
3945                                    Element charElement = getCharacterElement(offset - 1);
3946                                    AttributeSet attrs = charElement
3947                                            .getAttributes();
3948
3949                                    if (attrs
3950                                            .isDefined(StyleConstants.ComposedTextAttribute)) {
3951                                        joinP = false;
3952                                    } else {
3953                                        Object name = attrs
3954                                                .getAttribute(StyleConstants.NameAttribute);
3955                                        if (name instanceof  HTML.Tag) {
3956                                            HTML.Tag tag = (HTML.Tag) name;
3957                                            if (tag == HTML.Tag.IMG
3958                                                    || tag == HTML.Tag.HR
3959                                                    || tag == HTML.Tag.COMMENT
3960                                                    || (tag instanceof  HTML.UnknownTag)) {
3961                                                joinP = false;
3962                                            }
3963                                        }
3964                                    }
3965                                }
3966                                if (!joinP) {
3967                                    // If not joining with the previous element, be
3968                                    // sure and set the name (otherwise it will be
3969                                    // inherited).
3970                                    newAttrs = new SimpleAttributeSet();
3971                                    ((SimpleAttributeSet) newAttrs)
3972                                            .addAttribute(
3973                                                    StyleConstants.NameAttribute,
3974                                                    HTML.Tag.CONTENT);
3975                                }
3976                                ElementSpec es = new ElementSpec(newAttrs,
3977                                        ElementSpec.ContentType, NEWLINE, 0,
3978                                        NEWLINE.length);
3979                                if (joinP) {
3980                                    es
3981                                            .setDirection(ElementSpec.JoinPreviousDirection);
3982                                }
3983                                parseBuffer.addElement(es);
3984                            }
3985                        } catch (BadLocationException ble) {
3986                        }
3987                    }
3988                    // pops
3989                    for (int counter = 0; counter < popDepth; counter++) {
3990                        parseBuffer.addElement(new ElementSpec(null,
3991                                ElementSpec.EndTagType));
3992                    }
3993                    // pushes
3994                    for (int counter = 0; counter < pushDepth; counter++) {
3995                        ElementSpec es = new ElementSpec(null,
3996                                ElementSpec.StartTagType);
3997                        es.setDirection(ElementSpec.JoinNextDirection);
3998                        parseBuffer.addElement(es);
3999                    }
4000                    insertTagDepthDelta = depthTo(Math.max(0, offset - 1))
4001                            - popDepth + pushDepth - inBlock;
4002                    if (isBlockTag) {
4003                        // A start spec will be added (for this tag), so we account
4004                        // for it here.
4005                        insertTagDepthDelta++;
4006                    } else {
4007                        // An implied paragraph close (end spec) is going to be added,
4008                        // so we account for it here.
4009                        insertTagDepthDelta--;
4010                        inParagraph = true;
4011                        lastWasNewline = false;
4012                    }
4013                }
4014
4015                /**
4016                 * This is set to true when and end is invoked for <html>.
4017                 */
4018                private boolean receivedEndHTML;
4019                /** Number of times <code>flushBuffer</code> has been invoked. */
4020                private int flushCount;
4021                /** If true, behavior is similiar to insertTag, but instead of
4022                 * waiting for insertTag will wait for first Element without
4023                 * an 'implied' attribute and begin inserting then. */
4024                private boolean insertAfterImplied;
4025                /** This is only used if insertAfterImplied is true. If false, only
4026                 * inserting content, and there is a trailing newline it is removed. */
4027                private boolean wantsTrailingNewline;
4028                int threshold;
4029                int offset;
4030                boolean inParagraph = false;
4031                boolean impliedP = false;
4032                boolean inPre = false;
4033                boolean inTextArea = false;
4034                TextAreaDocument textAreaDocument = null;
4035                boolean inTitle = false;
4036                boolean lastWasNewline = true;
4037                boolean emptyAnchor;
4038                /** True if (!emptyDocument && insertTag == null), this is used so
4039                 * much it is cached. */
4040                boolean midInsert;
4041                /** True when the body has been encountered. */
4042                boolean inBody;
4043                /** If non null, gives parent Tag that insert is to happen at. */
4044                HTML.Tag insertTag;
4045                /** If true, the insertTag is inserted, otherwise elements after
4046                 * the insertTag is found are inserted. */
4047                boolean insertInsertTag;
4048                /** Set to true when insertTag has been found. */
4049                boolean foundInsertTag;
4050                /** When foundInsertTag is set to true, this will be updated to
4051                 * reflect the delta between the two structures. That is, it 
4052                 * will be the depth the inserts are happening at minus the
4053                 * depth of the tags being passed in. A value of 0 (the common
4054                 * case) indicates the structures match, a value greater than 0 indicates
4055                 * the insert is happening at a deeper depth than the stream is
4056                 * parsing, and a value less than 0 indicates the insert is happening earlier
4057                 * in the tree that the parser thinks and that we will need to remove
4058                 * EndTagType specs in the flushBuffer method.
4059                 */
4060                int insertTagDepthDelta;
4061                /** How many parents to ascend before insert new elements. */
4062                int popDepth;
4063                /** How many parents to descend (relative to popDepth) before
4064                 * inserting. */
4065                int pushDepth;
4066                /** Last Map that was encountered. */
4067                Map lastMap;
4068                /** Set to true when a style element is encountered. */
4069                boolean inStyle = false;
4070                /** Name of style to use. Obtained from Meta tag. */
4071                String defaultStyle;
4072                /** Vector describing styles that should be include. Will consist
4073                 * of a bunch of HTML.Tags, which will either be:
4074                 * <p>LINK: in which case it is followed by an AttributeSet
4075                 * <p>STYLE: in which case the following element is a String
4076                 * indicating the type (may be null), and the elements following
4077                 * it until the next HTML.Tag are the rules as Strings.
4078                 */
4079                Vector styles;
4080                /** True if inside the head tag. */
4081                boolean inHead = false;
4082                /** Set to true if the style language is text/css. Since this is
4083                 * used alot, it is cached. */
4084                boolean isStyleCSS;
4085                /** True if inserting into an empty document. */
4086                boolean emptyDocument;
4087                /** Attributes from a style Attribute. */
4088                AttributeSet styleAttributes;
4089
4090                /**
4091                 * Current option, if in an option element (needed to
4092                 * load the label.
4093                 */
4094                Option option;
4095
4096                protected Vector<ElementSpec> parseBuffer = new Vector(); // Vector<ElementSpec>
4097                protected MutableAttributeSet charAttr = new TaggedAttributeSet();
4098                Stack charAttrStack = new Stack();
4099                Hashtable tagMap;
4100                int inBlock = 0;
4101
4102                /** 
4103                 * This attribute is sometimes used to refer to next tag
4104                 * to be handled after p-implied when the latter is 
4105                 * the current tag which is being handled.
4106                 */
4107                private HTML.Tag nextTagAfterPImplied = null;
4108            }
4109
4110            /**
4111             * Used by StyleSheet to determine when to avoid removing HTML.Tags
4112             * matching StyleConstants.
4113             */
4114            static class TaggedAttributeSet extends SimpleAttributeSet {
4115                TaggedAttributeSet() {
4116                    super ();
4117                }
4118            }
4119
4120            /**
4121             * An element that represents a chunk of text that has
4122             * a set of HTML character level attributes assigned to
4123             * it.
4124             */
4125            public class RunElement extends LeafElement {
4126
4127                /**
4128                 * Constructs an element that represents content within the
4129                 * document (has no children).
4130                 *
4131                 * @param parent  the parent element
4132                 * @param a       the element attributes
4133                 * @param offs0   the start offset (must be at least 0)
4134                 * @param offs1   the end offset (must be at least offs0)
4135                 * @since 1.4
4136                 */
4137                public RunElement(Element parent, AttributeSet a, int offs0,
4138                        int offs1) {
4139                    super (parent, a, offs0, offs1);
4140                }
4141
4142                /**
4143                 * Gets the name of the element.
4144                 *
4145                 * @return the name, null if none
4146                 */
4147                public String getName() {
4148                    Object o = getAttribute(StyleConstants.NameAttribute);
4149                    if (o != null) {
4150                        return o.toString();
4151                    }
4152                    return super .getName();
4153                }
4154
4155                /**
4156                 * Gets the resolving parent.  HTML attributes are not inherited
4157                 * at the model level so we override this to return null.
4158                 *
4159                 * @return null, there are none
4160                 * @see AttributeSet#getResolveParent
4161                 */
4162                public AttributeSet getResolveParent() {
4163                    return null;
4164                }
4165            }
4166
4167            /**
4168             * An element that represents a structural <em>block</em> of
4169             * HTML.
4170             */
4171            public class BlockElement extends BranchElement {
4172
4173                /**
4174                 * Constructs a composite element that initially contains
4175                 * no children.
4176                 *
4177                 * @param parent  the parent element
4178                 * @param a       the attributes for the element
4179                 * @since 1.4
4180                 */
4181                public BlockElement(Element parent, AttributeSet a) {
4182                    super (parent, a);
4183                }
4184
4185                /**
4186                 * Gets the name of the element.
4187                 *
4188                 * @return the name, null if none
4189                 */
4190                public String getName() {
4191                    Object o = getAttribute(StyleConstants.NameAttribute);
4192                    if (o != null) {
4193                        return o.toString();
4194                    }
4195                    return super .getName();
4196                }
4197
4198                /**
4199                 * Gets the resolving parent.  HTML attributes are not inherited
4200                 * at the model level so we override this to return null.
4201                 *
4202                 * @return null, there are none
4203                 * @see AttributeSet#getResolveParent
4204                 */
4205                public AttributeSet getResolveParent() {
4206                    return null;
4207                }
4208
4209            }
4210
4211            /**
4212             * Document that allows you to set the maximum length of the text.
4213             */
4214            private static class FixedLengthDocument extends PlainDocument {
4215                private int maxLength;
4216
4217                public FixedLengthDocument(int maxLength) {
4218                    this .maxLength = maxLength;
4219                }
4220
4221                public void insertString(int offset, String str, AttributeSet a)
4222                        throws BadLocationException {
4223                    if (str != null && str.length() + getLength() <= maxLength) {
4224                        super.insertString(offset, str, a);
4225                    }
4226                }
4227            }
4228        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.