FileDocCategorySizeDatePackage
PlainDocument.javaAPI DocJava SE 5 API11104Fri Aug 26 14:58:16 BST 2005javax.swing.text

PlainDocument

public class PlainDocument extends AbstractDocument
A plain document that maintains no character attributes. The default element structure for this document is a map of the lines in the text. The Element returned by getDefaultRootElement is a map of the lines, and each child element represents a line. This model does not maintain any character level attributes, but each line can be tagged with an arbitrary set of attributes. Line to offset, and offset to line translations can be quickly performed using the default root element. The structure information of the DocumentEvent's fired by edits will indicate the line structure changes.

The default content storage management is performed by a gapped buffer implementation (GapContent). It supports editing reasonably large documents with good efficiency when the edits are contiguous or clustered, as is typical.

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
version
1.43 12/19/03
see
Document
see
AbstractDocument

Fields Summary
public static final String
tabSizeAttribute
Name of the attribute that specifies the tab size for tabs contained in the content. The type for the value is Integer.
public static final String
lineLimitAttribute
Name of the attribute that specifies the maximum length of a line, if there is a maximum length. The type for the value is Integer.
private AbstractElement
defaultRoot
private Vector
added
private Vector
removed
private transient Segment
s
Constructors Summary
public PlainDocument()
Constructs a plain text document. A default model using GapContent is constructed and set.


                         
      
	this(new GapContent());
    
public PlainDocument(Content c)
Constructs a plain text document. A default root element is created, and the tab size set to 8.

param
c the container for the content

	super(c);
	putProperty(tabSizeAttribute, new Integer(8));
	defaultRoot = createDefaultRoot();
    
Methods Summary
protected AbstractElementcreateDefaultRoot()
Creates the root element to be used to represent the default document structure.

return
the element base

	BranchElement map = (BranchElement) createBranchElement(null, null);
	Element line = createLeafElement(map, null, 0, 1);
	Element[] lines = new Element[1];
	lines[0] = line;
	map.replace(0, 0, lines);
	return map;
    
public javax.swing.text.ElementgetDefaultRootElement()
Gets the default root element for the document model.

return
the root
see
Document#getDefaultRootElement

	return defaultRoot;
    
public javax.swing.text.ElementgetParagraphElement(int pos)
Get the paragraph element containing the given position. Since this document only models lines, it returns the line instead.

        Element lineMap = getDefaultRootElement();
        return lineMap.getElement( lineMap.getElementIndex( pos ) );
    
private voidinsertComposedTextUpdate(DefaultDocumentEvent chng, javax.swing.text.AttributeSet attr)

	added.removeAllElements();
	BranchElement lineMap = (BranchElement) getDefaultRootElement();
	int offset = chng.getOffset();
	int length = chng.getLength();
	int index = lineMap.getElementIndex(offset);
	Element elem = lineMap.getElement(index);
	int elemStart = elem.getStartOffset();
	int elemEnd = elem.getEndOffset();
	BranchElement[] abelem = new BranchElement[1];
	abelem[0] = (BranchElement) createBranchElement(lineMap, null);
	Element[] relem = new Element[1];
	relem[0] = elem;
	if (elemStart != offset) 
	    added.addElement(createLeafElement(abelem[0], null, elemStart, offset));
	added.addElement(createLeafElement(abelem[0], attr, offset, offset+length));
	if (elemEnd != offset+length) 
	    added.addElement(createLeafElement(abelem[0], null, offset+length, elemEnd));
	Element[] alelem = new Element[added.size()];
	added.copyInto(alelem);
	ElementEdit ee = new ElementEdit(lineMap, index, relem, abelem);
	chng.addEdit(ee);

	abelem[0].replace(0, 0, alelem);
	lineMap.replace(index, 1, abelem);
    
public voidinsertString(int offs, java.lang.String str, javax.swing.text.AttributeSet a)
Inserts some content into the document. Inserting content causes a write lock to be held while the actual changes are taking place, followed by notification to the observers on the thread that grabbed the write lock.

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

