FileDocCategorySizeDatePackage
HTMLDocument.javaAPI DocJava SE 6 API127142Tue Jun 10 00:27:00 BST 2008javax.swing.text.html

HTMLDocument

public class HTMLDocument extends DefaultStyledDocument
A document that models HTML. The purpose of this model is to support both browsing and editing. As a result, the structure described by an HTML document is not exactly replicated by default. The element structure that is modeled by default, is built by the class HTMLDocument.HTMLReader, which implements the HTMLEditorKit.ParserCallback protocol that the parser expects. To change the structure one can subclass HTMLReader, and reimplement the method {@link #getReader(int)} to return the new reader implementation. The documentation for HTMLReader should be consulted for the details of the default structure created. The intent is that the document be non-lossy (although reproducing the HTML format may result in a different format).

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

This document supports incremental loading. The TokenThreshold property controls how much of the parse is buffered before trying to update the element structure of the document. This property is set by the EditorKit so that subclasses can disable it.

The Base property determines the URL against which relative URLs are resolved. By default, this will be the Document.StreamDescriptionProperty if the value of the property is a URL. If a <BASE> tag is encountered, the base will become the URL specified by that tag. Because the base URL is a property, it can of course be set directly.

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

Warning: Serialized objects of this class will not be compatible with future Swing releases. The current serialization support is appropriate for short term storage or RMI between applications running the same version of Swing. As of 1.4, support for long term storage of all JavaBeansTM has been added to the java.beans package. Please see {@link java.beans.XMLEncoder}.

author
Timothy Prinzing
author
Scott Violet
author
Sunita Mani
version
1.180 05/10/06

Fields Summary
private boolean
frameDocument
private boolean
preservesUnknownTags
private HashMap
radioButtonGroupsMap
static final String
TokenThreshold
Document property for the number of tokens to buffer before building an element subtree to represent them.
private static final int
MaxThreshold
private static final int
StepThreshold
public static final String
AdditionalComments
Document property key value. The value for the key will be a Vector of Strings that are comments not found in the body.
static final String
StyleType
Document property key value. The value for the key will be a String indicating the default type of stylesheet links.
URL
base
The location to resolve relative URLs against. By default this will be the document's URL if the document was loaded from a URL. If a base tag is found and can be parsed, it will be used as the base location.
boolean
hasBaseTag
does the document have base tag
private String
baseTarget
BASE tag's TARGET attribute value
private HTMLEditorKit$Parser
parser
The parser that is used when inserting html into the existing document.
private static AttributeSet
contentAttributeSet
Used for inserts when a null AttributeSet is supplied.
static String
MAP_PROPERTY
Property Maps are registered under, will be a Hashtable.
private static char[]
NEWLINE
private static final String
IMPLIED_CR
private static final String
I18NProperty
I18N property key.
Constructors Summary
public HTMLDocument()
Constructs an HTML document using the default buffer size and a default StyleSheet. This is a convenience method for the constructor HTMLDocument(Content, StyleSheet).

	this(new GapContent(BUFFER_SIZE_DEFAULT), new StyleSheet());
    
public HTMLDocument(StyleSheet styles)
Constructs an HTML document with the default content storage implementation and the specified style/attribute storage mechanism. This is a convenience method for the constructor HTMLDocument(Content, StyleSheet).

param
styles the styles

	this(new GapContent(BUFFER_SIZE_DEFAULT), styles);
    
public HTMLDocument(Content c, StyleSheet styles)
Constructs an HTML document with the given content storage implementation and the given style/attribute storage mechanism.

param
c the container for the content
param
styles the styles

        super(c, styles);
    
Methods Summary
voidaddMap(javax.swing.text.html.Map map)
Adds the specified map, this will remove a Map that has been previously registered with the same name.

param
map the Map to be registered

	String     name = map.getName();

	if (name != null) {
	    Object     maps = getProperty(MAP_PROPERTY);

	    if (maps == null) {
		maps = new Hashtable(11);
		putProperty(MAP_PROPERTY, maps);
	    }
	    if (maps instanceof Hashtable) {
		((Hashtable)maps).put("#" + name, map);
	    }
	}
    
protected voidcreate(ElementSpec[] data)
Replaces the contents of the document with the given element specifications. This is called before insert if the loading is done in bursts. This is the only method called if loading the document entirely in one burst.

param
data the new contents of the document

	super.create(data);
    
protected javax.swing.text.ElementcreateBranchElement(javax.swing.text.Element parent, javax.swing.text.AttributeSet a)
Creates a document branch element, that can contain other elements. This is implemented to return an element of type HTMLDocument.BlockElement.

param
parent the parent element
param
a the attributes
return
the element

	return new BlockElement(parent, a);
    
protected AbstractElementcreateDefaultRoot()
Creates the root element to be used to represent the default document structure.

return
the element base

	// grabs a write-lock for this initialization and
	// abandon it during initialization so in normal
	// operation we can detect an illegitimate attempt
	// to mutate attributes.
	writeLock();
	MutableAttributeSet a = new SimpleAttributeSet();
	a.addAttribute(StyleConstants.NameAttribute, HTML.Tag.HTML);
	BlockElement html = new BlockElement(null, a.copyAttributes());
	a.removeAttributes(a);
	a.addAttribute(StyleConstants.NameAttribute, HTML.Tag.BODY);
	BlockElement body = new BlockElement(html, a.copyAttributes());
	a.removeAttributes(a);
	a.addAttribute(StyleConstants.NameAttribute, HTML.Tag.P);
        getStyleSheet().addCSSAttributeFromHTML(a, CSS.Attribute.MARGIN_TOP, "0");
	BlockElement paragraph = new BlockElement(body, a.copyAttributes());
	a.removeAttributes(a);
	a.addAttribute(StyleConstants.NameAttribute, HTML.Tag.CONTENT);
	RunElement brk = new RunElement(paragraph, a, 0, 1);
	Element[] buff = new Element[1];
	buff[0] = brk;
	paragraph.replace(0, 0, buff);
	buff[0] = paragraph;
	body.replace(0, 0, buff);
	buff[0] = body;
	html.replace(0, 0, buff);
	writeUnlock();
	return html;
    
protected javax.swing.text.ElementcreateLeafElement(javax.swing.text.Element parent, javax.swing.text.AttributeSet a, int p0, int p1)
Creates a document leaf element that directly represents text (doesn't have any children). This is implemented to return an element of type HTMLDocument.RunElement.

param
parent the parent element
param
a the attributes for the element
param
p0 the beginning of the range (must be at least 0)
param
p1 the end of the range (must be at least p0)
return
the new element

	return new RunElement(parent, a, p0, p1);
    
private javax.swing.text.ElementfindFrame(java.lang.String frameName)
Searches the element hierarchy for an FRAME element that has its name attribute equal to the frameName.

param
frameName
return
the element whose NAME attribute has a value of frameName; returns null if not found

	ElementIterator it = new ElementIterator(this);
	Element next = null;

	while ((next = it.next()) != null) {
	    AttributeSet attr = next.getAttributes();
	    if (matchNameAttribute(attr, HTML.Tag.FRAME)) {
		String frameTarget = (String)attr.getAttribute(HTML.Attribute.NAME);
		if (frameTarget != null && frameTarget.equals(frameName)) {
		    break;
		}
	    }
	}
	return next;
    
protected voidfireChangedUpdate(javax.swing.event.DocumentEvent e)
Notifies all listeners that have registered interest for notification on this event type. The event instance is lazily created using the parameters passed into the fire method.

param
e the event
see
EventListenerList

	super.fireChangedUpdate(e);
    
protected voidfireUndoableEditUpdate(javax.swing.event.UndoableEditEvent e)
Notifies all listeners that have registered interest for notification on this event type. The event instance is lazily created using the parameters passed into the fire method.

param
e the event
see
EventListenerList

	super.fireUndoableEditUpdate(e);
    
public java.net.URLgetBase()
Returns the location to resolve relative URLs against. By default this will be the document's URL if the document was loaded from a URL. If a base tag is found and can be parsed, it will be used as the base location.

return
the base location

	return base;
    
java.lang.StringgetBaseTarget()

	return baseTarget;
    
java.lang.StringgetDefaultStyleSheetType()
Returns the content type language used for style sheets. The default is text/css.

return
the content type language used for the style sheets

	String retValue = (String)getProperty(StyleType);
	if (retValue == null) {
	    return "text/css";
	}
	return retValue;
    
public javax.swing.text.ElementgetElement(java.lang.String id)
Returns the element that has the given id Attribute. If the element can't be found, null is returned. Note that this method works on an Attribute, not a character tag. In the following HTML snippet: <a id="HelloThere"> the attribute is 'id' and the character tag is 'a'. This is a convenience method for getElement(RootElement, HTML.Attribute.id, id). This is not thread-safe.

param
id the string representing the desired Attribute
return
the element with the specified Attribute or null if it can't be found, or null if id is null
see
javax.swing.text.html.HTML.Attribute
since
1.3

	if (id == null) {
	    return null;
	}
	return getElement(getDefaultRootElement(), HTML.Attribute.ID, id,
			  true);
    
public javax.swing.text.ElementgetElement(javax.swing.text.Element e, java.lang.Object attribute, java.lang.Object value)
Returns the child element of e that contains the attribute, attribute with value value, or null if one isn't found. This is not thread-safe.

param
e the root element where the search begins
param
attribute the desired Attribute
param
value the values for the specified Attribute
return
the element with the specified Attribute and the specified value, or null if it can't be found
see
javax.swing.text.html.HTML.Attribute
since
1.3

	return getElement(e, attribute, value, true);
    
private javax.swing.text.ElementgetElement(javax.swing.text.Element e, java.lang.Object attribute, java.lang.Object value, boolean searchLeafAttributes)
Returns the child element of e that contains the attribute, attribute with value value, or null if one isn't found. This is not thread-safe.

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

param
e the root element where the search begins
param
attribute the desired Attribute
param
value the values for the specified Attribute
return
the element with the specified Attribute and the specified value, or null if it can't be found
see
javax.swing.text.html.HTML.Attribute

	AttributeSet attr = e.getAttributes();

	if (attr != null && attr.isDefined(attribute)) {
	    if (value.equals(attr.getAttribute(attribute))) {
		return e;
	    }
	}
	if (!e.isLeaf()) {
	    for (int counter = 0, maxCounter = e.getElementCount();
		 counter < maxCounter; counter++) {
		Element retValue = getElement(e.getElement(counter), attribute,
					      value, searchLeafAttributes);

		if (retValue != null) {
		    return retValue;
		}
	    }
	}
	else if (searchLeafAttributes && attr != null) {
	    // For some leaf elements we store the actual attributes inside
	    // the AttributeSet of the Element (such as anchors).
	    Enumeration names = attr.getAttributeNames();
	    if (names != null) {
		while (names.hasMoreElements()) {
		    Object name = names.nextElement();
		    if ((name instanceof HTML.Tag) &&
			(attr.getAttribute(name) instanceof AttributeSet)) {

			AttributeSet check = (AttributeSet)attr.
			                     getAttribute(name);
			if (check.isDefined(attribute) &&
			    value.equals(check.getAttribute(attribute))) {
			    return e;
			}
		    }
		}
	    }
	}
	return null;
    
public javax.swing.text.html.HTMLDocument$IteratorgetIterator(javax.swing.text.html.HTML$Tag t)
Fetches an iterator for the specified HTML tag. This can be used for things like iterating over the set of anchors contained, or iterating over the input elements.

param
t the requested HTML.Tag
return
the Iterator for the given HTML tag
see
javax.swing.text.html.HTML.Tag

	if (t.isBlock()) {
	    // TBD
	    return null;
	}
	return new LeafIterator(t, this);
    
javax.swing.text.html.MapgetMap(java.lang.String name)
Returns the Map associated with the given name.

param
the name of the desired Map
return
the Map or null if it can't be found, or if name is null

	if (name != null) {
	    Object     maps = getProperty(MAP_PROPERTY);

	    if (maps != null && (maps instanceof Hashtable)) {
		return (Map)((Hashtable)maps).get(name);
	    }
	}
	return null;
    
java.util.EnumerationgetMaps()
Returns an Enumeration of the possible Maps.

return
the enumerated list of maps, or null if the maps are not an instance of Hashtable

	Object     maps = getProperty(MAP_PROPERTY);

	if (maps instanceof Hashtable) {
	    return ((Hashtable)maps).elements();
	}
	return null;
    
public javax.swing.text.html.HTMLEditorKit$ParsergetParser()
Returns the parser that is used when inserting HTML into the existing document.

return
the parser used for text insertion
since
1.3

	Object p = getProperty("__PARSER__");

	if (p instanceof HTMLEditorKit.Parser) {
	    return (HTMLEditorKit.Parser)p;
	}
	return parser;
    
public booleangetPreservesUnknownTags()
Returns the behavior the parser observes when encountering unknown tags.

see
javax.swing.text.html.HTML.Tag
return
true if unknown tags are to be preserved when parsing

	return preservesUnknownTags;
    
public javax.swing.text.html.HTMLEditorKit$ParserCallbackgetReader(int pos)
Fetches the reader for the parser to use when loading the document with HTML. This is implemented to return an instance of HTMLDocument.HTMLReader. Subclasses can reimplement this method to change how the document gets structured if desired. (For example, to handle custom tags, or structurally represent character style elements.)

param
pos the starting position
return
the reader used by the parser to load the document

	Object desc = getProperty(Document.StreamDescriptionProperty);
	if (desc instanceof URL) { 
	    setBase((URL)desc);
	}
	HTMLReader reader = new HTMLReader(pos);
	return reader;
    
public javax.swing.text.html.HTMLEditorKit$ParserCallbackgetReader(int pos, int popDepth, int pushDepth, javax.swing.text.html.HTML$Tag insertTag)
Returns the reader for the parser to use to load the document with HTML. This is implemented to return an instance of HTMLDocument.HTMLReader. Subclasses can reimplement this method to change how the document gets structured if desired. (For example, to handle custom tags, or structurally represent character style elements.)

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

param
popDepth the number of ElementSpec.EndTagTypes to generate before inserting
param
pushDepth the number of ElementSpec.StartTagTypes with a direction of ElementSpec.JoinNextDirection that should be generated before inserting, but after the end tags have been generated
param
insertTag the first tag to start inserting into document
return
the reader used by the parser to load the document

	return getReader(pos, popDepth, pushDepth, insertTag, true);
    
javax.swing.text.html.HTMLEditorKit$ParserCallbackgetReader(int pos, int popDepth, int pushDepth, javax.swing.text.html.HTML$Tag insertTag, boolean insertInsertTag)
Fetches the reader for the parser to use to load the document with HTML. This is implemented to return an instance of HTMLDocument.HTMLReader. Subclasses can reimplement this method to change how the document get structured if desired (e.g. to handle custom tags, structurally represent character style elements, etc.).

param
popDepth the number of ElementSpec.EndTagTypes to generate before inserting
param
pushDepth the number of ElementSpec.StartTagTypes with a direction of ElementSpec.JoinNextDirection that should be generated before inserting, but after the end tags have been generated
param
insertTag the first tag to start inserting into document
param
insertInsertTag false if all the Elements after insertTag should be inserted; otherwise insertTag will be inserted
return
the reader used by the parser to load the document

	Object desc = getProperty(Document.StreamDescriptionProperty);
	if (desc instanceof URL) { 
	    setBase((URL)desc);
	}
	HTMLReader reader = new HTMLReader(pos, popDepth, pushDepth,
					   insertTag, insertInsertTag, false,
					   true);
	return reader;
    
public javax.swing.text.html.StyleSheetgetStyleSheet()
Fetches the StyleSheet with the document-specific display rules (CSS) that were specified in the HTML document itself.

return
the StyleSheet

	return (StyleSheet) getAttributeContext();
    
public intgetTokenThreshold()
Gets the number of tokens to buffer before trying to update the documents element structure. The default value is Integer.MAX_VALUE.

return
the number of tokens to buffer

	Integer i = (Integer) getProperty(TokenThreshold);
	if (i != null) {
	    return i.intValue();
	}
	return Integer.MAX_VALUE;
    
booleanhasBaseTag()

	return hasBaseTag;
    
protected voidinsert(int offset, ElementSpec[] data)
Inserts new elements in bulk. This is how elements get created in the document. The parsing determines what structure is needed and creates the specification as a set of tokens that describe the edit while leaving the document free of a write-lock. This method can then be called in bursts by the reader to acquire a write-lock for a shorter duration (i.e. while the document is actually being altered).

param
offset the starting offset
param
data the element data
exception
BadLocationException if the given position does not represent a valid location in the associated document.

	super.insert(offset, data);
    
public voidinsertAfterEnd(javax.swing.text.Element elem, java.lang.String htmlText)
Inserts the HTML specified as a string after the the end of the given element.

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

param
elem the element to be the root for the new text
param
htmlText the string to be parsed and assigned to elem
throws
IllegalStateException if an HTMLEditorKit.Parser has not been set on the document
since
1.3

	verifyParser();
	if (elem != null) {
	    Element parent = elem.getParentElement();

	    if (parent != null) {
		int offset = elem.getEndOffset();
                if (offset > getLength()) {
                    offset--;
                }
		else if (elem.isLeaf() && getText(offset - 1, 1).
		    charAt(0) == NEWLINE[0]) {
		    offset--;
		}
		insertHTML(parent, offset, htmlText, false);
	    }
	}
    
public voidinsertAfterStart(javax.swing.text.Element elem, java.lang.String htmlText)
Inserts the HTML specified as a string at the start of the element.

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

param
elem the branch element to be the root for the new text
param
htmlText the string to be parsed and assigned to elem
throws
IllegalStateException if an HTMLEditorKit.Parser has not been set on the document
since
1.3

	verifyParser();
	if (elem != null && elem.isLeaf()) {
	    throw new IllegalArgumentException
		("Can not insert HTML after start of a leaf");
	}
	insertHTML(elem, elem.getStartOffset(), htmlText, false);
    
public voidinsertBeforeEnd(javax.swing.text.Element elem, java.lang.String htmlText)
Inserts the HTML specified as a string at the end of the element.

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

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

param
elem the element to be the root for the new text
param
htmlText the string to be parsed and assigned to elem
throws
IllegalStateException if an HTMLEditorKit.Parser has not been set on the document
since
1.3

	verifyParser();
	if (elem != null && elem.isLeaf()) {
	    throw new IllegalArgumentException
		("Can not set inner HTML before end of leaf");
	}
	int offset = elem.getEndOffset();
	if (elem.getElement(elem.getElementIndex(offset - 1)).isLeaf() &&
	    getText(offset - 1, 1).charAt(0) == NEWLINE[0]) {
	    offset--;
	}
	insertHTML(elem, offset, htmlText, false);
    
public voidinsertBeforeStart(javax.swing.text.Element elem, java.lang.String htmlText)
Inserts the HTML specified as a string before the start of the given element.

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

param
elem the element to be the root for the new text
param
htmlText the string to be parsed and assigned to elem
throws
IllegalStateException if an HTMLEditorKit.Parser has not been set on the document
since
1.3

	verifyParser();
	if (elem != null) {
	    Element parent = elem.getParentElement();

	    if (parent != null) {
		insertHTML(parent, elem.getStartOffset(), htmlText, false);
	    }
	}
    
private voidinsertHTML(javax.swing.text.Element parent, int offset, java.lang.String html, boolean wantsTrailingNewline)
Inserts a string of HTML into the document at the given position. parent is used to identify the location to insert the html. If parent is a leaf this can have unexpected results.

	if (parent != null && html != null) {
	    HTMLEditorKit.Parser parser = getParser();
	    if (parser != null) {
		int lastOffset = Math.max(0, offset - 1);
		Element charElement = getCharacterElement(lastOffset);
		Element commonParent = parent;
		int pop = 0;
		int push = 0;

		if (parent.getStartOffset() > lastOffset) {
		    while (commonParent != null &&
			   commonParent.getStartOffset() > lastOffset) {
			commonParent = commonParent.getParentElement();
			push++;
		    }
		    if (commonParent == null) {
			throw new BadLocationException("No common parent",
						       offset);
		    }
		}
		while (charElement != null && charElement != commonParent) {
		    pop++;
		    charElement = charElement.getParentElement();
		}
		if (charElement != null) {
		    // Found it, do the insert.
		    HTMLReader reader = new HTMLReader(offset, pop - 1, push,
						       null, false, true,
						       wantsTrailingNewline);

		    parser.parse(new StringReader(html), reader, true);
		    reader.flush();
		}
	    }
	}
    
protected voidinsertUpdate(DefaultDocumentEvent chng, javax.swing.text.AttributeSet attr)
Updates document structure as a result of text insertion. This will happen within a write lock. This implementation simply parses the inserted content for line breaks and builds up a set of instructions for the element buffer.

param
chng a description of the document change
param
attr the attributes

	if(attr == null) {
	    attr = contentAttributeSet;
	}

	// If this is the composed text element, merge the content attribute to it
	else if (attr.isDefined(StyleConstants.ComposedTextAttribute)) {
	    ((MutableAttributeSet)attr).addAttributes(contentAttributeSet);
	}

	if (attr.isDefined(IMPLIED_CR)) {
	    ((MutableAttributeSet)attr).removeAttribute(IMPLIED_CR);
	}

	super.insertUpdate(chng, attr);
    
private voidinstallParserIfNecessary()
Installs a default Parser if one has not been installed yet.

        if (getParser() == null) {
            setParser(new HTMLEditorKit().getParser());
        }
    
booleanisFrameDocument()
Returns true if the document will be viewed in a frame.

return
true if document will be viewed in a frame, otherwise false

	return frameDocument;
    
static booleanmatchNameAttribute(javax.swing.text.AttributeSet attr, javax.swing.text.html.HTML$Tag tag)
Returns true if StyleConstants.NameAttribute is equal to the tag that is passed in as a parameter.

param
attr the attributes to be matched
param
tag the value to be matched
return
true if there is a match, false otherwise
see
javax.swing.text.html.HTML.Attribute

	Object o = attr.getAttribute(StyleConstants.NameAttribute);
	if (o instanceof HTML.Tag) {
	    HTML.Tag name = (HTML.Tag) o;
	    if (name == tag) {
		return true;
	    }
	}
	return false;
    
voidobtainLock()

	writeLock();
    
public voidprocessHTMLFrameHyperlinkEvent(javax.swing.text.html.HTMLFrameHyperlinkEvent e)
Processes HyperlinkEvents that are generated by documents in an HTML frame. The HyperlinkEvent type, as the parameter suggests, is HTMLFrameHyperlinkEvent. In addition to the typical information contained in a HyperlinkEvent, this event contains the element that corresponds to the frame in which the click happened (the source element) and the target name. The target name has 4 possible values:
  • _self
  • _parent
  • _top
  • a named frame
If target is _self, the action is to change the value of the HTML.Attribute.SRC attribute and fires a ChangedUpdate event.

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

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

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

param
e the event

 	String frameName = e.getTarget();
	Element element = e.getSourceElement();
	String urlStr = e.getURL().toString();

	if (frameName.equals("_self")) {
	    /*
	      The source and destination elements
	      are the same.
	    */
	    updateFrame(element, urlStr);
	} else if (frameName.equals("_parent")) {
	    /*
	      The destination is the parent of the frame.
	    */
	    updateFrameSet(element.getParentElement(), urlStr);
	} else {
	    /*
	      locate a named frame
	    */
	    Element targetElement = findFrame(frameName);
	    if (targetElement != null) {
		updateFrame(targetElement, urlStr);
	    }
	}
    
voidreleaseLock()

	writeUnlock();
    
private voidremoveElements(javax.swing.text.Element e, int index, int count)
Removes child Elements of the passed in Element e. This will do the necessary cleanup to ensure the element representing the end character is correctly created.

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

	writeLock();
	try {
	    int start = e.getElement(index).getStartOffset();
	    int end = e.getElement(index + count - 1).getEndOffset();
	    if (end > getLength()) {
		removeElementsAtEnd(e, index, count, start, end);
	    }
	    else {
		removeElements(e, index, count, start, end);
	    }
	} finally {
	    writeUnlock();
	}
    
private voidremoveElements(javax.swing.text.Element e, int index, int count, int start, int end)
Called to remove child Elements when the end is not touched.

	Element[] removed = new Element[count];
	Element[] added = new Element[0];
	for (int counter = 0; counter < count; counter++) {
	    removed[counter] = e.getElement(counter + index);
	}
	DefaultDocumentEvent dde = new DefaultDocumentEvent
		(start, end - start, DocumentEvent.EventType.REMOVE);
	((AbstractDocument.BranchElement)e).replace(index, removed.length,
						    added);
	dde.addEdit(new ElementEdit(e, index, removed, added));
	UndoableEdit u = getContent().remove(start, end - start);
	if (u != null) {
	    dde.addEdit(u);
	}
	postRemoveUpdate(dde);
	dde.end();
	fireRemoveUpdate(dde);
	if (u != null) {
	    fireUndoableEditUpdate(new UndoableEditEvent(this, dde));
	}
    
private voidremoveElementsAtEnd(javax.swing.text.Element e, int index, int count, int start, int end)
Called to remove child elements of e when one of the elements to remove is representing the end character.

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

	// index must be > 0 otherwise no insert would have happened.
	boolean isLeaf = (e.getElement(index - 1).isLeaf());
        DefaultDocumentEvent dde = new DefaultDocumentEvent(
                       start - 1, end - start + 1, DocumentEvent.
                       EventType.REMOVE);

	if (isLeaf) {
            Element endE = getCharacterElement(getLength());
            // e.getElement(index - 1) should represent the newline.
            index--;
            if (endE.getParentElement() != e) {
                // The hiearchies don't match, we'll have to manually
                // recreate the leaf at e.getElement(index - 1)
                replace(dde, e, index, ++count, start, end, true, true);
            }
            else {
                // The hierarchies for the end Element and
                // e.getElement(index - 1), match, we can safely remove
                // the Elements and the end content will be aligned
                // appropriately.
                replace(dde, e, index, count, start, end, true, false);
            }
        }
        else {
	    // Not a leaf, descend until we find the leaf representing
	    // start - 1 and remove it.
	    Element newLineE = e.getElement(index - 1);
	    while (!newLineE.isLeaf()) {
		newLineE = newLineE.getElement(newLineE.getElementCount() - 1);
	    }
            newLineE = newLineE.getParentElement();
            replace(dde, e, index, count, start, end, false, false);
            replace(dde, newLineE, newLineE.getElementCount() - 1, 1, start,
                    end, true, true);
        }
	postRemoveUpdate(dde);
	dde.end();
	fireRemoveUpdate(dde);
        fireUndoableEditUpdate(new UndoableEditEvent(this, dde));
    
voidremoveMap(javax.swing.text.html.Map map)
Removes a previously registered map.

param
map the Map to be removed

	String     name = map.getName();

	if (name != null) {
	    Object     maps = getProperty(MAP_PROPERTY);

	    if (maps instanceof Hashtable) {
		((Hashtable)maps).remove("#" + name);
	    }
	}
    
private voidreplace(DefaultDocumentEvent dde, javax.swing.text.Element e, int index, int count, int start, int end, boolean remove, boolean create)
This is used by removeElementsAtEnd, it removes count elements starting at start from e. If remove is true text of length start - 1 to end - 1 is removed. If create is true a new leaf is created of length 1.

        Element[] added;
        AttributeSet attrs = e.getElement(index).getAttributes();
        Element[] removed = new Element[count];

        for (int counter = 0; counter < count; counter++) {
            removed[counter] = e.getElement(counter + index);
        }
        if (remove) {
            UndoableEdit u = getContent().remove(start - 1, end - start);
            if (u != null) {
                dde.addEdit(u);
            }
        }
        if (create) {
            added = new Element[1];
            added[0] = createLeafElement(e, attrs, start - 1, start);
        }
        else {
            added = new Element[0];
        }
        dde.addEdit(new ElementEdit(e, index, removed, added));
        ((AbstractDocument.BranchElement)e).replace(
                                             index, removed.length, added);
    
public voidsetBase(java.net.URL u)
Sets the location to resolve relative URLs against. By default this will be the document's URL if the document was loaded from a URL. If a base tag is found and can be parsed, it will be used as the base location.

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

param
u the desired base URL

	base = u;
	getStyleSheet().setBase(u);
    
voidsetDefaultStyleSheetType(java.lang.String contentType)
Sets the content type language used for style sheets that do not explicitly specify the type. The default is text/css.

param
contentType the content type language for the style sheets

	putProperty(StyleType, contentType);
    
voidsetFrameDocumentState(boolean frameDoc)
Sets a boolean state about whether the document will be viewed in a frame.

param
frameDoc true if the document will be viewed in a frame, otherwise false

 	this.frameDocument = frameDoc;
    
public voidsetInnerHTML(javax.swing.text.Element elem, java.lang.String htmlText)
Replaces the children of the given element with the contents specified as an HTML string.

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

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

param
elem the branch element whose children will be replaced
param
htmlText the string to be parsed and assigned to elem
throws
IllegalArgumentException if elem is a leaf
throws
IllegalStateException if an HTMLEditorKit.Parser has not been defined
since
1.3

	verifyParser();
	if (elem != null && elem.isLeaf()) {
	    throw new IllegalArgumentException
		("Can not set inner HTML of a leaf");
	}
	if (elem != null && htmlText != null) {
	    int oldCount = elem.getElementCount();
	    int insertPosition = elem.getStartOffset();
	    insertHTML(elem, elem.getStartOffset(), htmlText, true);
	    if (elem.getElementCount() > oldCount) {
		// Elements were inserted, do the cleanup.
		removeElements(elem, elem.getElementCount() - oldCount,
			       oldCount);
	    }
	}
    
public voidsetOuterHTML(javax.swing.text.Element elem, java.lang.String htmlText)
Replaces the given element in the parent with the contents specified as an HTML string.

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

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

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

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

param
elem the branch element whose children will be replaced
param
htmlText the string to be parsed and assigned to elem
throws
IllegalStateException if an HTMLEditorKit.Parser has not been set
since
1.3

	verifyParser();
	if (elem != null && elem.getParentElement() != null &&
	    htmlText != null) {
	    int start = elem.getStartOffset();
	    int end = elem.getEndOffset();
	    int startLength = getLength();
	    // We don't want a newline if elem is a leaf, and doesn't contain
	    // a newline.
	    boolean wantsNewline = !elem.isLeaf();
	    if (!wantsNewline && (end > startLength ||
				 getText(end - 1, 1).charAt(0) == NEWLINE[0])){
		wantsNewline = true;
	    }
	    Element parent = elem.getParentElement();
	    int oldCount = parent.getElementCount();
	    insertHTML(parent, start, htmlText, wantsNewline);
	    // Remove old.
	    int newLength = getLength();
	    if (oldCount != parent.getElementCount()) {
		int removeIndex = parent.getElementIndex(start + newLength -
							 startLength);
		removeElements(parent, removeIndex, 1);
	    }
	}
    
public voidsetParagraphAttributes(int offset, int length, javax.swing.text.AttributeSet s, boolean replace)
Sets attributes for a paragraph.

This method is thread safe, although most Swing methods are not. Please see How to Use Threads for more information.

param
offset the offset into the paragraph (must be at least 0)
param
length the number of characters affected (must be at least 0)
param
s the attributes
param
replace whether to replace existing attributes, or merge them

	try {
	    writeLock();
	    // Make sure we send out a change for the length of the paragraph.
	    int end = Math.min(offset + length, getLength());
	    Element e = getParagraphElement(offset);
	    offset = e.getStartOffset();
	    e = getParagraphElement(end);
	    length = Math.max(0, e.getEndOffset() - offset);
	    DefaultDocumentEvent changes = 
		new DefaultDocumentEvent(offset, length,
					 DocumentEvent.EventType.CHANGE);
	    AttributeSet sCopy = s.copyAttributes();
	    int lastEnd = Integer.MAX_VALUE;
	    for (int pos = offset; pos <= end; pos = lastEnd) {
		Element paragraph = getParagraphElement(pos);
		if (lastEnd == paragraph.getEndOffset()) {
		    lastEnd++;
		}
		else {
		    lastEnd = paragraph.getEndOffset();
		}
		MutableAttributeSet attr = 
		    (MutableAttributeSet) paragraph.getAttributes();
		changes.addEdit(new AttributeUndoableEdit(paragraph, sCopy, replace));
		if (replace) {
		    attr.removeAttributes(attr);
		}
		attr.addAttributes(s);
	    }
	    changes.end();
	    fireChangedUpdate(changes);
	    fireUndoableEditUpdate(new UndoableEditEvent(this, changes));
	} finally {
	    writeUnlock();
	}
    
public voidsetParser(javax.swing.text.html.HTMLEditorKit$Parser parser)
Sets the parser that is used by the methods that insert html into the existing document, such as setInnerHTML, and setOuterHTML.

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

param
parser the parser to be used for text insertion
since
1.3

	this.parser = parser;
	putProperty("__PARSER__", null);
    
public voidsetPreservesUnknownTags(boolean preservesTags)
Determines how unknown tags are handled by the parser. If set to true, unknown tags are put in the model, otherwise they are dropped.

param
preservesTags true if unknown tags should be saved in the model, otherwise tags are dropped
see
javax.swing.text.html.HTML.Tag

	preservesUnknownTags = preservesTags;
    
public voidsetTokenThreshold(int n)
Sets the number of tokens to buffer before trying to update the documents element structure.

param
n the number of tokens to buffer

	putProperty(TokenThreshold, new Integer(n));
    
private voidupdateFrame(javax.swing.text.Element element, java.lang.String url)
Updates the Frame elements HTML.Attribute.SRC attribute and fires a ChangedUpdate event.

param
element a FRAME element whose SRC attribute will be updated
param
url a string specifying the new value for the SRC attribute


	try {
	    writeLock();
	    DefaultDocumentEvent changes = new DefaultDocumentEvent(element.getStartOffset(),
								    1,
								    DocumentEvent.EventType.CHANGE);
	    AttributeSet sCopy = element.getAttributes().copyAttributes();
	    MutableAttributeSet attr = (MutableAttributeSet) element.getAttributes();
	    changes.addEdit(new AttributeUndoableEdit(element, sCopy, false));
	    attr.removeAttribute(HTML.Attribute.SRC);
	    attr.addAttribute(HTML.Attribute.SRC, url);
	    changes.end();
	    fireChangedUpdate(changes);
	    fireUndoableEditUpdate(new UndoableEditEvent(this, changes));
	} finally {
	    writeUnlock();
	}
    
private voidupdateFrameSet(javax.swing.text.Element element, java.lang.String url)
Replaces a frameset branch Element with a frame leaf element.

param
element the frameset element to remove
param
url the value for the SRC attribute for the new frame that will replace the frameset

	try {
	    int startOffset = element.getStartOffset();
	    int endOffset = Math.min(getLength(), element.getEndOffset());
            String html = "<frame";
            if (url != null) {
                html += " src=\"" + url + "\"";
            }
            html += ">";
            installParserIfNecessary();
            setOuterHTML(element, html);
	} catch (BadLocationException e1) {
	    // Should handle this better
	} catch (IOException ioe) {
	    // Should handle this better
	}
    
private voidverifyParser()
Verifies the document has an HTMLEditorKit.Parser set. If getParser returns null, this will throw an IllegalStateException.

throws
IllegalStateException if the document does not have a Parser

	if (getParser() == null) {
	    throw new IllegalStateException("No HTMLEditorKit.Parser");
	}