Merge from mainline.
[official-gcc.git] / libjava / classpath / javax / swing / text / AbstractDocument.java
blob1ef81732fed45222c99310114677656b502409ca
1 /* AbstractDocument.java --
2 Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package javax.swing.text;
41 import java.io.PrintStream;
42 import java.io.Serializable;
43 import java.util.Dictionary;
44 import java.util.Enumeration;
45 import java.util.EventListener;
46 import java.util.Hashtable;
47 import java.util.Vector;
49 import javax.swing.event.DocumentEvent;
50 import javax.swing.event.DocumentListener;
51 import javax.swing.event.EventListenerList;
52 import javax.swing.event.UndoableEditEvent;
53 import javax.swing.event.UndoableEditListener;
54 import javax.swing.text.DocumentFilter;
55 import javax.swing.tree.TreeNode;
56 import javax.swing.undo.AbstractUndoableEdit;
57 import javax.swing.undo.CompoundEdit;
58 import javax.swing.undo.UndoableEdit;
60 /**
61 * An abstract base implementation for the {@link Document} interface.
62 * This class provides some common functionality for all <code>Element</code>s,
63 * most notably it implements a locking mechanism to make document modification
64 * thread-safe.
66 * @author original author unknown
67 * @author Roman Kennke (roman@kennke.org)
69 public abstract class AbstractDocument implements Document, Serializable
71 /** The serialization UID (compatible with JDK1.5). */
72 private static final long serialVersionUID = 6842927725919637215L;
74 /**
75 * Standard error message to indicate a bad location.
77 protected static final String BAD_LOCATION = "document location failure";
79 /**
80 * Standard name for unidirectional <code>Element</code>s.
82 public static final String BidiElementName = "bidi level";
84 /**
85 * Standard name for content <code>Element</code>s. These are usually
86 * {@link LeafElement}s.
88 public static final String ContentElementName = "content";
90 /**
91 * Standard name for paragraph <code>Element</code>s. These are usually
92 * {@link BranchElement}s.
94 public static final String ParagraphElementName = "paragraph";
96 /**
97 * Standard name for section <code>Element</code>s. These are usually
98 * {@link DefaultStyledDocument.SectionElement}s.
100 public static final String SectionElementName = "section";
103 * Attribute key for storing the element name.
105 public static final String ElementNameAttribute = "$ename";
108 * The actual content model of this <code>Document</code>.
110 Content content;
113 * The AttributeContext for this <code>Document</code>.
115 AttributeContext context;
118 * The currently installed <code>DocumentFilter</code>.
120 DocumentFilter documentFilter;
123 * The documents properties.
125 Dictionary properties;
128 * Manages event listeners for this <code>Document</code>.
130 protected EventListenerList listenerList = new EventListenerList();
133 * Stores the current writer thread. Used for locking.
135 private Thread currentWriter = null;
138 * The number of readers. Used for locking.
140 private int numReaders = 0;
143 * Tells if there are one or more writers waiting.
145 private int numWritersWaiting = 0;
148 * A condition variable that readers and writers wait on.
150 Object documentCV = new Object();
152 /** An instance of a DocumentFilter.FilterBypass which allows calling
153 * the insert, remove and replace method without checking for an installed
154 * document filter.
156 DocumentFilter.FilterBypass bypass;
159 * Creates a new <code>AbstractDocument</code> with the specified
160 * {@link Content} model.
162 * @param doc the <code>Content</code> model to be used in this
163 * <code>Document<code>
165 * @see GapContent
166 * @see StringContent
168 protected AbstractDocument(Content doc)
170 this(doc, StyleContext.getDefaultStyleContext());
174 * Creates a new <code>AbstractDocument</code> with the specified
175 * {@link Content} model and {@link AttributeContext}.
177 * @param doc the <code>Content</code> model to be used in this
178 * <code>Document<code>
179 * @param ctx the <code>AttributeContext</code> to use
181 * @see GapContent
182 * @see StringContent
184 protected AbstractDocument(Content doc, AttributeContext ctx)
186 content = doc;
187 context = ctx;
190 /** Returns the DocumentFilter.FilterBypass instance for this
191 * document and create it if it does not exist yet.
193 * @return This document's DocumentFilter.FilterBypass instance.
195 private DocumentFilter.FilterBypass getBypass()
197 if (bypass == null)
198 bypass = new Bypass();
200 return bypass;
204 * Returns the paragraph {@link Element} that holds the specified position.
206 * @param pos the position for which to get the paragraph element
208 * @return the paragraph {@link Element} that holds the specified position
210 public abstract Element getParagraphElement(int pos);
213 * Returns the default root {@link Element} of this <code>Document</code>.
214 * Usual <code>Document</code>s only have one root element and return this.
215 * However, there may be <code>Document</code> implementations that
216 * support multiple root elements, they have to return a default root element
217 * here.
219 * @return the default root {@link Element} of this <code>Document</code>
221 public abstract Element getDefaultRootElement();
224 * Creates and returns a branch element with the specified
225 * <code>parent</code> and <code>attributes</code>. Note that the new
226 * <code>Element</code> is linked to the parent <code>Element</code>
227 * through {@link Element#getParentElement}, but it is not yet added
228 * to the parent <code>Element</code> as child.
230 * @param parent the parent <code>Element</code> for the new branch element
231 * @param attributes the text attributes to be installed in the new element
233 * @return the new branch <code>Element</code>
235 * @see BranchElement
237 protected Element createBranchElement(Element parent,
238 AttributeSet attributes)
240 return new BranchElement(parent, attributes);
244 * Creates and returns a leaf element with the specified
245 * <code>parent</code> and <code>attributes</code>. Note that the new
246 * <code>Element</code> is linked to the parent <code>Element</code>
247 * through {@link Element#getParentElement}, but it is not yet added
248 * to the parent <code>Element</code> as child.
250 * @param parent the parent <code>Element</code> for the new branch element
251 * @param attributes the text attributes to be installed in the new element
253 * @return the new branch <code>Element</code>
255 * @see LeafElement
257 protected Element createLeafElement(Element parent, AttributeSet attributes,
258 int start, int end)
260 return new LeafElement(parent, attributes, start, end);
264 * Creates a {@link Position} that keeps track of the location at the
265 * specified <code>offset</code>.
267 * @param offset the location in the document to keep track by the new
268 * <code>Position</code>
270 * @return the newly created <code>Position</code>
272 * @throws BadLocationException if <code>offset</code> is not a valid
273 * location in the documents content model
275 public Position createPosition(final int offset) throws BadLocationException
277 return content.createPosition(offset);
281 * Notifies all registered listeners when the document model changes.
283 * @param event the <code>DocumentEvent</code> to be fired
285 protected void fireChangedUpdate(DocumentEvent event)
287 DocumentListener[] listeners = getDocumentListeners();
289 for (int index = 0; index < listeners.length; ++index)
290 listeners[index].changedUpdate(event);
294 * Notifies all registered listeners when content is inserted in the document
295 * model.
297 * @param event the <code>DocumentEvent</code> to be fired
299 protected void fireInsertUpdate(DocumentEvent event)
301 DocumentListener[] listeners = getDocumentListeners();
303 for (int index = 0; index < listeners.length; ++index)
304 listeners[index].insertUpdate(event);
308 * Notifies all registered listeners when content is removed from the
309 * document model.
311 * @param event the <code>DocumentEvent</code> to be fired
313 protected void fireRemoveUpdate(DocumentEvent event)
315 DocumentListener[] listeners = getDocumentListeners();
317 for (int index = 0; index < listeners.length; ++index)
318 listeners[index].removeUpdate(event);
322 * Notifies all registered listeners when an <code>UndoableEdit</code> has
323 * been performed on this <code>Document</code>.
325 * @param event the <code>UndoableEditEvent</code> to be fired
327 protected void fireUndoableEditUpdate(UndoableEditEvent event)
329 UndoableEditListener[] listeners = getUndoableEditListeners();
331 for (int index = 0; index < listeners.length; ++index)
332 listeners[index].undoableEditHappened(event);
336 * Returns the asynchronous loading priority. Returns <code>-1</code> if this
337 * document should not be loaded asynchronously.
339 * @return the asynchronous loading priority
341 public int getAsynchronousLoadPriority()
343 return 0;
347 * Returns the {@link AttributeContext} used in this <code>Document</code>.
349 * @return the {@link AttributeContext} used in this <code>Document</code>
351 protected final AttributeContext getAttributeContext()
353 return context;
357 * Returns the root element for bidirectional content.
359 * @return the root element for bidirectional content
361 public Element getBidiRootElement()
363 return null;
367 * Returns the {@link Content} model for this <code>Document</code>
369 * @return the {@link Content} model for this <code>Document</code>
371 * @see GapContent
372 * @see StringContent
374 protected final Content getContent()
376 return content;
380 * Returns the thread that currently modifies this <code>Document</code>
381 * if there is one, otherwise <code>null</code>. This can be used to
382 * distinguish between a method call that is part of an ongoing modification
383 * or if it is a separate modification for which a new lock must be aquired.
385 * @return the thread that currently modifies this <code>Document</code>
386 * if there is one, otherwise <code>null</code>
388 protected final Thread getCurrentWriter()
390 return currentWriter;
394 * Returns the properties of this <code>Document</code>.
396 * @return the properties of this <code>Document</code>
398 public Dictionary getDocumentProperties()
400 // FIXME: make me thread-safe
401 if (properties == null)
402 properties = new Hashtable();
404 return properties;
408 * Returns a {@link Position} which will always mark the end of the
409 * <code>Document</code>.
411 * @return a {@link Position} which will always mark the end of the
412 * <code>Document</code>
414 public final Position getEndPosition()
416 // FIXME: Properly implement this by calling Content.createPosition().
417 return new Position()
419 public int getOffset()
421 return getLength();
427 * Returns the length of this <code>Document</code>'s content.
429 * @return the length of this <code>Document</code>'s content
431 public int getLength()
433 // We return Content.getLength() -1 here because there is always an
434 // implicit \n at the end of the Content which does count in Content
435 // but not in Document.
436 return content.length() - 1;
440 * Returns all registered listeners of a given listener type.
442 * @param listenerType the type of the listeners to be queried
444 * @return all registered listeners of the specified type
446 public EventListener[] getListeners(Class listenerType)
448 return listenerList.getListeners(listenerType);
452 * Returns a property from this <code>Document</code>'s property list.
454 * @param key the key of the property to be fetched
456 * @return the property for <code>key</code> or <code>null</code> if there
457 * is no such property stored
459 public final Object getProperty(Object key)
461 // FIXME: make me thread-safe
462 Object value = null;
463 if (properties != null)
464 value = properties.get(key);
466 return value;
470 * Returns all root elements of this <code>Document</code>. By default
471 * this just returns the single root element returned by
472 * {@link #getDefaultRootElement()}. <code>Document</code> implementations
473 * that support multiple roots must override this method and return all roots
474 * here.
476 * @return all root elements of this <code>Document</code>
478 public Element[] getRootElements()
480 Element[] elements = new Element[1];
481 elements[0] = getDefaultRootElement();
482 return elements;
486 * Returns a {@link Position} which will always mark the beginning of the
487 * <code>Document</code>.
489 * @return a {@link Position} which will always mark the beginning of the
490 * <code>Document</code>
492 public final Position getStartPosition()
494 // FIXME: Properly implement this using Content.createPosition().
495 return new Position()
497 public int getOffset()
499 return 0;
505 * Returns a piece of this <code>Document</code>'s content.
507 * @param offset the start offset of the content
508 * @param length the length of the content
510 * @return the piece of content specified by <code>offset</code> and
511 * <code>length</code>
513 * @throws BadLocationException if <code>offset</code> or <code>offset +
514 * length</code> are invalid locations with this
515 * <code>Document</code>
517 public String getText(int offset, int length) throws BadLocationException
519 return content.getString(offset, length);
523 * Fetches a piece of this <code>Document</code>'s content and stores
524 * it in the given {@link Segment}.
526 * @param offset the start offset of the content
527 * @param length the length of the content
528 * @param segment the <code>Segment</code> to store the content in
530 * @throws BadLocationException if <code>offset</code> or <code>offset +
531 * length</code> are invalid locations with this
532 * <code>Document</code>
534 public void getText(int offset, int length, Segment segment)
535 throws BadLocationException
537 content.getChars(offset, length, segment);
541 * Inserts a String into this <code>Document</code> at the specified
542 * position and assigning the specified attributes to it.
544 * <p>If a {@link DocumentFilter} is installed in this document, the
545 * corresponding method of the filter object is called.</p>
547 * <p>The method has no effect when <code>text</code> is <code>null</code>
548 * or has a length of zero.</p>
551 * @param offset the location at which the string should be inserted
552 * @param text the content to be inserted
553 * @param attributes the text attributes to be assigned to that string
555 * @throws BadLocationException if <code>offset</code> is not a valid
556 * location in this <code>Document</code>
558 public void insertString(int offset, String text, AttributeSet attributes)
559 throws BadLocationException
561 // Bail out if we have a bogus insertion (Behavior observed in RI).
562 if (text == null || text.length() == 0)
563 return;
565 if (documentFilter == null)
566 insertStringImpl(offset, text, attributes);
567 else
568 documentFilter.insertString(getBypass(), offset, text, attributes);
571 void insertStringImpl(int offset, String text, AttributeSet attributes)
572 throws BadLocationException
574 // Just return when no text to insert was given.
575 if (text == null || text.length() == 0)
576 return;
577 DefaultDocumentEvent event =
578 new DefaultDocumentEvent(offset, text.length(),
579 DocumentEvent.EventType.INSERT);
583 writeLock();
584 UndoableEdit undo = content.insertString(offset, text);
585 if (undo != null)
586 event.addEdit(undo);
588 insertUpdate(event, attributes);
590 fireInsertUpdate(event);
591 if (undo != null)
592 fireUndoableEditUpdate(new UndoableEditEvent(this, undo));
594 finally
596 writeUnlock();
601 * Called to indicate that text has been inserted into this
602 * <code>Document</code>. The default implementation does nothing.
603 * This method is executed within a write lock.
605 * @param chng the <code>DefaultDocumentEvent</code> describing the change
606 * @param attr the attributes of the changed content
608 protected void insertUpdate(DefaultDocumentEvent chng, AttributeSet attr)
610 // Do nothing here. Subclasses may want to override this.
614 * Called after some content has been removed from this
615 * <code>Document</code>. The default implementation does nothing.
616 * This method is executed within a write lock.
618 * @param chng the <code>DefaultDocumentEvent</code> describing the change
620 protected void postRemoveUpdate(DefaultDocumentEvent chng)
622 // Do nothing here. Subclasses may want to override this.
626 * Stores a property in this <code>Document</code>'s property list.
628 * @param key the key of the property to be stored
629 * @param value the value of the property to be stored
631 public final void putProperty(Object key, Object value)
633 // FIXME: make me thread-safe
634 if (properties == null)
635 properties = new Hashtable();
637 properties.put(key, value);
641 * Blocks until a read lock can be obtained. Must block if there is
642 * currently a writer modifying the <code>Document</code>.
644 public final void readLock()
646 if (currentWriter != null && currentWriter.equals(Thread.currentThread()))
647 return;
648 synchronized (documentCV)
650 while (currentWriter != null || numWritersWaiting > 0)
654 documentCV.wait();
656 catch (InterruptedException ie)
658 throw new Error("interrupted trying to get a readLock");
661 numReaders++;
666 * Releases the read lock. If this was the only reader on this
667 * <code>Document</code>, writing may begin now.
669 public final void readUnlock()
671 // Note we could have a problem here if readUnlock was called without a
672 // prior call to readLock but the specs simply warn users to ensure that
673 // balance by using a finally block:
674 // readLock()
675 // try
676 // {
677 // doSomethingHere
678 // }
679 // finally
680 // {
681 // readUnlock();
682 // }
684 // All that the JDK seems to check for is that you don't call unlock
685 // more times than you've previously called lock, but it doesn't make
686 // sure that the threads calling unlock were the same ones that called lock
688 // If the current thread holds the write lock, and attempted to also obtain
689 // a readLock, then numReaders hasn't been incremented and we don't need
690 // to unlock it here.
691 if (currentWriter == Thread.currentThread())
692 return;
694 // FIXME: the reference implementation throws a
695 // javax.swing.text.StateInvariantError here
696 if (numReaders == 0)
697 throw new IllegalStateException("document lock failure");
699 synchronized (documentCV)
701 // If currentWriter is not null, the application code probably had a
702 // writeLock and then tried to obtain a readLock, in which case
703 // numReaders wasn't incremented
704 if (currentWriter == null)
706 numReaders --;
707 if (numReaders == 0 && numWritersWaiting != 0)
708 documentCV.notify();
714 * Removes a piece of content from this <code>Document</code>.
716 * <p>If a {@link DocumentFilter} is installed in this document, the
717 * corresponding method of the filter object is called. The
718 * <code>DocumentFilter</code> is called even if <code>length</code>
719 * is zero. This is different from {@link #replace}.</p>
721 * <p>Note: When <code>length</code> is zero or below the call is not
722 * forwarded to the underlying {@link AbstractDocument.Content} instance
723 * of this document and no exception is thrown.</p>
725 * @param offset the start offset of the fragment to be removed
726 * @param length the length of the fragment to be removed
728 * @throws BadLocationException if <code>offset</code> or
729 * <code>offset + length</code> or invalid locations within this
730 * document
732 public void remove(int offset, int length) throws BadLocationException
734 if (documentFilter == null)
735 removeImpl(offset, length);
736 else
737 documentFilter.remove(getBypass(), offset, length);
740 void removeImpl(int offset, int length) throws BadLocationException
742 // Prevent some unneccessary method invocation (observed in the RI).
743 if (length <= 0)
744 return;
746 DefaultDocumentEvent event =
747 new DefaultDocumentEvent(offset, length,
748 DocumentEvent.EventType.REMOVE);
752 writeLock();
754 // The order of the operations below is critical!
755 removeUpdate(event);
756 UndoableEdit temp = content.remove(offset, length);
758 postRemoveUpdate(event);
759 fireRemoveUpdate(event);
761 finally
763 writeUnlock();
768 * Replaces a piece of content in this <code>Document</code> with
769 * another piece of content.
771 * <p>If a {@link DocumentFilter} is installed in this document, the
772 * corresponding method of the filter object is called.</p>
774 * <p>The method has no effect if <code>length</code> is zero (and
775 * only zero) and, at the same time, <code>text</code> is
776 * <code>null</code> or has zero length.</p>
778 * @param offset the start offset of the fragment to be removed
779 * @param length the length of the fragment to be removed
780 * @param text the text to replace the content with
781 * @param attributes the text attributes to assign to the new content
783 * @throws BadLocationException if <code>offset</code> or
784 * <code>offset + length</code> or invalid locations within this
785 * document
787 * @since 1.4
789 public void replace(int offset, int length, String text,
790 AttributeSet attributes)
791 throws BadLocationException
793 // Bail out if we have a bogus replacement (Behavior observed in RI).
794 if (length == 0
795 && (text == null || text.length() == 0))
796 return;
798 if (documentFilter == null)
800 // It is important to call the methods which again do the checks
801 // of the arguments and the DocumentFilter because subclasses may
802 // have overridden these methods and provide crucial behavior
803 // which would be skipped if we call the non-checking variants.
804 // An example for this is PlainDocument where insertString can
805 // provide a filtering of newlines.
806 remove(offset, length);
807 insertString(offset, text, attributes);
809 else
810 documentFilter.replace(getBypass(), offset, length, text, attributes);
814 void replaceImpl(int offset, int length, String text,
815 AttributeSet attributes)
816 throws BadLocationException
818 removeImpl(offset, length);
819 insertStringImpl(offset, text, attributes);
823 * Adds a <code>DocumentListener</code> object to this document.
825 * @param listener the listener to add
827 public void addDocumentListener(DocumentListener listener)
829 listenerList.add(DocumentListener.class, listener);
833 * Removes a <code>DocumentListener</code> object from this document.
835 * @param listener the listener to remove
837 public void removeDocumentListener(DocumentListener listener)
839 listenerList.remove(DocumentListener.class, listener);
843 * Returns all registered <code>DocumentListener</code>s.
845 * @return all registered <code>DocumentListener</code>s
847 public DocumentListener[] getDocumentListeners()
849 return (DocumentListener[]) getListeners(DocumentListener.class);
853 * Adds an {@link UndoableEditListener} to this <code>Document</code>.
855 * @param listener the listener to add
857 public void addUndoableEditListener(UndoableEditListener listener)
859 listenerList.add(UndoableEditListener.class, listener);
863 * Removes an {@link UndoableEditListener} from this <code>Document</code>.
865 * @param listener the listener to remove
867 public void removeUndoableEditListener(UndoableEditListener listener)
869 listenerList.remove(UndoableEditListener.class, listener);
873 * Returns all registered {@link UndoableEditListener}s.
875 * @return all registered {@link UndoableEditListener}s
877 public UndoableEditListener[] getUndoableEditListeners()
879 return (UndoableEditListener[]) getListeners(UndoableEditListener.class);
883 * Called before some content gets removed from this <code>Document</code>.
884 * The default implementation does nothing but may be overridden by
885 * subclasses to modify the <code>Document</code> structure in response
886 * to a remove request. The method is executed within a write lock.
888 * @param chng the <code>DefaultDocumentEvent</code> describing the change
890 protected void removeUpdate(DefaultDocumentEvent chng)
892 // Do nothing here. Subclasses may wish to override this.
896 * Called to render this <code>Document</code> visually. It obtains a read
897 * lock, ensuring that no changes will be made to the <code>document</code>
898 * during the rendering process. It then calls the {@link Runnable#run()}
899 * method on <code>runnable</code>. This method <em>must not</em> attempt
900 * to modifiy the <code>Document</code>, since a deadlock will occur if it
901 * tries to obtain a write lock. When the {@link Runnable#run()} method
902 * completes (either naturally or by throwing an exception), the read lock
903 * is released. Note that there is nothing in this method related to
904 * the actual rendering. It could be used to execute arbitrary code within
905 * a read lock.
907 * @param runnable the {@link Runnable} to execute
909 public void render(Runnable runnable)
911 readLock();
914 runnable.run();
916 finally
918 readUnlock();
923 * Sets the asynchronous loading priority for this <code>Document</code>.
924 * A value of <code>-1</code> indicates that this <code>Document</code>
925 * should be loaded synchronously.
927 * @param p the asynchronous loading priority to set
929 public void setAsynchronousLoadPriority(int p)
931 // TODO: Implement this properly.
935 * Sets the properties of this <code>Document</code>.
937 * @param p the document properties to set
939 public void setDocumentProperties(Dictionary p)
941 // FIXME: make me thread-safe
942 properties = p;
946 * Blocks until a write lock can be obtained. Must wait if there are
947 * readers currently reading or another thread is currently writing.
949 protected final void writeLock()
951 if (currentWriter != null && currentWriter.equals(Thread.currentThread()))
952 return;
953 synchronized (documentCV)
955 numWritersWaiting++;
956 while (numReaders > 0)
960 documentCV.wait();
962 catch (InterruptedException ie)
964 throw new Error("interruped while trying to obtain write lock");
967 numWritersWaiting --;
968 currentWriter = Thread.currentThread();
973 * Releases the write lock. This allows waiting readers or writers to
974 * obtain the lock.
976 protected final void writeUnlock()
978 synchronized (documentCV)
980 if (Thread.currentThread().equals(currentWriter))
982 currentWriter = null;
983 documentCV.notifyAll();
989 * Returns the currently installed {@link DocumentFilter} for this
990 * <code>Document</code>.
992 * @return the currently installed {@link DocumentFilter} for this
993 * <code>Document</code>
995 * @since 1.4
997 public DocumentFilter getDocumentFilter()
999 return documentFilter;
1003 * Sets the {@link DocumentFilter} for this <code>Document</code>.
1005 * @param filter the <code>DocumentFilter</code> to set
1007 * @since 1.4
1009 public void setDocumentFilter(DocumentFilter filter)
1011 this.documentFilter = filter;
1015 * Dumps diagnostic information to the specified <code>PrintStream</code>.
1017 * @param out the stream to write the diagnostic information to
1019 public void dump(PrintStream out)
1021 ((AbstractElement) getDefaultRootElement()).dump(out, 0);
1025 * Defines a set of methods for managing text attributes for one or more
1026 * <code>Document</code>s.
1028 * Replicating {@link AttributeSet}s throughout a <code>Document</code> can
1029 * be very expensive. Implementations of this interface are intended to
1030 * provide intelligent management of <code>AttributeSet</code>s, eliminating
1031 * costly duplication.
1033 * @see StyleContext
1035 public interface AttributeContext
1038 * Returns an {@link AttributeSet} that contains the attributes
1039 * of <code>old</code> plus the new attribute specified by
1040 * <code>name</code> and <code>value</code>.
1042 * @param old the attribute set to be merged with the new attribute
1043 * @param name the name of the attribute to be added
1044 * @param value the value of the attribute to be added
1046 * @return the old attributes plus the new attribute
1048 AttributeSet addAttribute(AttributeSet old, Object name, Object value);
1051 * Returns an {@link AttributeSet} that contains the attributes
1052 * of <code>old</code> plus the new attributes in <code>attributes</code>.
1054 * @param old the set of attributes where to add the new attributes
1055 * @param attributes the attributes to be added
1057 * @return an {@link AttributeSet} that contains the attributes
1058 * of <code>old</code> plus the new attributes in
1059 * <code>attributes</code>
1061 AttributeSet addAttributes(AttributeSet old, AttributeSet attributes);
1064 * Returns an empty {@link AttributeSet}.
1066 * @return an empty {@link AttributeSet}
1068 AttributeSet getEmptySet();
1071 * Called to indicate that the attributes in <code>attributes</code> are
1072 * no longer used.
1074 * @param attributes the attributes are no longer used
1076 void reclaim(AttributeSet attributes);
1079 * Returns a {@link AttributeSet} that has the attribute with the specified
1080 * <code>name</code> removed from <code>old</code>.
1082 * @param old the attribute set from which an attribute is removed
1083 * @param name the name of the attribute to be removed
1085 * @return the attributes of <code>old</code> minus the attribute
1086 * specified by <code>name</code>
1088 AttributeSet removeAttribute(AttributeSet old, Object name);
1091 * Removes all attributes in <code>attributes</code> from <code>old</code>
1092 * and returns the resulting <code>AttributeSet</code>.
1094 * @param old the set of attributes from which to remove attributes
1095 * @param attributes the attributes to be removed from <code>old</code>
1097 * @return the attributes of <code>old</code> minus the attributes in
1098 * <code>attributes</code>
1100 AttributeSet removeAttributes(AttributeSet old, AttributeSet attributes);
1103 * Removes all attributes specified by <code>names</code> from
1104 * <code>old</code> and returns the resulting <code>AttributeSet</code>.
1106 * @param old the set of attributes from which to remove attributes
1107 * @param names the names of the attributes to be removed from
1108 * <code>old</code>
1110 * @return the attributes of <code>old</code> minus the attributes in
1111 * <code>attributes</code>
1113 AttributeSet removeAttributes(AttributeSet old, Enumeration names);
1117 * A sequence of data that can be edited. This is were the actual content
1118 * in <code>AbstractDocument</code>'s is stored.
1120 public interface Content
1123 * Creates a {@link Position} that keeps track of the location at
1124 * <code>offset</code>.
1126 * @return a {@link Position} that keeps track of the location at
1127 * <code>offset</code>.
1129 * @throw BadLocationException if <code>offset</code> is not a valid
1130 * location in this <code>Content</code> model
1132 Position createPosition(int offset) throws BadLocationException;
1135 * Returns the length of the content.
1137 * @return the length of the content
1139 int length();
1142 * Inserts a string into the content model.
1144 * @param where the offset at which to insert the string
1145 * @param str the string to be inserted
1147 * @return an <code>UndoableEdit</code> or <code>null</code> if undo is
1148 * not supported by this <code>Content</code> model
1150 * @throws BadLocationException if <code>where</code> is not a valid
1151 * location in this <code>Content</code> model
1153 UndoableEdit insertString(int where, String str)
1154 throws BadLocationException;
1157 * Removes a piece of content from the content model.
1159 * @param where the offset at which to remove content
1160 * @param nitems the number of characters to be removed
1162 * @return an <code>UndoableEdit</code> or <code>null</code> if undo is
1163 * not supported by this <code>Content</code> model
1165 * @throws BadLocationException if <code>where</code> is not a valid
1166 * location in this <code>Content</code> model
1168 UndoableEdit remove(int where, int nitems) throws BadLocationException;
1171 * Returns a piece of content.
1173 * @param where the start offset of the requested fragment
1174 * @param len the length of the requested fragment
1176 * @return the requested fragment
1177 * @throws BadLocationException if <code>offset</code> or
1178 * <code>offset + len</code>is not a valid
1179 * location in this <code>Content</code> model
1181 String getString(int where, int len) throws BadLocationException;
1184 * Fetches a piece of content and stores it in <code>txt</code>.
1186 * @param where the start offset of the requested fragment
1187 * @param len the length of the requested fragment
1188 * @param txt the <code>Segment</code> where to fragment is stored into
1190 * @throws BadLocationException if <code>offset</code> or
1191 * <code>offset + len</code>is not a valid
1192 * location in this <code>Content</code> model
1194 void getChars(int where, int len, Segment txt) throws BadLocationException;
1198 * An abstract base implementation of the {@link Element} interface.
1200 public abstract class AbstractElement
1201 implements Element, MutableAttributeSet, TreeNode, Serializable
1203 /** The serialization UID (compatible with JDK1.5). */
1204 private static final long serialVersionUID = 1712240033321461704L;
1206 /** The number of characters that this Element spans. */
1207 int count;
1209 /** The starting offset of this Element. */
1210 int offset;
1212 /** The attributes of this Element. */
1213 AttributeSet attributes;
1215 /** The parent element. */
1216 Element element_parent;
1218 /** The parent in the TreeNode interface. */
1219 TreeNode tree_parent;
1221 /** The children of this element. */
1222 Vector tree_children;
1225 * Creates a new instance of <code>AbstractElement</code> with a
1226 * specified parent <code>Element</code> and <code>AttributeSet</code>.
1228 * @param p the parent of this <code>AbstractElement</code>
1229 * @param s the attributes to be assigned to this
1230 * <code>AbstractElement</code>
1232 public AbstractElement(Element p, AttributeSet s)
1234 element_parent = p;
1235 AttributeContext ctx = getAttributeContext();
1236 attributes = ctx.getEmptySet();
1237 if (s != null)
1238 attributes = ctx.addAttributes(attributes, s);
1242 * Returns the child nodes of this <code>Element</code> as an
1243 * <code>Enumeration</code> of {@link TreeNode}s.
1245 * @return the child nodes of this <code>Element</code> as an
1246 * <code>Enumeration</code> of {@link TreeNode}s
1248 public abstract Enumeration children();
1251 * Returns <code>true</code> if this <code>AbstractElement</code>
1252 * allows children.
1254 * @return <code>true</code> if this <code>AbstractElement</code>
1255 * allows children
1257 public abstract boolean getAllowsChildren();
1260 * Returns the child of this <code>AbstractElement</code> at
1261 * <code>index</code>.
1263 * @param index the position in the child list of the child element to
1264 * be returned
1266 * @return the child of this <code>AbstractElement</code> at
1267 * <code>index</code>
1269 public TreeNode getChildAt(int index)
1271 return (TreeNode) tree_children.get(index);
1275 * Returns the number of children of this <code>AbstractElement</code>.
1277 * @return the number of children of this <code>AbstractElement</code>
1279 public int getChildCount()
1281 return tree_children.size();
1285 * Returns the index of a given child <code>TreeNode</code> or
1286 * <code>-1</code> if <code>node</code> is not a child of this
1287 * <code>AbstractElement</code>.
1289 * @param node the node for which the index is requested
1291 * @return the index of a given child <code>TreeNode</code> or
1292 * <code>-1</code> if <code>node</code> is not a child of this
1293 * <code>AbstractElement</code>
1295 public int getIndex(TreeNode node)
1297 return tree_children.indexOf(node);
1301 * Returns the parent <code>TreeNode</code> of this
1302 * <code>AbstractElement</code> or <code>null</code> if this element
1303 * has no parent.
1305 * @return the parent <code>TreeNode</code> of this
1306 * <code>AbstractElement</code> or <code>null</code> if this
1307 * element has no parent
1309 public TreeNode getParent()
1311 return tree_parent;
1315 * Returns <code>true</code> if this <code>AbstractElement</code> is a
1316 * leaf element, <code>false</code> otherwise.
1318 * @return <code>true</code> if this <code>AbstractElement</code> is a
1319 * leaf element, <code>false</code> otherwise
1321 public abstract boolean isLeaf();
1324 * Adds an attribute to this element.
1326 * @param name the name of the attribute to be added
1327 * @param value the value of the attribute to be added
1329 public void addAttribute(Object name, Object value)
1331 attributes = getAttributeContext().addAttribute(attributes, name, value);
1335 * Adds a set of attributes to this element.
1337 * @param attrs the attributes to be added to this element
1339 public void addAttributes(AttributeSet attrs)
1341 attributes = getAttributeContext().addAttributes(attributes, attrs);
1345 * Removes an attribute from this element.
1347 * @param name the name of the attribute to be removed
1349 public void removeAttribute(Object name)
1351 attributes = getAttributeContext().removeAttribute(attributes, name);
1355 * Removes a set of attributes from this element.
1357 * @param attrs the attributes to be removed
1359 public void removeAttributes(AttributeSet attrs)
1361 attributes = getAttributeContext().removeAttributes(attributes, attrs);
1365 * Removes a set of attribute from this element.
1367 * @param names the names of the attributes to be removed
1369 public void removeAttributes(Enumeration names)
1371 attributes = getAttributeContext().removeAttributes(attributes, names);
1375 * Sets the parent attribute set against which the element can resolve
1376 * attributes that are not defined in itself.
1378 * @param parent the resolve parent to set
1380 public void setResolveParent(AttributeSet parent)
1382 attributes = getAttributeContext().addAttribute(attributes,
1383 ResolveAttribute,
1384 parent);
1388 * Returns <code>true</code> if this element contains the specified
1389 * attribute.
1391 * @param name the name of the attribute to check
1392 * @param value the value of the attribute to check
1394 * @return <code>true</code> if this element contains the specified
1395 * attribute
1397 public boolean containsAttribute(Object name, Object value)
1399 return attributes.containsAttribute(name, value);
1403 * Returns <code>true</code> if this element contains all of the
1404 * specified attributes.
1406 * @param attrs the attributes to check
1408 * @return <code>true</code> if this element contains all of the
1409 * specified attributes
1411 public boolean containsAttributes(AttributeSet attrs)
1413 return attributes.containsAttributes(attrs);
1417 * Returns a copy of the attributes of this element.
1419 * @return a copy of the attributes of this element
1421 public AttributeSet copyAttributes()
1423 return attributes.copyAttributes();
1427 * Returns the attribute value with the specified key. If this attribute
1428 * is not defined in this element and this element has a resolving
1429 * parent, the search goes upward to the resolve parent chain.
1431 * @param key the key of the requested attribute
1433 * @return the attribute value for <code>key</code> of <code>null</code>
1434 * if <code>key</code> is not found locally and cannot be resolved
1435 * in this element's resolve parents
1437 public Object getAttribute(Object key)
1439 Object result = attributes.getAttribute(key);
1440 if (result == null)
1442 AttributeSet resParent = getResolveParent();
1443 if (resParent != null)
1444 result = resParent.getAttribute(key);
1446 return result;
1450 * Returns the number of defined attributes in this element.
1452 * @return the number of defined attributes in this element
1454 public int getAttributeCount()
1456 return attributes.getAttributeCount();
1460 * Returns the names of the attributes of this element.
1462 * @return the names of the attributes of this element
1464 public Enumeration getAttributeNames()
1466 return attributes.getAttributeNames();
1470 * Returns the resolve parent of this element.
1471 * This is taken from the AttributeSet, but if this is null,
1472 * this method instead returns the Element's parent's
1473 * AttributeSet
1475 * @return the resolve parent of this element
1477 * @see #setResolveParent(AttributeSet)
1479 public AttributeSet getResolveParent()
1481 return attributes.getResolveParent();
1485 * Returns <code>true</code> if an attribute with the specified name
1486 * is defined in this element, <code>false</code> otherwise.
1488 * @param attrName the name of the requested attributes
1490 * @return <code>true</code> if an attribute with the specified name
1491 * is defined in this element, <code>false</code> otherwise
1493 public boolean isDefined(Object attrName)
1495 return attributes.isDefined(attrName);
1499 * Returns <code>true</code> if the specified <code>AttributeSet</code>
1500 * is equal to this element's <code>AttributeSet</code>, <code>false</code>
1501 * otherwise.
1503 * @param attrs the attributes to compare this element to
1505 * @return <code>true</code> if the specified <code>AttributeSet</code>
1506 * is equal to this element's <code>AttributeSet</code>,
1507 * <code>false</code> otherwise
1509 public boolean isEqual(AttributeSet attrs)
1511 return attributes.isEqual(attrs);
1515 * Returns the attributes of this element.
1517 * @return the attributes of this element
1519 public AttributeSet getAttributes()
1521 return this;
1525 * Returns the {@link Document} to which this element belongs.
1527 * @return the {@link Document} to which this element belongs
1529 public Document getDocument()
1531 return AbstractDocument.this;
1535 * Returns the child element at the specified <code>index</code>.
1537 * @param index the index of the requested child element
1539 * @return the requested element
1541 public abstract Element getElement(int index);
1544 * Returns the name of this element.
1546 * @return the name of this element
1548 public String getName()
1550 return (String) getAttribute(NameAttribute);
1554 * Returns the parent element of this element.
1556 * @return the parent element of this element
1558 public Element getParentElement()
1560 return element_parent;
1564 * Returns the offset inside the document model that is after the last
1565 * character of this element.
1567 * @return the offset inside the document model that is after the last
1568 * character of this element
1570 public abstract int getEndOffset();
1573 * Returns the number of child elements of this element.
1575 * @return the number of child elements of this element
1577 public abstract int getElementCount();
1580 * Returns the index of the child element that spans the specified
1581 * offset in the document model.
1583 * @param offset the offset for which the responsible element is searched
1585 * @return the index of the child element that spans the specified
1586 * offset in the document model
1588 public abstract int getElementIndex(int offset);
1591 * Returns the start offset if this element inside the document model.
1593 * @return the start offset if this element inside the document model
1595 public abstract int getStartOffset();
1598 * Prints diagnostic output to the specified stream.
1600 * @param stream the stream to write to
1601 * @param indent the indentation level
1603 public void dump(PrintStream stream, int indent)
1605 StringBuffer b = new StringBuffer();
1606 for (int i = 0; i < indent; ++i)
1607 b.append(' ');
1608 b.append('<');
1609 b.append(getName());
1610 // Dump attributes if there are any.
1611 if (getAttributeCount() > 0)
1613 b.append('\n');
1614 Enumeration attNames = getAttributeNames();
1615 while (attNames.hasMoreElements())
1617 for (int i = 0; i < indent + 2; ++i)
1618 b.append(' ');
1619 Object attName = attNames.nextElement();
1620 b.append(attName);
1621 b.append('=');
1622 Object attribute = getAttribute(attName);
1623 b.append(attribute);
1624 b.append('\n');
1627 b.append(">\n");
1629 // Dump element content for leaf elements.
1630 if (isLeaf())
1632 for (int i = 0; i < indent + 2; ++i)
1633 b.append(' ');
1634 int start = getStartOffset();
1635 int end = getEndOffset();
1636 b.append('[');
1637 b.append(start);
1638 b.append(',');
1639 b.append(end);
1640 b.append("][");
1643 b.append(getDocument().getText(start, end - start));
1645 catch (BadLocationException ex)
1647 AssertionError err = new AssertionError("BadLocationException "
1648 + "must not be thrown "
1649 + "here.");
1650 err.initCause(ex);
1651 throw err;
1653 b.append("]\n");
1655 stream.print(b.toString());
1657 // Dump child elements if any.
1658 int count = getElementCount();
1659 for (int i = 0; i < count; ++i)
1661 Element el = getElement(i);
1662 if (el instanceof AbstractElement)
1663 ((AbstractElement) el).dump(stream, indent + 2);
1669 * An implementation of {@link Element} to represent composite
1670 * <code>Element</code>s that contain other <code>Element</code>s.
1672 public class BranchElement extends AbstractElement
1674 /** The serialization UID (compatible with JDK1.5). */
1675 private static final long serialVersionUID = -6037216547466333183L;
1677 /** The child elements of this BranchElement. */
1678 private Element[] children = new Element[0];
1681 * The cached startOffset value. This is used in the case when a
1682 * BranchElement (temporarily) has no child elements.
1684 private int startOffset;
1687 * The cached endOffset value. This is used in the case when a
1688 * BranchElement (temporarily) has no child elements.
1690 private int endOffset;
1693 * Creates a new <code>BranchElement</code> with the specified
1694 * parent and attributes.
1696 * @param parent the parent element of this <code>BranchElement</code>
1697 * @param attributes the attributes to set on this
1698 * <code>BranchElement</code>
1700 public BranchElement(Element parent, AttributeSet attributes)
1702 super(parent, attributes);
1703 startOffset = -1;
1704 endOffset = -1;
1708 * Returns the children of this <code>BranchElement</code>.
1710 * @return the children of this <code>BranchElement</code>
1712 public Enumeration children()
1714 if (children.length == 0)
1715 return null;
1717 Vector tmp = new Vector();
1719 for (int index = 0; index < children.length; ++index)
1720 tmp.add(children[index]);
1722 return tmp.elements();
1726 * Returns <code>true</code> since <code>BranchElements</code> allow
1727 * child elements.
1729 * @return <code>true</code> since <code>BranchElements</code> allow
1730 * child elements
1732 public boolean getAllowsChildren()
1734 return true;
1738 * Returns the child element at the specified <code>index</code>.
1740 * @param index the index of the requested child element
1742 * @return the requested element
1744 public Element getElement(int index)
1746 if (index < 0 || index >= children.length)
1747 return null;
1749 return children[index];
1753 * Returns the number of child elements of this element.
1755 * @return the number of child elements of this element
1757 public int getElementCount()
1759 return children.length;
1763 * Returns the index of the child element that spans the specified
1764 * offset in the document model.
1766 * @param offset the offset for which the responsible element is searched
1768 * @return the index of the child element that spans the specified
1769 * offset in the document model
1771 public int getElementIndex(int offset)
1773 // If offset is less than the start offset of our first child,
1774 // return 0
1775 if (offset < getStartOffset())
1776 return 0;
1778 // XXX: There is surely a better algorithm
1779 // as beginning from first element each time.
1780 for (int index = 0; index < children.length - 1; ++index)
1782 Element elem = children[index];
1784 if ((elem.getStartOffset() <= offset)
1785 && (offset < elem.getEndOffset()))
1786 return index;
1787 // If the next element's start offset is greater than offset
1788 // then we have to return the closest Element, since no Elements
1789 // will contain the offset
1790 if (children[index + 1].getStartOffset() > offset)
1792 if ((offset - elem.getEndOffset()) > (children[index + 1].getStartOffset() - offset))
1793 return index + 1;
1794 else
1795 return index;
1799 // If offset is greater than the index of the last element, return
1800 // the index of the last element.
1801 return getElementCount() - 1;
1805 * Returns the offset inside the document model that is after the last
1806 * character of this element.
1807 * This is the end offset of the last child element. If this element
1808 * has no children, this method throws a <code>NullPointerException</code>.
1810 * @return the offset inside the document model that is after the last
1811 * character of this element
1813 * @throws NullPointerException if this branch element has no children
1815 public int getEndOffset()
1817 if (children.length == 0)
1819 if (endOffset == -1)
1820 throw new NullPointerException("BranchElement has no children.");
1822 else
1823 endOffset = children[children.length - 1].getEndOffset();
1825 return endOffset;
1829 * Returns the name of this element. This is {@link #ParagraphElementName}
1830 * in this case.
1832 * @return the name of this element
1834 public String getName()
1836 return ParagraphElementName;
1840 * Returns the start offset of this element inside the document model.
1841 * This is the start offset of the first child element. If this element
1842 * has no children, this method throws a <code>NullPointerException</code>.
1844 * @return the start offset of this element inside the document model
1846 * @throws NullPointerException if this branch element has no children and
1847 * no startOffset value has been cached
1849 public int getStartOffset()
1851 if (children.length == 0)
1853 if (startOffset == -1)
1854 throw new NullPointerException("BranchElement has no children.");
1856 else
1857 startOffset = children[0].getStartOffset();
1859 return startOffset;
1863 * Returns <code>false</code> since <code>BranchElement</code> are no
1864 * leafes.
1866 * @return <code>false</code> since <code>BranchElement</code> are no
1867 * leafes
1869 public boolean isLeaf()
1871 return false;
1875 * Returns the <code>Element</code> at the specified <code>Document</code>
1876 * offset.
1878 * @return the <code>Element</code> at the specified <code>Document</code>
1879 * offset
1881 * @see #getElementIndex(int)
1883 public Element positionToElement(int position)
1885 // XXX: There is surely a better algorithm
1886 // as beginning from first element each time.
1887 for (int index = 0; index < children.length; ++index)
1889 Element elem = children[index];
1891 if ((elem.getStartOffset() <= position)
1892 && (position < elem.getEndOffset()))
1893 return elem;
1896 return null;
1900 * Replaces a set of child elements with a new set of child elemens.
1902 * @param offset the start index of the elements to be removed
1903 * @param length the number of elements to be removed
1904 * @param elements the new elements to be inserted
1906 public void replace(int offset, int length, Element[] elements)
1908 Element[] target = new Element[children.length - length
1909 + elements.length];
1910 System.arraycopy(children, 0, target, 0, offset);
1911 System.arraycopy(elements, 0, target, offset, elements.length);
1912 System.arraycopy(children, offset + length, target,
1913 offset + elements.length,
1914 children.length - offset - length);
1915 children = target;
1919 * Returns a string representation of this element.
1921 * @return a string representation of this element
1923 public String toString()
1925 return ("BranchElement(" + getName() + ") "
1926 + getStartOffset() + "," + getEndOffset() + "\n");
1931 * Stores the changes when a <code>Document</code> is beeing modified.
1933 public class DefaultDocumentEvent extends CompoundEdit
1934 implements DocumentEvent
1936 /** The serialization UID (compatible with JDK1.5). */
1937 private static final long serialVersionUID = 5230037221564563284L;
1939 /** The starting offset of the change. */
1940 private int offset;
1942 /** The length of the change. */
1943 private int length;
1945 /** The type of change. */
1946 private DocumentEvent.EventType type;
1949 * Maps <code>Element</code> to their change records.
1951 Hashtable changes;
1954 * Indicates if this event has been modified or not. This is used to
1955 * determine if this event is thrown.
1957 boolean modified;
1960 * Creates a new <code>DefaultDocumentEvent</code>.
1962 * @param offset the starting offset of the change
1963 * @param length the length of the change
1964 * @param type the type of change
1966 public DefaultDocumentEvent(int offset, int length,
1967 DocumentEvent.EventType type)
1969 this.offset = offset;
1970 this.length = length;
1971 this.type = type;
1972 changes = new Hashtable();
1973 modified = false;
1977 * Adds an UndoableEdit to this <code>DocumentEvent</code>. If this
1978 * edit is an instance of {@link ElementEdit}, then this record can
1979 * later be fetched by calling {@link #getChange}.
1981 * @param edit the undoable edit to add
1983 public boolean addEdit(UndoableEdit edit)
1985 // XXX - Fully qualify ElementChange to work around gcj bug #2499.
1986 if (edit instanceof DocumentEvent.ElementChange)
1988 modified = true;
1989 DocumentEvent.ElementChange elEdit =
1990 (DocumentEvent.ElementChange) edit;
1991 changes.put(elEdit.getElement(), elEdit);
1993 return super.addEdit(edit);
1997 * Returns the document that has been modified.
1999 * @return the document that has been modified
2001 public Document getDocument()
2003 return AbstractDocument.this;
2007 * Returns the length of the modification.
2009 * @return the length of the modification
2011 public int getLength()
2013 return length;
2017 * Returns the start offset of the modification.
2019 * @return the start offset of the modification
2021 public int getOffset()
2023 return offset;
2027 * Returns the type of the modification.
2029 * @return the type of the modification
2031 public DocumentEvent.EventType getType()
2033 return type;
2037 * Returns the changes for an element.
2039 * @param elem the element for which the changes are requested
2041 * @return the changes for <code>elem</code> or <code>null</code> if
2042 * <code>elem</code> has not been changed
2044 public DocumentEvent.ElementChange getChange(Element elem)
2046 // XXX - Fully qualify ElementChange to work around gcj bug #2499.
2047 return (DocumentEvent.ElementChange) changes.get(elem);
2051 * Returns a String description of the change event. This returns the
2052 * toString method of the Vector of edits.
2054 public String toString()
2056 return edits.toString();
2061 * An implementation of {@link DocumentEvent.ElementChange} to be added
2062 * to {@link DefaultDocumentEvent}s.
2064 public static class ElementEdit extends AbstractUndoableEdit
2065 implements DocumentEvent.ElementChange
2067 /** The serial version UID of ElementEdit. */
2068 private static final long serialVersionUID = -1216620962142928304L;
2071 * The changed element.
2073 private Element elem;
2076 * The index of the change.
2078 private int index;
2081 * The removed elements.
2083 private Element[] removed;
2086 * The added elements.
2088 private Element[] added;
2091 * Creates a new <code>ElementEdit</code>.
2093 * @param elem the changed element
2094 * @param index the index of the change
2095 * @param removed the removed elements
2096 * @param added the added elements
2098 public ElementEdit(Element elem, int index,
2099 Element[] removed, Element[] added)
2101 this.elem = elem;
2102 this.index = index;
2103 this.removed = removed;
2104 this.added = added;
2108 * Returns the added elements.
2110 * @return the added elements
2112 public Element[] getChildrenAdded()
2114 return added;
2118 * Returns the removed elements.
2120 * @return the removed elements
2122 public Element[] getChildrenRemoved()
2124 return removed;
2128 * Returns the changed element.
2130 * @return the changed element
2132 public Element getElement()
2134 return elem;
2138 * Returns the index of the change.
2140 * @return the index of the change
2142 public int getIndex()
2144 return index;
2149 * An implementation of {@link Element} that represents a leaf in the
2150 * document structure. This is used to actually store content.
2152 public class LeafElement extends AbstractElement
2154 /** The serialization UID (compatible with JDK1.5). */
2155 private static final long serialVersionUID = -8906306331347768017L;
2158 * Manages the start offset of this element.
2160 private Position startPos;
2163 * Manages the end offset of this element.
2165 private Position endPos;
2168 * This gets possible added to the startOffset when a startOffset
2169 * outside the document range is requested.
2171 private int startDelta;
2174 * This gets possible added to the endOffset when a endOffset
2175 * outside the document range is requested.
2177 private int endDelta;
2180 * Creates a new <code>LeafElement</code>.
2182 * @param parent the parent of this <code>LeafElement</code>
2183 * @param attributes the attributes to be set
2184 * @param start the start index of this element inside the document model
2185 * @param end the end index of this element inside the document model
2187 public LeafElement(Element parent, AttributeSet attributes, int start,
2188 int end)
2190 super(parent, attributes);
2191 int len = content.length();
2192 startDelta = 0;
2193 if (start > len)
2194 startDelta = start - len;
2195 endDelta = 0;
2196 if (end > len)
2197 endDelta = end - len;
2200 startPos = createPosition(start - startDelta);
2201 endPos = createPosition(end - endDelta);
2203 catch (BadLocationException ex)
2205 AssertionError as;
2206 as = new AssertionError("BadLocationException thrown "
2207 + "here. start=" + start
2208 + ", end=" + end
2209 + ", length=" + getLength());
2210 as.initCause(ex);
2211 throw as;
2216 * Returns <code>null</code> since <code>LeafElement</code>s cannot have
2217 * children.
2219 * @return <code>null</code> since <code>LeafElement</code>s cannot have
2220 * children
2222 public Enumeration children()
2224 return null;
2228 * Returns <code>false</code> since <code>LeafElement</code>s cannot have
2229 * children.
2231 * @return <code>false</code> since <code>LeafElement</code>s cannot have
2232 * children
2234 public boolean getAllowsChildren()
2236 return false;
2240 * Returns <code>null</code> since <code>LeafElement</code>s cannot have
2241 * children.
2243 * @return <code>null</code> since <code>LeafElement</code>s cannot have
2244 * children
2246 public Element getElement(int index)
2248 return null;
2252 * Returns <code>0</code> since <code>LeafElement</code>s cannot have
2253 * children.
2255 * @return <code>0</code> since <code>LeafElement</code>s cannot have
2256 * children
2258 public int getElementCount()
2260 return 0;
2264 * Returns <code>-1</code> since <code>LeafElement</code>s cannot have
2265 * children.
2267 * @return <code>-1</code> since <code>LeafElement</code>s cannot have
2268 * children
2270 public int getElementIndex(int offset)
2272 return -1;
2276 * Returns the end offset of this <code>Element</code> inside the
2277 * document.
2279 * @return the end offset of this <code>Element</code> inside the
2280 * document
2282 public int getEndOffset()
2284 return endPos.getOffset() + endDelta;
2288 * Returns the name of this <code>Element</code>. This is
2289 * {@link #ContentElementName} in this case.
2291 * @return the name of this <code>Element</code>
2293 public String getName()
2295 String name = super.getName();
2296 if (name == null)
2297 name = ContentElementName;
2298 return name;
2302 * Returns the start offset of this <code>Element</code> inside the
2303 * document.
2305 * @return the start offset of this <code>Element</code> inside the
2306 * document
2308 public int getStartOffset()
2310 return startPos.getOffset() + startDelta;
2314 * Returns <code>true</code>.
2316 * @return <code>true</code>
2318 public boolean isLeaf()
2320 return true;
2324 * Returns a string representation of this <code>Element</code>.
2326 * @return a string representation of this <code>Element</code>
2328 public String toString()
2330 return ("LeafElement(" + getName() + ") "
2331 + getStartOffset() + "," + getEndOffset() + "\n");
2335 /** A class whose methods delegate to the insert, remove and replace methods
2336 * of this document which do not check for an installed DocumentFilter.
2338 class Bypass extends DocumentFilter.FilterBypass
2341 public Document getDocument()
2343 return AbstractDocument.this;
2346 public void insertString(int offset, String string, AttributeSet attr)
2347 throws BadLocationException
2349 AbstractDocument.this.insertStringImpl(offset, string, attr);
2352 public void remove(int offset, int length)
2353 throws BadLocationException
2355 AbstractDocument.this.removeImpl(offset, length);
2358 public void replace(int offset, int length, String string,
2359 AttributeSet attrs)
2360 throws BadLocationException
2362 AbstractDocument.this.replaceImpl(offset, length, string, attrs);