param
offs the starting offset >= 0
param
str the string to insert; does nothing with null/empty strings
param
a the attributes for the inserted content
exception
BadLocationException the given insert position is not a valid position within the document
see
Document#insertString

	// fields don't want to have multiple lines.  We may provide a field-specific
	// model in the future in which case the filtering logic here will no longer
	// be needed.
	Object filterNewlines = getProperty("filterNewlines");
	if ((filterNewlines instanceof Boolean) && filterNewlines.equals(Boolean.TRUE)) {
	    if ((str != null) && (str.indexOf('\n") >= 0)) {
		StringBuffer filtered = new StringBuffer(str);
		int n = filtered.length();
		for (int i = 0; i < n; i++) {
		    if (filtered.charAt(i) == '\n") {
			filtered.setCharAt(i, ' ");
		    }
		}
		str = filtered.toString();
	    }
	}
	super.insertString(offs, str, a);
    
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. Since this document simply maps out lines, we refresh the line map.

param
chng the change event describing the dit
param
attr the set of attributes for the inserted text

	removed.removeAllElements();
	added.removeAllElements();
	BranchElement lineMap = (BranchElement) getDefaultRootElement();
	int offset = chng.getOffset();
	int length = chng.getLength();
	if (offset > 0) {
	  offset -= 1;
	  length += 1;
	}
	int index = lineMap.getElementIndex(offset);
	Element rmCandidate = lineMap.getElement(index);
	int rmOffs0 = rmCandidate.getStartOffset();
	int rmOffs1 = rmCandidate.getEndOffset();
	int lastOffset = rmOffs0;
	try {
            if (s == null) {
                s = new Segment();
            }
            getContent().getChars(offset, length, s);
	    boolean hasBreaks = false;
	    for (int i = 0; i < length; i++) {
                char c = s.array[s.offset + i];
		if (c == '\n") {
		    int breakOffset = offset + i + 1;
		    added.addElement(createLeafElement(lineMap, null, lastOffset, breakOffset));
		    lastOffset = breakOffset;
		    hasBreaks = true;
		}
	    }
	    if (hasBreaks) {
		int rmCount = 1;
		removed.addElement(rmCandidate);
		if ((offset + length == rmOffs1) && (lastOffset != rmOffs1) &&
		    ((index+1) < lineMap.getElementCount())) {
		    rmCount += 1;
		    Element e = lineMap.getElement(index+1);
		    removed.addElement(e);
		    rmOffs1 = e.getEndOffset();
		}
		if (lastOffset < rmOffs1) {
		    added.addElement(createLeafElement(lineMap, null, lastOffset, rmOffs1));
		}

		Element[] aelems = new Element[added.size()];
		added.copyInto(aelems);
		Element[] relems = new Element[removed.size()];
		removed.copyInto(relems);
		ElementEdit ee = new ElementEdit(lineMap, index, relems, aelems);
		chng.addEdit(ee);
		lineMap.replace(index, relems.length, aelems);
	    }
	    if (Utilities.isComposedTextAttributeDefined(attr)) {
	        insertComposedTextUpdate(chng, attr);
	    }
	} catch (BadLocationException e) {
	    throw new Error("Internal error: " + e.toString());
	}
	super.insertUpdate(chng, attr);
    
protected voidremoveUpdate(DefaultDocumentEvent chng)
Updates any document structure as a result of text removal. This will happen within a write lock. Since the structure represents a line map, this just checks to see if the removal spans lines. If it does, the two lines outside of the removal area are joined together.

param
chng the change event describing the edit

	removed.removeAllElements();
	BranchElement map = (BranchElement) getDefaultRootElement();
	int offset = chng.getOffset();
	int length = chng.getLength();
	int line0 = map.getElementIndex(offset);
	int line1 = map.getElementIndex(offset + length);
	if (line0 != line1) {
	    // a line was removed
	    for (int i = line0; i <= line1; i++) {
		removed.addElement(map.getElement(i));
	    }
	    int p0 = map.getElement(line0).getStartOffset();
	    int p1 = map.getElement(line1).getEndOffset();
	    Element[] aelems = new Element[1];
	    aelems[0] = createLeafElement(map, null, p0, p1);
	    Element[] relems = new Element[removed.size()];
	    removed.copyInto(relems);
	    ElementEdit ee = new ElementEdit(map, line0, relems, aelems);
	    chng.addEdit(ee);
	    map.replace(line0, relems.length, aelems);
	} else {
	    //Check for the composed text element
	    Element line = map.getElement(line0);
	    if (!line.isLeaf()) {
	        Element leaf = line.getElement(line.getElementIndex(offset));
		if (Utilities.isComposedTextElement(leaf)) {
	            Element[] aelem = new Element[1];
	            aelem[0] = createLeafElement(map, null, 
		        line.getStartOffset(), line.getEndOffset());
	            Element[] relem = new Element[1];
	            relem[0] = line;
	            ElementEdit ee = new ElementEdit(map, line0, relem, aelem);
	            chng.addEdit(ee);
	            map.replace(line0, 1, aelem);
		}
	    }
	}
	super.removeUpdate(chng);