diff options
author | Tom Tromey <tromey@gcc.gnu.org> | 2005-09-23 21:31:04 +0000 |
---|---|---|
committer | Tom Tromey <tromey@gcc.gnu.org> | 2005-09-23 21:31:04 +0000 |
commit | 1ea63ef8be1cc54dd0de9d82c684713a1dcf1e06 (patch) | |
tree | 3ca4b2e68dc14c3128b9c781d23f1d0b1f2bee49 /libjava/classpath/javax/swing/text/AbstractDocument.java | |
parent | 9b044d19517541c95681d35a92dbc81e6e21d94f (diff) | |
download | gcc-1ea63ef8be1cc54dd0de9d82c684713a1dcf1e06.zip gcc-1ea63ef8be1cc54dd0de9d82c684713a1dcf1e06.tar.gz gcc-1ea63ef8be1cc54dd0de9d82c684713a1dcf1e06.tar.bz2 |
Imported Classpath 0.18.
* sources.am, Makefile.in: Updated.
* Makefile.am (nat_source_files): Removed natProxy.cc.
* java/lang/reflect/natProxy.cc: Removed.
* gnu/classpath/jdwp/VMFrame.java,
gnu/classpath/jdwp/VMIdManager.java,
gnu/classpath/jdwp/VMVirtualMachine.java,
java/lang/reflect/VMProxy.java: New files.
2005-09-23 Thomas Fitzsimmons <fitzsim@redhat.com>
* scripts/makemake.tcl (verbose): Add gnu/java/awt/peer/qt to BC
list.
2005-09-23 Thomas Fitzsimmons <fitzsim@redhat.com>
* gnu/java/net/DefaultContentHandlerFactory.java (getContent):
Remove ClasspathToolkit references.
2005-09-23 Thomas Fitzsimmons <fitzsim@redhat.com>
* gnu/awt/xlib/XCanvasPeer.java: Add new peer methods.
* gnu/awt/xlib/XFramePeer.java: Likewise.
* gnu/awt/xlib/XGraphicsConfiguration.java: Likewise.
2005-09-23 Thomas Fitzsimmons <fitzsim@redhat.com>
* Makefile.am (libgcjawt_la_SOURCES): Remove jawt.c. Add
classpath/native/jawt/jawt.c.
* Makefile.in: Regenerate.
* jawt.c: Remove file.
* include/Makefile.am (tool_include__HEADERS): Remove jawt.h and
jawt_md.h. Add ../classpath/include/jawt.h and
../classpath/include/jawt_md.h.
* include/Makefile.in: Regenerate.
* include/jawt.h: Regenerate.
* include/jawt_md.h: Regenerate.
From-SVN: r104586
Diffstat (limited to 'libjava/classpath/javax/swing/text/AbstractDocument.java')
-rw-r--r-- | libjava/classpath/javax/swing/text/AbstractDocument.java | 1236 |
1 files changed, 1167 insertions, 69 deletions
diff --git a/libjava/classpath/javax/swing/text/AbstractDocument.java b/libjava/classpath/javax/swing/text/AbstractDocument.java index c3a3d70..3c9a4d4 100644 --- a/libjava/classpath/javax/swing/text/AbstractDocument.java +++ b/libjava/classpath/javax/swing/text/AbstractDocument.java @@ -56,70 +56,193 @@ import javax.swing.undo.AbstractUndoableEdit; import javax.swing.undo.CompoundEdit; import javax.swing.undo.UndoableEdit; +/** + * An abstract base implementation for the {@link Document} interface. + * This class provides some common functionality for all <code>Element</code>s, + * most notably it implements a locking mechanism to make document modification + * thread-safe. + * + * @author original author unknown + * @author Roman Kennke (roman@kennke.org) + */ public abstract class AbstractDocument implements Document, Serializable { + /** The serial version UID for this class as of JDK1.4. */ private static final long serialVersionUID = -116069779446114664L; - + + /** + * Standard error message to indicate a bad location. + */ protected static final String BAD_LOCATION = "document location failure"; - + + /** + * Standard name for unidirectional <code>Element</code>s. + */ public static final String BidiElementName = "bidi level"; + + /** + * Standard name for content <code>Element</code>s. These are usually + * {@link LeafElement}s. + */ public static final String ContentElementName = "content"; + + /** + * Standard name for paragraph <code>Element</code>s. These are usually + * {@link BranchElement}s. + */ public static final String ParagraphElementName = "paragraph"; + + /** + * Standard name for section <code>Element</code>s. These are usually + * {@link DefaultStyledDocument.SectionElement}s. + */ public static final String SectionElementName = "section"; + + /** + * Attribute key for storing the element name. + */ public static final String ElementNameAttribute = "$ename"; + /** + * The actual content model of this <code>Document</code>. + */ Content content; + + /** + * The AttributeContext for this <code>Document</code>. + */ AttributeContext context; + + /** + * The currently installed <code>DocumentFilter</code>. + */ DocumentFilter documentFilter; - /** The documents properties. */ + /** + * The documents properties. + */ Dictionary properties; + /** + * Manages event listeners for this <code>Document</code>. + */ protected EventListenerList listenerList = new EventListenerList(); + /** + * Creates a new <code>AbstractDocument</code> with the specified + * {@link Content} model. + * + * @param doc the <code>Content</code> model to be used in this + * <code>Document<code> + * + * @see GapContent + * @see StringContent + */ protected AbstractDocument(Content doc) { this(doc, StyleContext.getDefaultStyleContext()); } + /** + * Creates a new <code>AbstractDocument</code> with the specified + * {@link Content} model and {@link AttributeContext}. + * + * @param doc the <code>Content</code> model to be used in this + * <code>Document<code> + * @param ctx the <code>AttributeContext</code> to use + * + * @see GapContent + * @see StringContent + */ protected AbstractDocument(Content doc, AttributeContext ctx) { content = doc; context = ctx; } - // These still need to be implemented by a derived class: + /** + * Returns the paragraph {@link Element} that holds the specified position. + * + * @param pos the position for which to get the paragraph element + * + * @return the paragraph {@link Element} that holds the specified position + */ public abstract Element getParagraphElement(int pos); + /** + * Returns the default root {@link Element} of this <code>Document</code>. + * Usual <code>Document</code>s only have one root element and return this. + * However, there may be <code>Document</code> implementations that + * support multiple root elements, they have to return a default root element + * here. + * + * @return the default root {@link Element} of this <code>Document</code> + */ public abstract Element getDefaultRootElement(); + /** + * Creates and returns a branch element with the specified + * <code>parent</code> and <code>attributes</code>. Note that the new + * <code>Element</code> is linked to the parent <code>Element</code> + * through {@link Element#getParentElement}, but it is not yet added + * to the parent <code>Element</code> as child. + * + * @param parent the parent <code>Element</code> for the new branch element + * @param attributes the text attributes to be installed in the new element + * + * @return the new branch <code>Element</code> + * + * @see BranchElement + */ protected Element createBranchElement(Element parent, AttributeSet attributes) { return new BranchElement(parent, attributes); } + /** + * Creates and returns a leaf element with the specified + * <code>parent</code> and <code>attributes</code>. Note that the new + * <code>Element</code> is linked to the parent <code>Element</code> + * through {@link Element#getParentElement}, but it is not yet added + * to the parent <code>Element</code> as child. + * + * @param parent the parent <code>Element</code> for the new branch element + * @param attributes the text attributes to be installed in the new element + * + * @return the new branch <code>Element</code> + * + * @see LeafElement + */ protected Element createLeafElement(Element parent, AttributeSet attributes, int start, int end) { return new LeafElement(parent, attributes, start, end); } + /** + * Creates a {@link Position} that keeps track of the location at the + * specified <code>offset</code>. + * + * @param offset the location in the document to keep track by the new + * <code>Position</code> + * + * @return the newly created <code>Position</code> + * + * @throws BadLocationException if <code>offset</code> is not a valid + * location in the documents content model + */ public Position createPosition(final int offset) throws BadLocationException { - if (offset < 0 || offset > getLength()) - throw new BadLocationException(getText(0, getLength()), offset); - - return new Position() - { - public int getOffset() - { - return offset; - } - }; + return content.createPosition(offset); } + /** + * Notifies all registered listeners when the document model changes. + * + * @param event the <code>DocumentEvent</code> to be fired + */ protected void fireChangedUpdate(DocumentEvent event) { DocumentListener[] listeners = getDocumentListeners(); @@ -128,6 +251,12 @@ public abstract class AbstractDocument listeners[index].changedUpdate(event); } + /** + * Notifies all registered listeners when content is inserted in the document + * model. + * + * @param event the <code>DocumentEvent</code> to be fired + */ protected void fireInsertUpdate(DocumentEvent event) { DocumentListener[] listeners = getDocumentListeners(); @@ -136,6 +265,12 @@ public abstract class AbstractDocument listeners[index].insertUpdate(event); } + /** + * Notifies all registered listeners when content is removed from the + * document model. + * + * @param event the <code>DocumentEvent</code> to be fired + */ protected void fireRemoveUpdate(DocumentEvent event) { DocumentListener[] listeners = getDocumentListeners(); @@ -144,6 +279,12 @@ public abstract class AbstractDocument listeners[index].removeUpdate(event); } + /** + * Notifies all registered listeners when an <code>UndoableEdit</code> has + * been performed on this <code>Document</code>. + * + * @param event the <code>UndoableEditEvent</code> to be fired + */ protected void fireUndoableEditUpdate(UndoableEditEvent event) { UndoableEditListener[] listeners = getUndoableEditListeners(); @@ -152,31 +293,70 @@ public abstract class AbstractDocument listeners[index].undoableEditHappened(event); } + /** + * Returns the asynchronous loading priority. Returns <code>-1</code> if this + * document should not be loaded asynchronously. + * + * @return the asynchronous loading priority + */ public int getAsynchronousLoadPriority() { return 0; } + /** + * Returns the {@link AttributeContext} used in this <code>Document</code>. + * + * @return the {@link AttributeContext} used in this <code>Document</code> + */ protected AttributeContext getAttributeContext() { return context; } + /** + * Returns the root element for bidirectional content. + * + * @return the root element for bidirectional content + */ public Element getBidiRootElement() { return null; } + /** + * Returns the {@link Content} model for this <code>Document</code> + * + * @return the {@link Content} model for this <code>Document</code> + * + * @see GapContent + * @see StringContent + */ protected Content getContent() { return content; } + /** + * Returns the thread that currently modifies this <code>Document</code> + * if there is one, otherwise <code>null</code>. This can be used to + * distinguish between a method call that is part of an ongoing modification + * or if it is a separate modification for which a new lock must be aquired. + * + * @return the thread that currently modifies this <code>Document</code> + * if there is one, otherwise <code>null</code> + */ protected Thread getCurrentWriter() { + // FIXME: Implement locking! return null; } + /** + * Returns the properties of this <code>Document</code>. + * + * @return the properties of this <code>Document</code> + */ public Dictionary getDocumentProperties() { // FIXME: make me thread-safe @@ -186,8 +366,16 @@ public abstract class AbstractDocument return properties; } + /** + * Returns a {@link Position} which will always mark the end of the + * <code>Document</code>. + * + * @return a {@link Position} which will always mark the end of the + * <code>Document</code> + */ public Position getEndPosition() { + // FIXME: Properly implement this by calling Content.createPosition(). return new Position() { public int getOffset() @@ -197,16 +385,39 @@ public abstract class AbstractDocument }; } + /** + * Returns the length of this <code>Document</code>'s content. + * + * @return the length of this <code>Document</code>'s content + */ public int getLength() { + // We return Content.getLength() -1 here because there is always an + // implicit \n at the end of the Content which does count in Content + // but not in Document. return content.length() - 1; } + /** + * Returns all registered listeners of a given listener type. + * + * @param listenerType the type of the listeners to be queried + * + * @return all registered listeners of the specified type + */ public EventListener[] getListeners(Class listenerType) { return listenerList.getListeners(listenerType); } + /** + * Returns a property from this <code>Document</code>'s property list. + * + * @param key the key of the property to be fetched + * + * @return the property for <code>key</code> or <code>null</code> if there + * is no such property stored + */ public Object getProperty(Object key) { // FIXME: make me thread-safe @@ -217,6 +428,15 @@ public abstract class AbstractDocument return value; } + /** + * Returns all root elements of this <code>Document</code>. By default + * this just returns the single root element returned by + * {@link #getDefaultRootElement()}. <code>Document</code> implementations + * that support multiple roots must override this method and return all roots + * here. + * + * @return all root elements of this <code>Document</code> + */ public Element[] getRootElements() { Element[] elements = new Element[1]; @@ -224,8 +444,16 @@ public abstract class AbstractDocument return elements; } + /** + * Returns a {@link Position} which will always mark the beginning of the + * <code>Document</code>. + * + * @return a {@link Position} which will always mark the beginning of the + * <code>Document</code> + */ public Position getStartPosition() { + // FIXME: Properly implement this using Content.createPosition(). return new Position() { public int getOffset() @@ -235,17 +463,53 @@ public abstract class AbstractDocument }; } + /** + * Returns a piece of this <code>Document</code>'s content. + * + * @param offset the start offset of the content + * @param length the length of the content + * + * @return the piece of content specified by <code>offset</code> and + * <code>length</code> + * + * @throws BadLocationException if <code>offset</code> or <code>offset + + * length</code> are invalid locations with this + * <code>Document</code> + */ public String getText(int offset, int length) throws BadLocationException { return content.getString(offset, length); } + /** + * Fetches a piece of this <code>Document</code>'s content and stores + * it in the given {@link Segment}. + * + * @param offset the start offset of the content + * @param length the length of the content + * @param segment the <code>Segment</code> to store the content in + * + * @throws BadLocationException if <code>offset</code> or <code>offset + + * length</code> are invalid locations with this + * <code>Document</code> + */ public void getText(int offset, int length, Segment segment) throws BadLocationException { content.getChars(offset, length, segment); } + /** + * Inserts a String into this <code>Document</code> at the specified + * position and assigning the specified attributes to it. + * + * @param offset the location at which the string should be inserted + * @param text the content to be inserted + * @param attributes the text attributes to be assigned to that string + * + * @throws BadLocationException if <code>offset</code> is not a valid + * location in this <code>Document</code> + */ public void insertString(int offset, String text, AttributeSet attributes) throws BadLocationException { @@ -261,14 +525,37 @@ public abstract class AbstractDocument fireInsertUpdate(event); } + /** + * Called to indicate that text has been inserted into this + * <code>Document</code>. The default implementation does nothing. + * This method is executed within a write lock. + * + * @param chng the <code>DefaultDocumentEvent</code> describing the change + * @param attr the attributes of the changed content + */ protected void insertUpdate(DefaultDocumentEvent chng, AttributeSet attr) { + // Do nothing here. Subclasses may want to override this. } + /** + * Called after some content has been removed from this + * <code>Document</code>. The default implementation does nothing. + * This method is executed within a write lock. + * + * @param chng the <code>DefaultDocumentEvent</code> describing the change + */ protected void postRemoveUpdate(DefaultDocumentEvent chng) { + // Do nothing here. Subclasses may want to override this. } + /** + * Stores a property in this <code>Document</code>'s property list. + * + * @param key the key of the property to be stored + * @param value the value of the property to be stored + */ public void putProperty(Object key, Object value) { // FIXME: make me thread-safe @@ -278,14 +565,31 @@ public abstract class AbstractDocument properties.put(key, value); } + /** + * Blocks until a read lock can be obtained. + */ public void readLock() { } + /** + * Releases the read lock. If this was the only reader on this + * <code>Document</code>, writing may begin now. + */ public void readUnlock() { } + /** + * Removes a piece of content from this <code>Document</code>. + * + * @param offset the start offset of the fragment to be removed + * @param length the length of the fragment to be removed + * + * @throws BadLocationException if <code>offset</code> or + * <code>offset + length</code> or invalid locations within this + * document + */ public void remove(int offset, int length) throws BadLocationException { DefaultDocumentEvent event = @@ -298,7 +602,17 @@ public abstract class AbstractDocument } /** - * Replaces some text in the document. + * Replaces a piece of content in this <code>Document</code> with + * another piece of content. + * + * @param offset the start offset of the fragment to be removed + * @param length the length of the fragment to be removed + * @param text the text to replace the content with + * @param attributes the text attributes to assign to the new content + * + * @throws BadLocationException if <code>offset</code> or + * <code>offset + length</code> or invalid locations within this + * document * * @since 1.4 */ @@ -331,9 +645,9 @@ public abstract class AbstractDocument } /** - * Returns add added <code>DocumentListener</code> objects. + * Returns all registered <code>DocumentListener</code>s. * - * @return an array of listeners + * @return all registered <code>DocumentListener</code>s */ public DocumentListener[] getDocumentListeners() { @@ -341,7 +655,7 @@ public abstract class AbstractDocument } /** - * Adds a <code>UndoableEditListener</code> object to this document. + * Adds an {@link UndoableEditListener} to this <code>Document</code>. * * @param listener the listener to add */ @@ -351,7 +665,7 @@ public abstract class AbstractDocument } /** - * Removes a <code>UndoableEditListener</code> object from this document. + * Removes an {@link UndoableEditListener} from this <code>Document</code>. * * @param listener the listener to remove */ @@ -361,42 +675,93 @@ public abstract class AbstractDocument } /** - * Returns add added <code>UndoableEditListener</code> objects. + * Returns all registered {@link UndoableEditListener}s. * - * @return an array of listeners + * @return all registered {@link UndoableEditListener}s */ public UndoableEditListener[] getUndoableEditListeners() { return (UndoableEditListener[]) getListeners(UndoableEditListener.class); } + /** + * Called before some content gets removed from this <code>Document</code>. + * The default implementation does nothing but may be overridden by + * subclasses to modify the <code>Document</code> structure in response + * to a remove request. The method is executed within a write lock. + * + * @param chng the <code>DefaultDocumentEvent</code> describing the change + */ protected void removeUpdate(DefaultDocumentEvent chng) { + // Do nothing here. Subclasses may wish to override this. } - public void render(Runnable r) + /** + * Called to render this <code>Document</code> visually. It obtains a read + * lock, ensuring that no changes will be made to the <code>document</code> + * during the rendering process. It then calls the {@link Runnable#run()} + * method on <code>runnable</code>. This method <em>must not</em> attempt + * to modifiy the <code>Document</code>, since a deadlock will occur if it + * tries to obtain a write lock. When the {@link Runnable#run()} method + * completes (either naturally or by throwing an exception), the read lock + * is released. Note that there is nothing in this method related to + * the actual rendering. It could be used to execute arbitrary code within + * a read lock. + * + * @param runnable the {@link Runnable} to execute + */ + public void render(Runnable runnable) { + // FIXME: Implement me! } + /** + * Sets the asynchronous loading priority for this <code>Document</code>. + * A value of <code>-1</code> indicates that this <code>Document</code> + * should be loaded synchronously. + * + * @param p the asynchronous loading priority to set + */ public void setAsynchronousLoadPriority(int p) { } - public void setDocumentProperties(Dictionary x) + /** + * Sets the properties of this <code>Document</code>. + * + * @param p the document properties to set + */ + public void setDocumentProperties(Dictionary p) { // FIXME: make me thread-safe - properties = x; + properties = p; } + /** + * Blocks until a write lock can be obtained. + */ protected void writeLock() { + // FIXME: Implement me. } + /** + * Releases the write lock. This allows waiting readers or writers to + * obtain the lock. + */ protected void writeUnlock() { + // FIXME: Implement me. } /** + * Returns the currently installed {@link DocumentFilter} for this + * <code>Document</code>. + * + * @return the currently installed {@link DocumentFilter} for this + * <code>Document</code> + * * @since 1.4 */ public DocumentFilter getDocumentFilter() @@ -405,6 +770,10 @@ public abstract class AbstractDocument } /** + * Sets the {@link DocumentFilter} for this <code>Document</code>. + * + * @param filter the <code>DocumentFilter</code> to set + * * @since 1.4 */ public void setDocumentFilter(DocumentFilter filter) @@ -412,209 +781,592 @@ public abstract class AbstractDocument this.documentFilter = filter; } + /** + * Dumps diagnostic information to the specified <code>PrintStream</code>. + * + * @param out the stream to write the diagnostic information to + */ public void dump(PrintStream out) { ((AbstractElement) getDefaultRootElement()).dump(out, 0); } + /** + * Defines a set of methods for managing text attributes for one or more + * <code>Document</code>s. + * + * Replicating {@link AttributeSet}s throughout a <code>Document</code> can + * be very expensive. Implementations of this interface are intended to + * provide intelligent management of <code>AttributeSet</code>s, eliminating + * costly duplication. + * + * @see StyleContext + */ public interface AttributeContext { + /** + * Returns an {@link AttributeSet} that contains the attributes + * of <code>old</code> plus the new attribute specified by + * <code>name</code> and <code>value</code>. + * + * @param old the attribute set to be merged with the new attribute + * @param name the name of the attribute to be added + * @param value the value of the attribute to be added + * + * @return the old attributes plus the new attribute + */ AttributeSet addAttribute(AttributeSet old, Object name, Object value); + /** + * Returns an {@link AttributeSet} that contains the attributes + * of <code>old</code> plus the new attributes in <code>attributes</code>. + * + * @param old the set of attributes where to add the new attributes + * @param attributes the attributes to be added + * + * @return an {@link AttributeSet} that contains the attributes + * of <code>old</code> plus the new attributes in + * <code>attributes</code> + */ AttributeSet addAttributes(AttributeSet old, AttributeSet attributes); + /** + * Returns an empty {@link AttributeSet}. + * + * @return an empty {@link AttributeSet} + */ AttributeSet getEmptySet(); + /** + * Called to indicate that the attributes in <code>attributes</code> are + * no longer used. + * + * @param attributes the attributes are no longer used + */ void reclaim(AttributeSet attributes); + /** + * Returns a {@link AttributeSet} that has the attribute with the specified + * <code>name</code> removed from <code>old</code>. + * + * @param old the attribute set from which an attribute is removed + * @param name the name of the attribute to be removed + * + * @return the attributes of <code>old</code> minus the attribute + * specified by <code>name</code> + */ AttributeSet removeAttribute(AttributeSet old, Object name); + /** + * Removes all attributes in <code>attributes</code> from <code>old</code> + * and returns the resulting <code>AttributeSet</code>. + * + * @param old the set of attributes from which to remove attributes + * @param attributes the attributes to be removed from <code>old</code> + * + * @return the attributes of <code>old</code> minus the attributes in + * <code>attributes</code> + */ AttributeSet removeAttributes(AttributeSet old, AttributeSet attributes); + /** + * Removes all attributes specified by <code>names</code> from + * <code>old</code> and returns the resulting <code>AttributeSet</code>. + * + * @param old the set of attributes from which to remove attributes + * @param names the names of the attributes to be removed from + * <code>old</code> + * + * @return the attributes of <code>old</code> minus the attributes in + * <code>attributes</code> + */ AttributeSet removeAttributes(AttributeSet old, Enumeration names); } + /** + * A sequence of data that can be edited. This is were the actual content + * in <code>AbstractDocument</code>'s is stored. + */ public interface Content { + /** + * Creates a {@link Position} that keeps track of the location at + * <code>offset</code>. + * + * @return a {@link Position} that keeps track of the location at + * <code>offset</code>. + * + * @throw BadLocationException if <code>offset</code> is not a valid + * location in this <code>Content</code> model + */ Position createPosition(int offset) throws BadLocationException; + /** + * Returns the length of the content. + * + * @return the length of the content + */ int length(); + /** + * Inserts a string into the content model. + * + * @param where the offset at which to insert the string + * @param str the string to be inserted + * + * @return an <code>UndoableEdit</code> or <code>null</code> if undo is + * not supported by this <code>Content</code> model + * + * @throws BadLocationException if <code>where</code> is not a valid + * location in this <code>Content</code> model + */ UndoableEdit insertString(int where, String str) throws BadLocationException; + /** + * Removes a piece of content from the content model. + * + * @param where the offset at which to remove content + * @param nitems the number of characters to be removed + * + * @return an <code>UndoableEdit</code> or <code>null</code> if undo is + * not supported by this <code>Content</code> model + * + * @throws BadLocationException if <code>where</code> is not a valid + * location in this <code>Content</code> model + */ UndoableEdit remove(int where, int nitems) throws BadLocationException; + /** + * Returns a piece of content. + * + * @param where the start offset of the requested fragment + * @param len the length of the requested fragment + * + * @return the requested fragment + * @throws BadLocationException if <code>offset</code> or + * <code>offset + len</code>is not a valid + * location in this <code>Content</code> model + */ String getString(int where, int len) throws BadLocationException; + /** + * Fetches a piece of content and stores it in <code>txt</code>. + * + * @param where the start offset of the requested fragment + * @param len the length of the requested fragment + * @param txt the <code>Segment</code> where to fragment is stored into + * + * @throws BadLocationException if <code>offset</code> or + * <code>offset + len</code>is not a valid + * location in this <code>Content</code> model + */ void getChars(int where, int len, Segment txt) throws BadLocationException; } + /** + * An abstract base implementation of the {@link Element} interface. + */ public abstract class AbstractElement implements Element, MutableAttributeSet, TreeNode, Serializable { + /** The serial version UID for AbstractElement. */ private static final long serialVersionUID = 1265312733007397733L; + + /** The number of characters that this Element spans. */ int count; + + /** The starting offset of this Element. */ int offset; + /** The attributes of this Element. */ AttributeSet attributes; + /** The parent element. */ Element element_parent; + /** The parent in the TreeNode interface. */ TreeNode tree_parent; + + /** The children of this element. */ Vector tree_children; + /** + * Creates a new instance of <code>AbstractElement</code> with a + * specified parent <code>Element</code> and <code>AttributeSet</code>. + * + * @param p the parent of this <code>AbstractElement</code> + * @param s the attributes to be assigned to this + * <code>AbstractElement</code> + */ public AbstractElement(Element p, AttributeSet s) { element_parent = p; - attributes = s; + AttributeContext ctx = getAttributeContext(); + attributes = ctx.getEmptySet(); + if (s != null) + attributes = ctx.addAttributes(attributes, s); } - // TreeNode implementation - + /** + * Returns the child nodes of this <code>Element</code> as an + * <code>Enumeration</code> of {@link TreeNode}s. + * + * @return the child nodes of this <code>Element</code> as an + * <code>Enumeration</code> of {@link TreeNode}s + */ public abstract Enumeration children(); - + + /** + * Returns <code>true</code> if this <code>AbstractElement</code> + * allows children. + * + * @return <code>true</code> if this <code>AbstractElement</code> + * allows children + */ public abstract boolean getAllowsChildren(); - + + /** + * Returns the child of this <code>AbstractElement</code> at + * <code>index</code>. + * + * @param index the position in the child list of the child element to + * be returned + * + * @return the child of this <code>AbstractElement</code> at + * <code>index</code> + */ public TreeNode getChildAt(int index) { return (TreeNode) tree_children.get(index); } - + + /** + * Returns the number of children of this <code>AbstractElement</code>. + * + * @return the number of children of this <code>AbstractElement</code> + */ public int getChildCount() { return tree_children.size(); } - + + /** + * Returns the index of a given child <code>TreeNode</code> or + * <code>-1</code> if <code>node</code> is not a child of this + * <code>AbstractElement</code>. + * + * @param node the node for which the index is requested + * + * @return the index of a given child <code>TreeNode</code> or + * <code>-1</code> if <code>node</code> is not a child of this + * <code>AbstractElement</code> + */ public int getIndex(TreeNode node) { return tree_children.indexOf(node); } + /** + * Returns the parent <code>TreeNode</code> of this + * <code>AbstractElement</code> or <code>null</code> if this element + * has no parent. + * + * @return the parent <code>TreeNode</code> of this + * <code>AbstractElement</code> or <code>null</code> if this + * element has no parent + */ public TreeNode getParent() { return tree_parent; } + /** + * Returns <code>true</code> if this <code>AbstractElement</code> is a + * leaf element, <code>false</code> otherwise. + * + * @return <code>true</code> if this <code>AbstractElement</code> is a + * leaf element, <code>false</code> otherwise + */ public abstract boolean isLeaf(); - - // MutableAttributeSet support - + /** + * Adds an attribute to this element. + * + * @param name the name of the attribute to be added + * @param value the value of the attribute to be added + */ public void addAttribute(Object name, Object value) { attributes = getAttributeContext().addAttribute(attributes, name, value); } + /** + * Adds a set of attributes to this element. + * + * @param attrs the attributes to be added to this element + */ public void addAttributes(AttributeSet attrs) { attributes = getAttributeContext().addAttributes(attributes, attrs); } + /** + * Removes an attribute from this element. + * + * @param name the name of the attribute to be removed + */ public void removeAttribute(Object name) { attributes = getAttributeContext().removeAttribute(attributes, name); } + /** + * Removes a set of attributes from this element. + * + * @param attrs the attributes to be removed + */ public void removeAttributes(AttributeSet attrs) { attributes = getAttributeContext().removeAttributes(attributes, attrs); } + /** + * Removes a set of attribute from this element. + * + * @param names the names of the attributes to be removed + */ public void removeAttributes(Enumeration names) { attributes = getAttributeContext().removeAttributes(attributes, names); } + /** + * Sets the parent attribute set against which the element can resolve + * attributes that are not defined in itself. + * + * @param parent the resolve parent to set + */ public void setResolveParent(AttributeSet parent) { - attributes = getAttributeContext().addAttribute(attributes, ResolveAttribute, parent); + attributes = getAttributeContext().addAttribute(attributes, + ResolveAttribute, + parent); } - - // AttributeSet interface support - + /** + * Returns <code>true</code> if this element contains the specified + * attribute. + * + * @param name the name of the attribute to check + * @param value the value of the attribute to check + * + * @return <code>true</code> if this element contains the specified + * attribute + */ public boolean containsAttribute(Object name, Object value) { return attributes.containsAttribute(name, value); } + /** + * Returns <code>true</code> if this element contains all of the + * specified attributes. + * + * @param attrs the attributes to check + * + * @return <code>true</code> if this element contains all of the + * specified attributes + */ public boolean containsAttributes(AttributeSet attrs) { return attributes.containsAttributes(attrs); } + /** + * Returns a copy of the attributes of this element. + * + * @return a copy of the attributes of this element + */ public AttributeSet copyAttributes() { return attributes.copyAttributes(); } + /** + * Returns the attribute value with the specified key. If this attribute + * is not defined in this element and this element has a resolving + * parent, the search goes upward to the resolve parent chain. + * + * @param key the key of the requested attribute + * + * @return the attribute value for <code>key</code> of <code>null</code> + * if <code>key</code> is not found locally and cannot be resolved + * in this element's resolve parents + */ public Object getAttribute(Object key) { return attributes.getAttribute(key); } + /** + * Returns the number of defined attributes in this element. + * + * @return the number of defined attributes in this element + */ public int getAttributeCount() { return attributes.getAttributeCount(); } - + + /** + * Returns the names of the attributes of this element. + * + * @return the names of the attributes of this element + */ public Enumeration getAttributeNames() { return attributes.getAttributeNames(); } - + + /** + * Returns the resolve parent of this element. + * + * @return the resolve parent of this element + * + * @see #setResolveParent(AttributeSet) + */ public AttributeSet getResolveParent() { return attributes.getResolveParent(); } + /** + * Returns <code>true</code> if an attribute with the specified name + * is defined in this element, <code>false</code> otherwise. + * + * @param attrName the name of the requested attributes + * + * @return <code>true</code> if an attribute with the specified name + * is defined in this element, <code>false</code> otherwise + */ public boolean isDefined(Object attrName) { return attributes.isDefined(attrName); } - + + /** + * Returns <code>true</code> if the specified <code>AttributeSet</code> + * is equal to this element's <code>AttributeSet</code>, <code>false</code> + * otherwise. + * + * @param attrs the attributes to compare this element to + * + * @return <code>true</code> if the specified <code>AttributeSet</code> + * is equal to this element's <code>AttributeSet</code>, + * <code>false</code> otherwise + */ public boolean isEqual(AttributeSet attrs) { return attributes.isEqual(attrs); } - // Element interface support - + /** + * Returns the attributes of this element. + * + * @return the attributes of this element + */ public AttributeSet getAttributes() { - return attributes; + return this; } + /** + * Returns the {@link Document} to which this element belongs. + * + * @return the {@link Document} to which this element belongs + */ public Document getDocument() { return AbstractDocument.this; } - + + /** + * Returns the child element at the specified <code>index</code>. + * + * @param index the index of the requested child element + * + * @return the requested element + */ public abstract Element getElement(int index); - + + /** + * Returns the name of this element. + * + * @return the name of this element + */ public String getName() { return (String) getAttribute(NameAttribute); } - + + /** + * Returns the parent element of this element. + * + * @return the parent element of this element + */ public Element getParentElement() { return element_parent; } - + + /** + * Returns the offset inside the document model that is after the last + * character of this element. + * + * @return the offset inside the document model that is after the last + * character of this element + */ public abstract int getEndOffset(); - + + /** + * Returns the number of child elements of this element. + * + * @return the number of child elements of this element + */ public abstract int getElementCount(); - + + /** + * Returns the index of the child element that spans the specified + * offset in the document model. + * + * @param offset the offset for which the responsible element is searched + * + * @return the index of the child element that spans the specified + * offset in the document model + */ public abstract int getElementIndex(int offset); - + + /** + * Returns the start offset if this element inside the document model. + * + * @return the start offset if this element inside the document model + */ public abstract int getStartOffset(); - private void dumpElement(PrintStream stream, String indent, Element element) + /** + * Prints diagnostic information to the specified stream. + * + * @param stream the stream to dump to + * @param indent the indentation level + * @param element the element to be dumped + */ + private void dumpElement(PrintStream stream, String indent, + Element element) { + // FIXME: Should the method be removed? System.out.println(indent + "<" + element.getName() +">"); - + if (element.isLeaf()) { int start = element.getStartOffset(); @@ -626,6 +1378,12 @@ public abstract class AbstractDocument } catch (BadLocationException e) { + AssertionError error = + new AssertionError("BadLocationException should not be " + + "thrown here. start = " + start + + ", end = " + end); + error.initCause(e); + throw error; } System.out.println(indent + " [" + start + "," @@ -638,7 +1396,13 @@ public abstract class AbstractDocument dumpElement(stream, indent + " ", element.getElement(i)); } } - + + /** + * Prints diagnostic output to the specified stream. + * + * @param stream the stream to write to + * @param indent the indentation level + */ public void dump(PrintStream stream, int indent) { String indentStr = ""; @@ -648,17 +1412,36 @@ public abstract class AbstractDocument } } + /** + * An implementation of {@link Element} to represent composite + * <code>Element</code>s that contain other <code>Element</code>s. + */ public class BranchElement extends AbstractElement { + /** The serial version UID for BranchElement. */ private static final long serialVersionUID = -8595176318868717313L; - + + /** The child elements of this BranchElement. */ private Element[] children = new Element[0]; + /** + * Creates a new <code>BranchElement</code> with the specified + * parent and attributes. + * + * @param parent the parent element of this <code>BranchElement</code> + * @param attributes the attributes to set on this + * <code>BranchElement</code> + */ public BranchElement(Element parent, AttributeSet attributes) { super(parent, attributes); } + /** + * Returns the children of this <code>BranchElement</code>. + * + * @return the children of this <code>BranchElement</code> + */ public Enumeration children() { if (children.length == 0) @@ -672,11 +1455,25 @@ public abstract class AbstractDocument return tmp.elements(); } + /** + * Returns <code>true</code> since <code>BranchElements</code> allow + * child elements. + * + * @return <code>true</code> since <code>BranchElements</code> allow + * child elements + */ public boolean getAllowsChildren() { return true; } + /** + * Returns the child element at the specified <code>index</code>. + * + * @param index the index of the requested child element + * + * @return the requested element + */ public Element getElement(int index) { if (index < 0 || index >= children.length) @@ -685,47 +1482,113 @@ public abstract class AbstractDocument return children[index]; } + /** + * Returns the number of child elements of this element. + * + * @return the number of child elements of this element + */ public int getElementCount() { return children.length; } + /** + * Returns the index of the child element that spans the specified + * offset in the document model. + * + * @param offset the offset for which the responsible element is searched + * + * @return the index of the child element that spans the specified + * offset in the document model + */ public int getElementIndex(int offset) { + // If we have no children, return -1. + if (getElementCount() == 0) + return - 1; + // XXX: There is surely a better algorithm // as beginning from first element each time. for (int index = 0; index < children.length; ++index) { - Element elem = children[index]; + Element elem = children[index]; - if ((elem.getStartOffset() <= offset) - && (offset < elem.getEndOffset())) - return index; + if ((elem.getStartOffset() <= offset) + && (offset < elem.getEndOffset())) + return index; } - return 0; + // If offset is greater than the index of the last element, return + // the index of the last element. + return getElementCount() - 1; } + /** + * Returns the offset inside the document model that is after the last + * character of this element. + * This is the end offset of the last child element. If this element + * has no children, this method throws a <code>NullPointerException</code>. + * + * @return the offset inside the document model that is after the last + * character of this element + * + * @throws NullPointerException if this branch element has no children + */ public int getEndOffset() { + if (getElementCount() == 0) + throw new NullPointerException("This BranchElement has no children."); return children[children.length - 1].getEndOffset(); } + /** + * Returns the name of this element. This is {@link #ParagraphElementName} + * in this case. + * + * @return the name of this element + */ public String getName() { return ParagraphElementName; } + /** + * Returns the start offset of this element inside the document model. + * This is the start offset of the first child element. If this element + * has no children, this method throws a <code>NullPointerException</code>. + * + * @return the start offset of this element inside the document model + * + * @throws NullPointerException if this branch element has no children + */ public int getStartOffset() { + if (getElementCount() == 0) + throw new NullPointerException("This BranchElement has no children."); return children[0].getStartOffset(); } + /** + * Returns <code>false</code> since <code>BranchElement</code> are no + * leafes. + * + * @return <code>false</code> since <code>BranchElement</code> are no + * leafes + */ public boolean isLeaf() { return false; } + /** + * Returns the <code>Element</code> at the specified <code>Document</code> + * offset. + * + * @return the <code>Element</code> at the specified <code>Document</code> + * offset + * + * @see #getElementIndex(int) + */ public Element positionToElement(int position) { // XXX: There is surely a better algorithm @@ -742,6 +1605,13 @@ public abstract class AbstractDocument return null; } + /** + * Replaces a set of child elements with a new set of child elemens. + * + * @param offset the start index of the elements to be removed + * @param length the number of elements to be removed + * @param elements the new elements to be inserted + */ public void replace(int offset, int length, Element[] elements) { Element[] target = new Element[children.length - length @@ -754,6 +1624,11 @@ public abstract class AbstractDocument children = target; } + /** + * Returns a string representation of this element. + * + * @return a string representation of this element + */ public String toString() { return ("BranchElement(" + getName() + ") " @@ -761,59 +1636,157 @@ public abstract class AbstractDocument } } + /** + * Stores the changes when a <code>Document</code> is beeing modified. + */ public class DefaultDocumentEvent extends CompoundEdit implements DocumentEvent { + /** The serial version UID of DefaultDocumentEvent. */ private static final long serialVersionUID = -7406103236022413522L; - + + /** The starting offset of the change. */ private int offset; + + /** The length of the change. */ private int length; + + /** The type of change. */ private DocumentEvent.EventType type; + /** + * Maps <code>Element</code> to their change records. + */ + Hashtable changes; + + /** + * Creates a new <code>DefaultDocumentEvent</code>. + * + * @param offset the starting offset of the change + * @param length the length of the change + * @param type the type of change + */ public DefaultDocumentEvent(int offset, int length, DocumentEvent.EventType type) { this.offset = offset; this.length = length; this.type = type; + changes = new Hashtable(); } + /** + * Adds an UndoableEdit to this <code>DocumentEvent</code>. If this + * edit is an instance of {@link ElementEdit}, then this record can + * later be fetched by calling {@link #getChange}. + * + * @param edit the undoable edit to add + */ + public boolean addEdit(UndoableEdit edit) + { + // XXX - Fully qualify ElementChange to work around gcj bug #2499. + if (edit instanceof DocumentEvent.ElementChange) + { + DocumentEvent.ElementChange elEdit = + (DocumentEvent.ElementChange) edit; + changes.put(elEdit.getElement(), elEdit); + } + return super.addEdit(edit); + } + + /** + * Returns the document that has been modified. + * + * @return the document that has been modified + */ public Document getDocument() { return AbstractDocument.this; } + /** + * Returns the length of the modification. + * + * @return the length of the modification + */ public int getLength() { return length; } + /** + * Returns the start offset of the modification. + * + * @return the start offset of the modification + */ public int getOffset() { return offset; } + /** + * Returns the type of the modification. + * + * @return the type of the modification + */ public DocumentEvent.EventType getType() { return type; } + /** + * Returns the changes for an element. + * + * @param elem the element for which the changes are requested + * + * @return the changes for <code>elem</code> or <code>null</code> if + * <code>elem</code> has not been changed + */ public DocumentEvent.ElementChange getChange(Element elem) { - return null; + // XXX - Fully qualify ElementChange to work around gcj bug #2499. + return (DocumentEvent.ElementChange) changes.get(elem); } } + /** + * An implementation of {@link DocumentEvent.ElementChange} to be added + * to {@link DefaultDocumentEvent}s. + */ public static class ElementEdit extends AbstractUndoableEdit implements DocumentEvent.ElementChange { + /** The serial version UID of ElementEdit. */ private static final long serialVersionUID = -1216620962142928304L; + /** + * The changed element. + */ private Element elem; + + /** + * The index of the change. + */ private int index; + + /** + * The removed elements. + */ private Element[] removed; + + /** + * The added elements. + */ private Element[] added; + /** + * Creates a new <code>ElementEdit</code>. + * + * @param elem the changed element + * @param index the index of the change + * @param removed the removed elements + * @param added the added elements + */ public ElementEdit(Element elem, int index, Element[] removed, Element[] added) { @@ -823,86 +1796,211 @@ public abstract class AbstractDocument this.added = added; } + /** + * Returns the added elements. + * + * @return the added elements + */ public Element[] getChildrenAdded() { return added; } - + + /** + * Returns the removed elements. + * + * @return the removed elements + */ public Element[] getChildrenRemoved() { return removed; } + /** + * Returns the changed element. + * + * @return the changed element + */ public Element getElement() { return elem; } + /** + * Returns the index of the change. + * + * @return the index of the change + */ public int getIndex() { return index; } } + /** + * An implementation of {@link Element} that represents a leaf in the + * document structure. This is used to actually store content. + */ public class LeafElement extends AbstractElement { + /** The serial version UID of LeafElement. */ private static final long serialVersionUID = 5115368706941283802L; - int start; - int end; + /** Manages the start offset of this element. */ + Position startPos; + + /** Manages the end offset of this element. */ + Position endPos; + + /** + * Creates a new <code>LeafElement</code>. + * + * @param parent the parent of this <code>LeafElement</code> + * @param attributes the attributes to be set + * @param start the start index of this element inside the document model + * @param end the end index of this element inside the document model + */ public LeafElement(Element parent, AttributeSet attributes, int start, int end) { super(parent, attributes); - this.start = start; - this.end = end; + { + try + { + if (parent != null) + { + startPos = parent.getDocument().createPosition(start); + endPos = parent.getDocument().createPosition(end); + } + else + { + startPos = createPosition(start); + endPos = createPosition(end); + } + } + catch (BadLocationException ex) + { + AssertionError as; + as = new AssertionError("BadLocationException thrown " + + "here. start=" + start + + ", end=" + end + + ", length=" + getLength()); + as.initCause(ex); + throw as; + } + } } + /** + * Returns <code>null</code> since <code>LeafElement</code>s cannot have + * children. + * + * @return <code>null</code> since <code>LeafElement</code>s cannot have + * children + */ public Enumeration children() { return null; } + /** + * Returns <code>false</code> since <code>LeafElement</code>s cannot have + * children. + * + * @return <code>false</code> since <code>LeafElement</code>s cannot have + * children + */ public boolean getAllowsChildren() { return false; } + /** + * Returns <code>null</code> since <code>LeafElement</code>s cannot have + * children. + * + * @return <code>null</code> since <code>LeafElement</code>s cannot have + * children + */ public Element getElement(int index) { return null; } + /** + * Returns <code>0</code> since <code>LeafElement</code>s cannot have + * children. + * + * @return <code>0</code> since <code>LeafElement</code>s cannot have + * children + */ public int getElementCount() { return 0; } + /** + * Returns <code>-1</code> since <code>LeafElement</code>s cannot have + * children. + * + * @return <code>-1</code> since <code>LeafElement</code>s cannot have + * children + */ public int getElementIndex(int offset) { return -1; } + /** + * Returns the end offset of this <code>Element</code> inside the + * document. + * + * @return the end offset of this <code>Element</code> inside the + * document + */ public int getEndOffset() { - return end; + return endPos.getOffset(); } + /** + * Returns the name of this <code>Element</code>. This is + * {@link #ContentElementName} in this case. + * + * @return the name of this <code>Element</code> + */ public String getName() { return ContentElementName; } + /** + * Returns the start offset of this <code>Element</code> inside the + * document. + * + * @return the start offset of this <code>Element</code> inside the + * document + */ public int getStartOffset() { - return start; + return startPos.getOffset(); } + /** + * Returns <code>true</code>. + * + * @return <code>true</code> + */ public boolean isLeaf() { return true; } + /** + * Returns a string representation of this <code>Element</code>. + * + * @return a string representation of this <code>Element</code> + */ public String toString() { return ("LeafElement(" + getName() + ") " |