KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > text > AbstractDocument


1 /*
2  * @(#)AbstractDocument.java 1.151 04/07/13
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7 package javax.swing.text;
8
9 import java.util.*;
10 import java.io.*;
11 import java.awt.font.TextAttribute JavaDoc;
12 import java.text.Bidi JavaDoc;
13
14 import javax.swing.UIManager JavaDoc;
15 import javax.swing.undo.*;
16 import javax.swing.event.ChangeListener JavaDoc;
17 import javax.swing.event.*;
18 import javax.swing.tree.TreeNode JavaDoc;
19
20 import sun.font.BidiUtils;
21
22 /**
23  * An implementation of the document interface to serve as a
24  * basis for implementing various kinds of documents. At this
25  * level there is very little policy, so there is a corresponding
26  * increase in difficulty of use.
27  * <p>
28  * This class implements a locking mechanism for the document. It
29  * allows multiple readers or one writer, and writers must wait until
30  * all observers of the document have been notified of a previous
31  * change before beginning another mutation to the document. The
32  * read lock is acquired and released using the <code>render</code>
33  * method. A write lock is aquired by the methods that mutate the
34  * document, and are held for the duration of the method call.
35  * Notification is done on the thread that produced the mutation,
36  * and the thread has full read access to the document for the
37  * duration of the notification, but other readers are kept out
38  * until the notification has finished. The notification is a
39  * beans event notification which does not allow any further
40  * mutations until all listeners have been notified.
41  * <p>
42  * Any models subclassed from this class and used in conjunction
43  * with a text component that has a look and feel implementation
44  * that is derived from BasicTextUI may be safely updated
45  * asynchronously, because all access to the View hierarchy
46  * is serialized by BasicTextUI if the document is of type
47  * <code>AbstractDocument</code>. The locking assumes that an
48  * independant thread will access the View hierarchy only from
49  * the DocumentListener methods, and that there will be only
50  * one event thread active at a time.
51  * <p>
52  * If concurrency support is desired, there are the following
53  * additional implications. The code path for any DocumentListener
54  * implementation and any UndoListener implementation must be threadsafe,
55  * and not access the component lock if trying to be safe from deadlocks.
56  * The <code>repaint</code> and <code>revalidate</code> methods
57  * on JComponent are safe.
58  * <p>
59  * AbstractDocument models an implied break at the end of the document.
60  * Among other things this allows you to position the caret after the last
61  * character. As a result of this, <code>getLength</code> returns one less
62  * than the length of the Content. If you create your own Content, be
63  * sure and initialize it to have an additional character. Refer to
64  * StringContent and GapContent for examples of this. Another implication
65  * of this is that Elements that model the implied end character will have
66  * an endOffset == (getLength() + 1). For example, in DefaultStyledDocument
67  * <code>getParagraphElement(getLength()).getEndOffset() == getLength() + 1
68  * </code>.
69  * <p>
70  * <strong>Warning:</strong>
71  * Serialized objects of this class will not be compatible with
72  * future Swing releases. The current serialization support is
73  * appropriate for short term storage or RMI between applications running
74  * the same version of Swing. As of 1.4, support for long term storage
75  * of all JavaBeans<sup><font size="-2">TM</font></sup>
76  * has been added to the <code>java.beans</code> package.
77  * Please see {@link java.beans.XMLEncoder}.
78  *
79  * @author Timothy Prinzing
80  * @version 1.151 07/13/04
81  */

82 public abstract class AbstractDocument implements Document JavaDoc, Serializable {
83
84     /**
85      * Constructs a new <code>AbstractDocument</code>, wrapped around some
86      * specified content storage mechanism.
87      *
88      * @param data the content
89      */

90     protected AbstractDocument(Content data) {
91     this(data, StyleContext.getDefaultStyleContext());
92     }
93
94     /**
95      * Constructs a new <code>AbstractDocument</code>, wrapped around some
96      * specified content storage mechanism.
97      *
98      * @param data the content
99      * @param context the attribute context
100      */

101     protected AbstractDocument(Content data, AttributeContext context) {
102     this.data = data;
103     this.context = context;
104         bidiRoot = new BidiRootElement();
105
106     if (defaultI18NProperty == null) {
107         // determine default setting for i18n support
108
Object JavaDoc o = java.security.AccessController.doPrivileged(
109         new java.security.PrivilegedAction JavaDoc() {
110                     public Object JavaDoc run() {
111             return System.getProperty(I18NProperty);
112             }
113                 }
114         );
115         if (o != null) {
116         defaultI18NProperty = Boolean.valueOf((String JavaDoc)o);
117         } else {
118         defaultI18NProperty = Boolean.FALSE;
119         }
120     }
121     putProperty( I18NProperty, defaultI18NProperty);
122
123         //REMIND(bcb) This creates an initial bidi element to account for
124
//the \n that exists by default in the content. Doing it this way
125
//seems to expose a little too much knowledge of the content given
126
//to us by the sub-class. Consider having the sub-class' constructor
127
//make an initial call to insertUpdate.
128
writeLock();
129         try {
130             Element JavaDoc[] p = new Element JavaDoc[1];
131             p[0] = new BidiElement( bidiRoot, 0, 1, 0 );
132             bidiRoot.replace(0,0,p);
133         } finally {
134             writeUnlock();
135         }
136     }
137
138     /**
139      * Supports managing a set of properties. Callers
140      * can use the <code>documentProperties</code> dictionary
141      * to annotate the document with document-wide properties.
142      *
143      * @return a non-<code>null</code> <code>Dictionary</code>
144      * @see #setDocumentProperties
145      */

146     public Dictionary<Object JavaDoc,Object JavaDoc> getDocumentProperties() {
147     if (documentProperties == null) {
148         documentProperties = new Hashtable(2);
149     }
150     return documentProperties;
151     }
152
153     /**
154      * Replaces the document properties dictionary for this document.
155      *
156      * @param x the new dictionary
157      * @see #getDocumentProperties
158      */

159     public void setDocumentProperties(Dictionary<Object JavaDoc,Object JavaDoc> x) {
160     documentProperties = x;
161     }
162
163     /**
164      * Notifies all listeners that have registered interest for
165      * notification on this event type. The event instance
166      * is lazily created using the parameters passed into
167      * the fire method.
168      *
169      * @param e the event
170      * @see EventListenerList
171      */

172     protected void fireInsertUpdate(DocumentEvent e) {
173         notifyingListeners = true;
174         try {
175             // Guaranteed to return a non-null array
176
Object JavaDoc[] listeners = listenerList.getListenerList();
177             // Process the listeners last to first, notifying
178
// those that are interested in this event
179
for (int i = listeners.length-2; i>=0; i-=2) {
180                 if (listeners[i]==DocumentListener.class) {
181                     // Lazily create the event:
182
// if (e == null)
183
// e = new ListSelectionEvent(this, firstIndex, lastIndex);
184
((DocumentListener)listeners[i+1]).insertUpdate(e);
185                 }
186             }
187         } finally {
188             notifyingListeners = false;
189         }
190     }
191
192     /**
193      * Notifies all listeners that have registered interest for
194      * notification on this event type. The event instance
195      * is lazily created using the parameters passed into
196      * the fire method.
197      *
198      * @param e the event
199      * @see EventListenerList
200      */

201     protected void fireChangedUpdate(DocumentEvent e) {
202         notifyingListeners = true;
203         try {
204             // Guaranteed to return a non-null array
205
Object JavaDoc[] listeners = listenerList.getListenerList();
206             // Process the listeners last to first, notifying
207
// those that are interested in this event
208
for (int i = listeners.length-2; i>=0; i-=2) {
209                 if (listeners[i]==DocumentListener.class) {
210                     // Lazily create the event:
211
// if (e == null)
212
// e = new ListSelectionEvent(this, firstIndex, lastIndex);
213
((DocumentListener)listeners[i+1]).changedUpdate(e);
214                 }
215             }
216         } finally {
217             notifyingListeners = false;
218         }
219     }
220
221     /**
222      * Notifies all listeners that have registered interest for
223      * notification on this event type. The event instance
224      * is lazily created using the parameters passed into
225      * the fire method.
226      *
227      * @param e the event
228      * @see EventListenerList
229      */

230     protected void fireRemoveUpdate(DocumentEvent e) {
231         notifyingListeners = true;
232         try {
233             // Guaranteed to return a non-null array
234
Object JavaDoc[] listeners = listenerList.getListenerList();
235             // Process the listeners last to first, notifying
236
// those that are interested in this event
237
for (int i = listeners.length-2; i>=0; i-=2) {
238                 if (listeners[i]==DocumentListener.class) {
239                     // Lazily create the event:
240
// if (e == null)
241
// e = new ListSelectionEvent(this, firstIndex, lastIndex);
242
((DocumentListener)listeners[i+1]).removeUpdate(e);
243                 }
244             }
245         } finally {
246             notifyingListeners = false;
247         }
248     }
249
250     /**
251      * Notifies all listeners that have registered interest for
252      * notification on this event type. The event instance
253      * is lazily created using the parameters passed into
254      * the fire method.
255      *
256      * @param e the event
257      * @see EventListenerList
258      */

259     protected void fireUndoableEditUpdate(UndoableEditEvent e) {
260     // Guaranteed to return a non-null array
261
Object JavaDoc[] listeners = listenerList.getListenerList();
262     // Process the listeners last to first, notifying
263
// those that are interested in this event
264
for (int i = listeners.length-2; i>=0; i-=2) {
265         if (listeners[i]==UndoableEditListener.class) {
266         // Lazily create the event:
267
// if (e == null)
268
// e = new ListSelectionEvent(this, firstIndex, lastIndex);
269
((UndoableEditListener)listeners[i+1]).undoableEditHappened(e);
270         }
271     }
272     }
273
274     /**
275      * Returns an array of all the objects currently registered
276      * as <code><em>Foo</em>Listener</code>s
277      * upon this document.
278      * <code><em>Foo</em>Listener</code>s are registered using the
279      * <code>add<em>Foo</em>Listener</code> method.
280      *
281      * <p>
282      * You can specify the <code>listenerType</code> argument
283      * with a class literal, such as
284      * <code><em>Foo</em>Listener.class</code>.
285      * For example, you can query a
286      * document <code>d</code>
287      * for its document listeners with the following code:
288      *
289      * <pre>DocumentListener[] mls = (DocumentListener[])(d.getListeners(DocumentListener.class));</pre>
290      *
291      * If no such listeners exist, this method returns an empty array.
292      *
293      * @param listenerType the type of listeners requested; this parameter
294      * should specify an interface that descends from
295      * <code>java.util.EventListener</code>
296      * @return an array of all objects registered as
297      * <code><em>Foo</em>Listener</code>s on this component,
298      * or an empty array if no such
299      * listeners have been added
300      * @exception ClassCastException if <code>listenerType</code>
301      * doesn't specify a class or interface that implements
302      * <code>java.util.EventListener</code>
303      *
304      * @see #getDocumentListeners
305      * @see #getUndoableEditListeners
306      *
307      * @since 1.3
308      */

309     public <T extends EventListener> T[] getListeners(Class JavaDoc<T> listenerType) {
310     return listenerList.getListeners(listenerType);
311     }
312
313     /**
314      * Gets the asynchronous loading priority. If less than zero,
315      * the document should not be loaded asynchronously.
316      *
317      * @return the asynchronous loading priority, or <code>-1</code>
318      * if the document should not be loaded asynchronously
319      */

320     public int getAsynchronousLoadPriority() {
321     Integer JavaDoc loadPriority = (Integer JavaDoc)
322         getProperty(AbstractDocument.AsyncLoadPriority);
323     if (loadPriority != null) {
324         return loadPriority.intValue();
325     }
326     return -1;
327     }
328
329     /**
330      * Sets the asynchronous loading priority.
331      * @param p the new asynchronous loading priority; a value
332      * less than zero indicates that the document should not be
333      * loaded asynchronously
334      */

335     public void setAsynchronousLoadPriority(int p) {
336     Integer JavaDoc loadPriority = (p >= 0) ? new Integer JavaDoc(p) : null;
337     putProperty(AbstractDocument.AsyncLoadPriority, loadPriority);
338     }
339
340     /**
341      * Sets the <code>DocumentFilter</code>. The <code>DocumentFilter</code>
342      * is passed <code>insert</code> and <code>remove</code> to conditionally
343      * allow inserting/deleting of the text. A <code>null</code> value
344      * indicates that no filtering will occur.
345      *
346      * @param filter the <code>DocumentFilter</code> used to constrain text
347      * @see #getDocumentFilter
348      * @since 1.4
349      */

350     public void setDocumentFilter(DocumentFilter JavaDoc filter) {
351         documentFilter = filter;
352     }
353
354     /**
355      * Returns the <code>DocumentFilter</code> that is responsible for
356      * filtering of insertion/removal. A <code>null</code> return value
357      * implies no filtering is to occur.
358      *
359      * @since 1.4
360      * @see #setDocumentFilter
361      * @return the DocumentFilter
362      */

363     public DocumentFilter JavaDoc getDocumentFilter() {
364         return documentFilter;
365     }
366
367     // --- Document methods -----------------------------------------
368

369     /**
370      * This allows the model to be safely rendered in the presence
371      * of currency, if the model supports being updated asynchronously.
372      * The given runnable will be executed in a way that allows it
373      * to safely read the model with no changes while the runnable
374      * is being executed. The runnable itself may <em>not</em>
375      * make any mutations.
376      * <p>
377      * This is implemented to aquire a read lock for the duration
378      * of the runnables execution. There may be multiple runnables
379      * executing at the same time, and all writers will be blocked
380      * while there are active rendering runnables. If the runnable
381      * throws an exception, its lock will be safely released.
382      * There is no protection against a runnable that never exits,
383      * which will effectively leave the document locked for it's
384      * lifetime.
385      * <p>
386      * If the given runnable attempts to make any mutations in
387      * this implementation, a deadlock will occur. There is
388      * no tracking of individual rendering threads to enable
389      * detecting this situation, but a subclass could incur
390      * the overhead of tracking them and throwing an error.
391      * <p>
392      * This method is thread safe, although most Swing methods
393      * are not. Please see
394      * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
395      * and Swing</A> for more information.
396      *
397      * @param r the renderer to execute
398      */

399     public void render(Runnable JavaDoc r) {
400     readLock();
401     try {
402         r.run();
403     } finally {
404         readUnlock();
405     }
406     }
407
408     /**
409      * Returns the length of the data. This is the number of
410      * characters of content that represents the users data.
411      *
412      * @return the length >= 0
413      * @see Document#getLength
414      */

415     public int getLength() {
416     return data.length() - 1;
417     }
418
419     /**
420      * Adds a document listener for notification of any changes.
421      *
422      * @param listener the <code>DocumentListener</code> to add
423      * @see Document#addDocumentListener
424      */

425     public void addDocumentListener(DocumentListener listener) {
426     listenerList.add(DocumentListener.class, listener);
427     }
428
429     /**
430      * Removes a document listener.
431      *
432      * @param listener the <code>DocumentListener</code> to remove
433      * @see Document#removeDocumentListener
434      */

435     public void removeDocumentListener(DocumentListener listener) {
436     listenerList.remove(DocumentListener.class, listener);
437     }
438
439     /**
440      * Returns an array of all the document listeners
441      * registered on this document.
442      *
443      * @return all of this document's <code>DocumentListener</code>s
444      * or an empty array if no document listeners are
445      * currently registered
446      *
447      * @see #addDocumentListener
448      * @see #removeDocumentListener
449      * @since 1.4
450      */

451     public DocumentListener[] getDocumentListeners() {
452         return (DocumentListener[])listenerList.getListeners(
453                 DocumentListener.class);
454     }
455
456     /**
457      * Adds an undo listener for notification of any changes.
458      * Undo/Redo operations performed on the <code>UndoableEdit</code>
459      * will cause the appropriate DocumentEvent to be fired to keep
460      * the view(s) in sync with the model.
461      *
462      * @param listener the <code>UndoableEditListener</code> to add
463      * @see Document#addUndoableEditListener
464      */

465     public void addUndoableEditListener(UndoableEditListener listener) {
466     listenerList.add(UndoableEditListener.class, listener);
467     }
468
469     /**
470      * Removes an undo listener.
471      *
472      * @param listener the <code>UndoableEditListener</code> to remove
473      * @see Document#removeDocumentListener
474      */

475     public void removeUndoableEditListener(UndoableEditListener listener) {
476     listenerList.remove(UndoableEditListener.class, listener);
477     }
478
479     /**
480      * Returns an array of all the undoable edit listeners
481      * registered on this document.
482      *
483      * @return all of this document's <code>UndoableEditListener</code>s
484      * or an empty array if no undoable edit listeners are
485      * currently registered
486      *
487      * @see #addUndoableEditListener
488      * @see #removeUndoableEditListener
489      *
490      * @since 1.4
491      */

492     public UndoableEditListener[] getUndoableEditListeners() {
493         return (UndoableEditListener[])listenerList.getListeners(
494                 UndoableEditListener.class);
495     }
496
497     /**
498      * A convenience method for looking up a property value. It is
499      * equivalent to:
500      * <pre>
501      * getDocumentProperties().get(key);
502      * </pre>
503      *
504      * @param key the non-<code>null</code> property key
505      * @return the value of this property or <code>null</code>
506      * @see #getDocumentProperties
507      */

508     public final Object JavaDoc getProperty(Object JavaDoc key) {
509         return getDocumentProperties().get(key);
510     }
511
512
513     /**
514      * A convenience method for storing up a property value. It is
515      * equivalent to:
516      * <pre>
517      * getDocumentProperties().put(key, value);
518      * </pre>
519      * If <code>value</code> is <code>null</code> this method will
520      * remove the property.
521      *
522      * @param key the non-<code>null</code> key
523      * @param value the property value
524      * @see #getDocumentProperties
525      */

526     public final void putProperty(Object JavaDoc key, Object JavaDoc value) {
527     if (value != null) {
528         getDocumentProperties().put(key, value);
529     } else {
530             getDocumentProperties().remove(key);
531         }
532         if( key == TextAttribute.RUN_DIRECTION
533             && Boolean.TRUE.equals(getProperty(I18NProperty)) )
534         {
535             //REMIND - this needs to flip on the i18n property if run dir
536
//is rtl and the i18n property is not already on.
537
writeLock();
538             try {
539                 DefaultDocumentEvent e
540                     = new DefaultDocumentEvent(0, getLength(),
541                                                DocumentEvent.EventType.INSERT);
542                 updateBidi( e );
543             } finally {
544                 writeUnlock();
545             }
546         }
547     }
548
549     /**
550      * Removes some content from the document.
551      * Removing content causes a write lock to be held while the
552      * actual changes are taking place. Observers are notified
553      * of the change on the thread that called this method.
554      * <p>
555      * This method is thread safe, although most Swing methods
556      * are not. Please see
557      * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
558      * and Swing</A> for more information.
559      *
560      * @param offs the starting offset >= 0
561      * @param len the number of characters to remove >= 0
562      * @exception BadLocationException the given remove position is not a valid
563      * position within the document
564      * @see Document#remove
565      */

566     public void remove(int offs, int len) throws BadLocationException JavaDoc {
567         DocumentFilter JavaDoc filter = getDocumentFilter();
568
569         writeLock();
570         try {
571             if (filter != null) {
572                 filter.remove(getFilterBypass(), offs, len);
573             }
574             else {
575                 handleRemove(offs, len);
576             }
577         } finally {
578             writeUnlock();
579         }
580     }
581
582     /**
583      * Performs the actual work of the remove. It is assumed the caller
584      * will have obtained a <code>writeLock</code> before invoking this.
585      */

586     void handleRemove(int offs, int len) throws BadLocationException JavaDoc {
587     if (len > 0) {
588             if (offs < 0 || (offs + len) > getLength()) {
589                 throw new BadLocationException JavaDoc("Invalid remove",
590                                                getLength() + 1);
591             }
592             DefaultDocumentEvent chng =
593             new DefaultDocumentEvent(offs, len, DocumentEvent.EventType.REMOVE);
594
595             boolean isComposedTextElement = false;
596             // Check whether the position of interest is the composed text
597
isComposedTextElement = Utilities.isComposedTextElement(this, offs);
598         
599             removeUpdate(chng);
600             UndoableEdit u = data.remove(offs, len);
601             if (u != null) {
602                 chng.addEdit(u);
603             }
604             postRemoveUpdate(chng);
605             // Mark the edit as done.
606
chng.end();
607             fireRemoveUpdate(chng);
608             // only fire undo if Content implementation supports it
609
// undo for the composed text is not supported for now
610
if ((u != null) && !isComposedTextElement) {
611                 fireUndoableEditUpdate(new UndoableEditEvent(this, chng));
612             }
613     }
614     }
615     
616     /**
617      * Indic, Thai, and surrogate char values require complex
618      * text layout and cursor support.
619      */

620     private static final boolean isComplex(char ch) {
621         return (ch >= '\u0900' && ch <= '\u0D7F') || // Indic
622
(ch >= '\u0E00' && ch <= '\u0E7F') || // Thai
623
(ch >= '\uD800' && ch <= '\uDFFF'); // surrogate value range
624
}
625
626     private static final boolean isComplex(char[] text, int start, int limit) {
627     for (int i = start; i < limit; ++i) {
628         if (isComplex(text[i])) {
629         return true;
630         }
631     }
632     return false;
633     }
634
635     /**
636      * Deletes the region of text from <code>offset</code> to
637      * <code>offset + length</code>, and replaces it with <code>text</code>.
638      * It is up to the implementation as to how this is implemented, some
639      * implementations may treat this as two distinct operations: a remove
640      * followed by an insert, others may treat the replace as one atomic
641      * operation.
642      *
643      * @param offset index of child element
644      * @param length length of text to delete, may be 0 indicating don't
645      * delete anything
646      * @param text text to insert, <code>null</code> indicates no text to insert
647      * @param attrs AttributeSet indicating attributes of inserted text,
648      * <code>null</code>
649      * is legal, and typically treated as an empty attributeset,
650      * but exact interpretation is left to the subclass
651      * @exception BadLocationException the given position is not a valid
652      * position within the document
653      * @since 1.4
654      */

655     public void replace(int offset, int length, String JavaDoc text,
656                         AttributeSet JavaDoc attrs) throws BadLocationException JavaDoc {
657         if (length == 0 && (text == null || text.length() == 0)) {
658             return;
659         }
660         DocumentFilter JavaDoc filter = getDocumentFilter();
661
662     writeLock();
663     try {
664             if (filter != null) {
665                 filter.replace(getFilterBypass(), offset, length, text,
666                                attrs);
667             }
668             else {
669                 if (length > 0) {
670                     remove(offset, length);
671                 }
672                 if (text != null && text.length() > 0) {
673                     insertString(offset, text, attrs);
674                 }
675             }
676         } finally {
677             writeUnlock();
678         }
679     }
680
681     /**
682      * Inserts some content into the document.
683      * Inserting content causes a write lock to be held while the
684      * actual changes are taking place, followed by notification
685      * to the observers on the thread that grabbed the write lock.
686      * <p>
687      * This method is thread safe, although most Swing methods
688      * are not. Please see
689      * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
690      * and Swing</A> for more information.
691      *
692      * @param offs the starting offset >= 0
693      * @param str the string to insert; does nothing with null/empty strings
694      * @param a the attributes for the inserted content
695      * @exception BadLocationException the given insert position is not a valid
696      * position within the document
697      * @see Document#insertString
698      */

699     public void insertString(int offs, String JavaDoc str, AttributeSet JavaDoc a) throws BadLocationException JavaDoc {
700         if ((str == null) || (str.length() == 0)) {
701         return;
702     }
703         DocumentFilter JavaDoc filter = getDocumentFilter();
704
705     writeLock();
706     try {
707             if (filter != null) {
708                 filter.insertString(getFilterBypass(), offs, str, a);
709             }
710             else {
711                 handleInsertString(offs, str, a);
712             }
713         } finally {
714             writeUnlock();
715         }
716     }
717
718     /**
719      * Performs the actual work of inserting the text; it is assumed the
720      * caller has obtained a write lock before invoking this.
721      */

722     void handleInsertString(int offs, String JavaDoc str, AttributeSet JavaDoc a)
723                          throws BadLocationException JavaDoc {
724         if ((str == null) || (str.length() == 0)) {
725         return;
726     }
727         UndoableEdit u = data.insertString(offs, str);
728         DefaultDocumentEvent e =
729             new DefaultDocumentEvent(offs, str.length(), DocumentEvent.EventType.INSERT);
730         if (u != null) {
731             e.addEdit(u);
732         }
733         
734         // see if complex glyph layout support is needed
735
if( getProperty(I18NProperty).equals( Boolean.FALSE ) ) {
736             // if a default direction of right-to-left has been specified,
737
// we want complex layout even if the text is all left to right.
738
Object JavaDoc d = getProperty(TextAttribute.RUN_DIRECTION);
739             if ((d != null) && (d.equals(TextAttribute.RUN_DIRECTION_RTL))) {
740                 putProperty( I18NProperty, Boolean.TRUE);
741             } else {
742         char[] chars = str.toCharArray();
743         if (Bidi.requiresBidi(chars, 0, chars.length) ||
744             isComplex(chars, 0, chars.length)) {
745             //
746
putProperty( I18NProperty, Boolean.TRUE);
747                 }
748             }
749         }
750
751         insertUpdate(e, a);
752         // Mark the edit as done.
753
e.end();
754         fireInsertUpdate(e);
755         // only fire undo if Content implementation supports it
756
// undo for the composed text is not supported for now
757
if (u != null &&
758             (a == null || !a.isDefined(StyleConstants.ComposedTextAttribute))) {
759             fireUndoableEditUpdate(new UndoableEditEvent(this, e));
760         }
761     }
762
763     /**
764      * Gets a sequence of text from the document.
765      *
766      * @param offset the starting offset >= 0
767      * @param length the number of characters to retrieve >= 0
768      * @return the text
769      * @exception BadLocationException the range given includes a position
770      * that is not a valid position within the document
771      * @see Document#getText
772      */

773     public String JavaDoc getText(int offset, int length) throws BadLocationException JavaDoc {
774     if (length < 0) {
775         throw new BadLocationException JavaDoc("Length must be positive", length);
776     }
777     String JavaDoc str = data.getString(offset, length);
778     return str;
779     }
780
781     /**
782      * Fetches the text contained within the given portion
783      * of the document.
784      * <p>
785      * If the partialReturn property on the txt parameter is false, the
786      * data returned in the Segment will be the entire length requested and
787      * may or may not be a copy depending upon how the data was stored.
788      * If the partialReturn property is true, only the amount of text that
789      * can be returned without creating a copy is returned. Using partial
790      * returns will give better performance for situations where large
791      * parts of the document are being scanned. The following is an example
792      * of using the partial return to access the entire document:
793      * <p>
794      * <pre>
795      * &nbsp; int nleft = doc.getDocumentLength();
796      * &nbsp; Segment text = new Segment();
797      * &nbsp; int offs = 0;
798      * &nbsp; text.setPartialReturn(true);
799      * &nbsp; while (nleft > 0) {
800      * &nbsp; doc.getText(offs, nleft, text);
801      * &nbsp; // do something with text
802      * &nbsp; nleft -= text.count;
803      * &nbsp; offs += text.count;
804      * &nbsp; }
805      * </pre>
806      *
807      * @param offset the starting offset >= 0
808      * @param length the number of characters to retrieve >= 0
809      * @param txt the Segment object to retrieve the text into
810      * @exception BadLocationException the range given includes a position
811      * that is not a valid position within the document
812      */

813     public void getText(int offset, int length, Segment JavaDoc txt) throws BadLocationException JavaDoc {
814     if (length < 0) {
815         throw new BadLocationException JavaDoc("Length must be positive", length);
816     }
817     data.getChars(offset, length, txt);
818     }
819
820     /**
821      * Returns a position that will track change as the document
822      * is altered.
823      * <p>
824      * This method is thread safe, although most Swing methods
825      * are not. Please see
826      * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
827      * and Swing</A> for more information.
828      *
829      * @param offs the position in the model >= 0
830      * @return the position
831      * @exception BadLocationException if the given position does not
832      * represent a valid location in the associated document
833      * @see Document#createPosition
834      */

835     public synchronized Position JavaDoc createPosition(int offs) throws BadLocationException JavaDoc {
836     return data.createPosition(offs);
837     }
838
839     /**
840      * Returns a position that represents the start of the document. The
841      * position returned can be counted on to track change and stay
842      * located at the beginning of the document.
843      *
844      * @return the position
845      */

846     public final Position JavaDoc getStartPosition() {
847     Position JavaDoc p;
848     try {
849         p = createPosition(0);
850     } catch (BadLocationException JavaDoc bl) {
851         p = null;
852     }
853     return p;
854     }
855     
856     /**
857      * Returns a position that represents the end of the document. The
858      * position returned can be counted on to track change and stay
859      * located at the end of the document.
860      *
861      * @return the position
862      */

863     public final Position JavaDoc getEndPosition() {
864     Position JavaDoc p;
865     try {
866         p = createPosition(data.length());
867     } catch (BadLocationException JavaDoc bl) {
868         p = null;
869     }
870     return p;
871     }
872
873     /**
874      * Gets all root elements defined. Typically, there
875      * will only be one so the default implementation
876      * is to return the default root element.
877      *
878      * @return the root element
879      */

880     public Element JavaDoc[] getRootElements() {
881     Element JavaDoc[] elems = new Element JavaDoc[2];
882     elems[0] = getDefaultRootElement();
883         elems[1] = getBidiRootElement();
884     return elems;
885     }
886
887     /**
888      * Returns the root element that views should be based upon
889      * unless some other mechanism for assigning views to element
890      * structures is provided.
891      *
892      * @return the root element
893      * @see Document#getDefaultRootElement
894      */

895     public abstract Element JavaDoc getDefaultRootElement();
896
897     // ---- local methods -----------------------------------------
898

899     /**
900      * Returns the <code>FilterBypass</code>. This will create one if one
901      * does not yet exist.
902      */

903     private DocumentFilter.FilterBypass JavaDoc getFilterBypass() {
904         if (filterBypass == null) {
905             filterBypass = new DefaultFilterBypass();
906         }
907         return filterBypass;
908     }
909
910     /**
911      * Returns the root element of the bidirectional structure for this
912      * document. Its children represent character runs with a given
913      * Unicode bidi level.
914      */

915     public Element JavaDoc getBidiRootElement() {
916         return bidiRoot;
917     }
918
919     /**
920      * Returns true if the text in the range <code>p0</code> to
921      * <code>p1</code> is left to right.
922      */

923     boolean isLeftToRight(int p0, int p1) {
924         if(!getProperty(I18NProperty).equals(Boolean.TRUE)) {
925         return true;
926     }
927     Element JavaDoc bidiRoot = getBidiRootElement();
928     int index = bidiRoot.getElementIndex(p0);
929     Element JavaDoc bidiElem = bidiRoot.getElement(index);
930     if(bidiElem.getEndOffset() >= p1) {
931         AttributeSet JavaDoc bidiAttrs = bidiElem.getAttributes();
932         return ((StyleConstants.getBidiLevel(bidiAttrs) % 2) == 0);
933     }
934     return true;
935     }
936
937     /**
938      * Get the paragraph element containing the given position. Sub-classes
939      * must define for themselves what exactly constitutes a paragraph. They
940      * should keep in mind however that a paragraph should at least be the
941      * unit of text over which to run the Unicode bidirectional algorithm.
942      *
943      * @param pos the starting offset >= 0
944      * @return the element */

945     public abstract Element JavaDoc getParagraphElement(int pos);
946
947
948     /**
949      * Fetches the context for managing attributes. This
950      * method effectively establishes the strategy used
951      * for compressing AttributeSet information.
952      *
953      * @return the context
954      */

955     protected final AttributeContext getAttributeContext() {
956     return context;
957     }
958
959     /**
960      * Updates document structure as a result of text insertion. This
961      * will happen within a write lock. If a subclass of
962      * this class reimplements this method, it should delegate to the
963      * superclass as well.
964      *
965      * @param chng a description of the change
966      * @param attr the attributes for the change
967      */

968     protected void insertUpdate(DefaultDocumentEvent chng, AttributeSet JavaDoc attr) {
969         if( getProperty(I18NProperty).equals( Boolean.TRUE ) )
970             updateBidi( chng );
971
972         // Check if a multi byte is encountered in the inserted text.
973
if (chng.type == DocumentEvent.EventType.INSERT &&
974                         chng.getLength() > 0 &&
975                         !Boolean.TRUE.equals(getProperty(MultiByteProperty))) {
976             Segment JavaDoc segment = SegmentCache.getSharedSegment();
977             try {
978                 getText(chng.getOffset(), chng.getLength(), segment);
979                 segment.first();
980                 do {
981                     if ((int)segment.current() > 255) {
982                         putProperty(MultiByteProperty, Boolean.TRUE);
983                         break;
984                     }
985                 } while (segment.next() != Segment.DONE);
986             } catch (BadLocationException JavaDoc ble) {
987                 // Should never happen
988
}
989             SegmentCache.releaseSharedSegment(segment);
990         }
991     }
992
993     /**
994      * Updates any document structure as a result of text removal. This
995      * method is called before the text is actually removed from the Content.
996      * This will happen within a write lock. If a subclass
997      * of this class reimplements this method, it should delegate to the
998      * superclass as well.
999      *
1000     * @param chng a description of the change
1001     */

1002    protected void removeUpdate(DefaultDocumentEvent chng) {
1003    }
1004
1005    /**
1006     * Updates any document structure as a result of text removal. This
1007     * method is called after the text has been removed from the Content.
1008     * This will happen within a write lock. If a subclass
1009     * of this class reimplements this method, it should delegate to the
1010     * superclass as well.
1011     *
1012     * @param chng a description of the change
1013     */

1014    protected void postRemoveUpdate(DefaultDocumentEvent chng) {
1015        if( getProperty(I18NProperty).equals( Boolean.TRUE ) )
1016            updateBidi( chng );
1017    }
1018
1019    
1020    /**
1021     * Update the bidi element structure as a result of the given change
1022     * to the document. The given change will be updated to reflect the
1023     * changes made to the bidi structure.
1024     *
1025     * This method assumes that every offset in the model is contained in
1026     * exactly one paragraph. This method also assumes that it is called
1027     * after the change is made to the default element structure.
1028     */

1029    void updateBidi( DefaultDocumentEvent chng ) {
1030
1031        // Calculate the range of paragraphs affected by the change.
1032
int firstPStart;
1033        int lastPEnd;
1034        if( chng.type == DocumentEvent.EventType.INSERT
1035            || chng.type == DocumentEvent.EventType.CHANGE )
1036        {
1037            int chngStart = chng.getOffset();
1038            int chngEnd = chngStart + chng.getLength();
1039            firstPStart = getParagraphElement(chngStart).getStartOffset();
1040            lastPEnd = getParagraphElement(chngEnd).getEndOffset();
1041        } else if( chng.type == DocumentEvent.EventType.REMOVE ) {
1042            Element JavaDoc paragraph = getParagraphElement( chng.getOffset() );
1043            firstPStart = paragraph.getStartOffset();
1044            lastPEnd = paragraph.getEndOffset();
1045        } else {
1046            throw new Error JavaDoc("Internal error: unknown event type.");
1047        }
1048        //System.out.println("updateBidi: firstPStart = " + firstPStart + " lastPEnd = " + lastPEnd );
1049

1050        
1051        // Calculate the bidi levels for the affected range of paragraphs. The
1052
// levels array will contain a bidi level for each character in the
1053
// affected text.
1054
byte levels[] = calculateBidiLevels( firstPStart, lastPEnd );
1055
1056        
1057        Vector newElements = new Vector();
1058        
1059        // Calculate the first span of characters in the affected range with
1060
// the same bidi level. If this level is the same as the level of the
1061
// previous bidi element (the existing bidi element containing
1062
// firstPStart-1), then merge in the previous element. If not, but
1063
// the previous element overlaps the affected range, truncate the
1064
// previous element at firstPStart.
1065
int firstSpanStart = firstPStart;
1066        int removeFromIndex = 0;
1067        if( firstSpanStart > 0 ) {
1068            int prevElemIndex = bidiRoot.getElementIndex(firstPStart-1);
1069            removeFromIndex = prevElemIndex;
1070            Element JavaDoc prevElem = bidiRoot.getElement(prevElemIndex);
1071            int prevLevel=StyleConstants.getBidiLevel(prevElem.getAttributes());
1072            //System.out.println("createbidiElements: prevElem= " + prevElem + " prevLevel= " + prevLevel + "level[0] = " + levels[0]);
1073
if( prevLevel==levels[0] ) {
1074                firstSpanStart = prevElem.getStartOffset();
1075            } else if( prevElem.getEndOffset() > firstPStart ) {
1076                newElements.addElement(new BidiElement(bidiRoot,
1077                                                       prevElem.getStartOffset(),
1078                                                       firstPStart, prevLevel));
1079            } else {
1080                removeFromIndex++;
1081            }
1082        }
1083        
1084        int firstSpanEnd = 0;
1085        while((firstSpanEnd<levels.length) && (levels[firstSpanEnd]==levels[0]))
1086            firstSpanEnd++;
1087
1088
1089        // Calculate the last span of characters in the affected range with
1090
// the same bidi level. If this level is the same as the level of the
1091
// next bidi element (the existing bidi element containing lastPEnd),
1092
// then merge in the next element. If not, but the next element
1093
// overlaps the affected range, adjust the next element to start at
1094
// lastPEnd.
1095
int lastSpanEnd = lastPEnd;
1096        Element JavaDoc newNextElem = null;
1097        int removeToIndex = bidiRoot.getElementCount() - 1;
1098        if( lastSpanEnd <= getLength() ) {
1099            int nextElemIndex = bidiRoot.getElementIndex( lastPEnd );
1100            removeToIndex = nextElemIndex;
1101            Element JavaDoc nextElem = bidiRoot.getElement( nextElemIndex );
1102            int nextLevel = StyleConstants.getBidiLevel(nextElem.getAttributes());
1103            if( nextLevel == levels[levels.length-1] ) {
1104                lastSpanEnd = nextElem.getEndOffset();
1105            } else if( nextElem.getStartOffset() < lastPEnd ) {
1106                newNextElem = new BidiElement(bidiRoot, lastPEnd,
1107                                              nextElem.getEndOffset(),
1108                                              nextLevel);
1109            } else {
1110                removeToIndex--;
1111            }
1112        }
1113        
1114        int lastSpanStart = levels.length;
1115        while( (lastSpanStart>firstSpanEnd)
1116               && (levels[lastSpanStart-1]==levels[levels.length-1]) )
1117            lastSpanStart--;
1118
1119
1120        // If the first and last spans are contiguous and have the same level,
1121
// merge them and create a single new element for the entire span.
1122
// Otherwise, create elements for the first and last spans as well as
1123
// any spans in between.
1124
if((firstSpanEnd==lastSpanStart)&&(levels[0]==levels[levels.length-1])){
1125            newElements.addElement(new BidiElement(bidiRoot, firstSpanStart,
1126                                                   lastSpanEnd, levels[0]));
1127        } else {
1128            // Create an element for the first span.
1129
newElements.addElement(new BidiElement(bidiRoot, firstSpanStart,
1130                                                   firstSpanEnd+firstPStart,
1131                                                   levels[0]));
1132            // Create elements for the spans in between the first and last
1133
for( int i=firstSpanEnd; i<lastSpanStart; ) {
1134                //System.out.println("executed line 872");
1135
int j;
1136                for( j=i; (j<levels.length) && (levels[j] == levels[i]); j++ );
1137                newElements.addElement(new BidiElement(bidiRoot, firstPStart+i,
1138                                                       firstPStart+j,
1139                                                       (int)levels[i]));
1140                i=j;
1141            }
1142            // Create an element for the last span.
1143
newElements.addElement(new BidiElement(bidiRoot,
1144                                                   lastSpanStart+firstPStart,
1145                                                   lastSpanEnd,
1146                                                   levels[levels.length-1]));
1147        }
1148        
1149        if( newNextElem != null )
1150            newElements.addElement( newNextElem );
1151
1152        
1153        // Calculate the set of existing bidi elements which must be
1154
// removed.
1155
int removedElemCount = 0;
1156        if( bidiRoot.getElementCount() > 0 ) {
1157            removedElemCount = removeToIndex - removeFromIndex + 1;
1158        }
1159        Element JavaDoc[] removedElems = new Element JavaDoc[removedElemCount];
1160        for( int i=0; i<removedElemCount; i++ ) {
1161            removedElems[i] = bidiRoot.getElement(removeFromIndex+i);
1162        }
1163
1164        Element JavaDoc[] addedElems = new Element JavaDoc[ newElements.size() ];
1165        newElements.copyInto( addedElems );
1166        
1167        // Update the change record.
1168
ElementEdit ee = new ElementEdit( bidiRoot, removeFromIndex,
1169                                          removedElems, addedElems );
1170        chng.addEdit( ee );
1171
1172        // Update the bidi element structure.
1173
bidiRoot.replace( removeFromIndex, removedElems.length, addedElems );
1174    }
1175
1176    
1177    /**
1178     * Calculate the levels array for a range of paragraphs.
1179     */

1180    private byte[] calculateBidiLevels( int firstPStart, int lastPEnd ) {
1181        
1182        byte levels[] = new byte[ lastPEnd - firstPStart ];
1183        int levelsEnd = 0;
1184    Boolean JavaDoc defaultDirection = null;
1185    Object JavaDoc d = getProperty(TextAttribute.RUN_DIRECTION);
1186    if (d instanceof Boolean JavaDoc) {
1187        defaultDirection = (Boolean JavaDoc) d;
1188    }
1189
1190        // For each paragraph in the given range of paragraphs, get its
1191
// levels array and add it to the levels array for the entire span.
1192
for(int o=firstPStart; o<lastPEnd; ) {
1193            Element JavaDoc p = getParagraphElement( o );
1194            int pStart = p.getStartOffset();
1195            int pEnd = p.getEndOffset();
1196
1197        // default run direction for the paragraph. This will be
1198
// null if there is no direction override specified (i.e.
1199
// the direction will be determined from the content).
1200
Boolean JavaDoc direction = defaultDirection;
1201        d = p.getAttributes().getAttribute(TextAttribute.RUN_DIRECTION);
1202        if (d instanceof Boolean JavaDoc) {
1203        direction = (Boolean JavaDoc) d;
1204        }
1205
1206            //System.out.println("updateBidi: paragraph start = " + pStart + " paragraph end = " + pEnd);
1207

1208            // Create a Bidi over this paragraph then get the level
1209
// array.
1210
Segment JavaDoc seg = SegmentCache.getSharedSegment();
1211            try {
1212                getText(pStart, pEnd-pStart, seg);
1213            } catch (BadLocationException JavaDoc e ) {
1214                throw new Error JavaDoc("Internal error: " + e.toString());
1215            }
1216            // REMIND(bcb) we should really be using a Segment here.
1217
Bidi JavaDoc bidiAnalyzer;
1218        int bidiflag = Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT;
1219        if (direction != null) {
1220        if (TextAttribute.RUN_DIRECTION_LTR.equals(direction)) {
1221            bidiflag = Bidi.DIRECTION_LEFT_TO_RIGHT;
1222        } else {
1223            bidiflag = Bidi.DIRECTION_RIGHT_TO_LEFT;
1224        }
1225        }
1226        bidiAnalyzer = new Bidi JavaDoc(seg.array, seg.offset, null, 0, seg.count,
1227                    bidiflag);
1228        BidiUtils.getLevels(bidiAnalyzer, levels, levelsEnd);
1229        levelsEnd += bidiAnalyzer.getLength();
1230
1231            o = p.getEndOffset();
1232            SegmentCache.releaseSharedSegment(seg);
1233        }
1234
1235        // REMIND(bcb) remove this code when debugging is done.
1236
if( levelsEnd != levels.length )
1237            throw new Error JavaDoc("levelsEnd assertion failed.");
1238
1239        return levels;
1240    }
1241
1242    /**
1243     * Gives a diagnostic dump.
1244     *
1245     * @param out the output stream
1246     */

1247    public void dump(PrintStream out) {
1248    Element JavaDoc root = getDefaultRootElement();
1249    if (root instanceof AbstractElement) {
1250        ((AbstractElement)root).dump(out, 0);
1251    }
1252        bidiRoot.dump(out,0);
1253    }
1254
1255    /**
1256     * Gets the content for the document.
1257     *
1258     * @return the content
1259     */

1260    protected final Content getContent() {
1261    return data;
1262    }
1263
1264    /**
1265     * Creates a document leaf element.
1266     * Hook through which elements are created to represent the
1267     * document structure. Because this implementation keeps
1268     * structure and content separate, elements grow automatically
1269     * when content is extended so splits of existing elements
1270     * follow. The document itself gets to decide how to generate
1271     * elements to give flexibility in the type of elements used.
1272     *
1273     * @param parent the parent element
1274     * @param a the attributes for the element
1275     * @param p0 the beginning of the range >= 0
1276     * @param p1 the end of the range >= p0
1277     * @return the new element
1278     */

1279    protected Element JavaDoc createLeafElement(Element JavaDoc parent, AttributeSet JavaDoc a, int p0, int p1) {
1280    return new LeafElement(parent, a, p0, p1);
1281    }
1282
1283    /**
1284     * Creates a document branch element, that can contain other elements.
1285     *
1286     * @param parent the parent element
1287     * @param a the attributes
1288     * @return the element
1289     */

1290    protected Element JavaDoc createBranchElement(Element JavaDoc parent, AttributeSet JavaDoc a) {
1291    return new BranchElement(parent, a);
1292    }
1293
1294    // --- Document locking ----------------------------------
1295

1296    /**
1297     * Fetches the current writing thread if there is one.
1298     * This can be used to distinguish whether a method is
1299     * being called as part of an existing modification or
1300     * if a lock needs to be acquired and a new transaction
1301     * started.
1302     *
1303     * @return the thread actively modifying the document
1304     * or <code>null</code> if there are no modifications in progress
1305     */

1306    protected synchronized final Thread JavaDoc getCurrentWriter() {
1307    return currWriter;
1308    }
1309
1310    /**
1311     * Acquires a lock to begin mutating the document this lock
1312     * protects. There can be no writing, notification of changes, or
1313     * reading going on in order to gain the lock. Additionally a thread is
1314     * allowed to gain more than one <code>writeLock</code>,
1315     * as long as it doesn't attempt to gain additional <code>writeLock</code>s
1316     * from within document notification. Attempting to gain a
1317     * <code>writeLock</code> from within a DocumentListener notification will
1318     * result in an <code>IllegalStateException</code>. The ability
1319     * to obtain more than one <code>writeLock</code> per thread allows
1320     * subclasses to gain a writeLock, perform a number of operations, then
1321     * release the lock.
1322     * <p>
1323     * Calls to <code>writeLock</code>
1324     * must be balanced with calls to <code>writeUnlock</code>, else the
1325     * <code>Document</code> will be left in a locked state so that no
1326     * reading or writing can be done.
1327     *
1328     * @exception IllegalStateException thrown on illegal lock
1329     * attempt. If the document is implemented properly, this can
1330     * only happen if a document listener attempts to mutate the
1331     * document. This situation violates the bean event model
1332     * where order of delivery is not guaranteed and all listeners
1333     * should be notified before further mutations are allowed.
1334     */

1335    protected synchronized final void writeLock() {
1336    try {
1337        while ((numReaders > 0) || (currWriter != null)) {
1338        if (Thread.currentThread() == currWriter) {
1339                    if (notifyingListeners) {
1340                        // Assuming one doesn't do something wrong in a
1341
// subclass this should only happen if a
1342
// DocumentListener tries to mutate the document.
1343
throw new IllegalStateException JavaDoc(
1344                                      "Attempt to mutate in notification");
1345                    }
1346                    numWriters++;
1347                    return;
1348                }
1349        wait();
1350        }
1351        currWriter = Thread.currentThread();
1352            numWriters = 1;
1353    } catch (InterruptedException JavaDoc e) {
1354        throw new Error JavaDoc("Interrupted attempt to aquire write lock");
1355    }
1356    }
1357
1358    /**
1359     * Releases a write lock previously obtained via <code>writeLock</code>.
1360     * After decrementing the lock count if there are no oustanding locks
1361     * this will allow a new writer, or readers.
1362     *
1363     * @see #writeLock
1364     */

1365    protected synchronized final void writeUnlock() {
1366        if (--numWriters <= 0) {
1367            numWriters = 0;
1368            currWriter = null;
1369            notifyAll();
1370        }
1371    }
1372
1373    /**
1374     * Acquires a lock to begin reading some state from the
1375     * document. There can be multiple readers at the same time.
1376     * Writing blocks the readers until notification of the change
1377     * to the listeners has been completed. This method should
1378     * be used very carefully to avoid unintended compromise
1379     * of the document. It should always be balanced with a
1380     * <code>readUnlock</code>.
1381     *
1382     * @see #readUnlock
1383     */

1384    public synchronized final void readLock() {
1385    try {
1386        while (currWriter != null) {
1387        if (currWriter == Thread.currentThread()) {
1388            // writer has full read access.... may try to acquire
1389
// lock in notification
1390
return;
1391        }
1392        wait();
1393        }
1394        numReaders += 1;
1395    } catch (InterruptedException JavaDoc e) {
1396        throw new Error JavaDoc("Interrupted attempt to aquire read lock");
1397    }
1398    }
1399
1400    /**
1401     * Does a read unlock. This signals that one
1402     * of the readers is done. If there are no more readers
1403     * then writing can begin again. This should be balanced
1404     * with a readLock, and should occur in a finally statement
1405     * so that the balance is guaranteed. The following is an
1406     * example.
1407     * <pre><code>
1408     * &nbsp; readLock();
1409     * &nbsp; try {
1410     * &nbsp; // do something
1411     * &nbsp; } finally {
1412     * &nbsp; readUnlock();
1413     * &nbsp; }
1414     * </code></pre>
1415     *
1416     * @see #readLock
1417     */

1418    public synchronized final void readUnlock() {
1419    if (currWriter == Thread.currentThread()) {
1420        // writer has full read access.... may try to acquire
1421
// lock in notification
1422
return;
1423    }
1424    if (numReaders <= 0) {
1425        throw new StateInvariantError JavaDoc(BAD_LOCK_STATE);
1426    }
1427    numReaders -= 1;
1428    notify();
1429    }
1430
1431    // --- serialization ---------------------------------------------
1432

1433    private void readObject(ObjectInputStream s)
1434      throws ClassNotFoundException JavaDoc, IOException
1435    {
1436    s.defaultReadObject();
1437    listenerList = new EventListenerList();
1438
1439        // Restore bidi structure
1440
//REMIND(bcb) This creates an initial bidi element to account for
1441
//the \n that exists by default in the content.
1442
bidiRoot = new BidiRootElement();
1443        try {
1444            writeLock();
1445            Element JavaDoc[] p = new Element JavaDoc[1];
1446            p[0] = new BidiElement( bidiRoot, 0, 1, 0 );
1447            bidiRoot.replace(0,0,p);
1448        } finally {
1449            writeUnlock();
1450        }
1451    // At this point bidi root is only partially correct. To fully
1452
// restore it we need access to getDefaultRootElement. But, this
1453
// is created by the subclass and at this point will be null. We
1454
// thus use registerValidation.
1455
s.registerValidation(new ObjectInputValidation() {
1456        public void validateObject() {
1457        try {
1458            writeLock();
1459            DefaultDocumentEvent e = new DefaultDocumentEvent
1460                       (0, getLength(),
1461                    DocumentEvent.EventType.INSERT);
1462            updateBidi( e );
1463        }
1464        finally {
1465            writeUnlock();
1466        }
1467        }
1468    }, 0);
1469    }
1470
1471    // ----- member variables ------------------------------------------
1472

1473    private transient int numReaders;
1474    private transient Thread JavaDoc currWriter;
1475    /**
1476     * The number of writers, all obtained from <code>currWriter</code>.
1477     */

1478    private transient int numWriters;
1479    /**
1480     * True will notifying listeners.
1481     */

1482    private transient boolean notifyingListeners;
1483
1484    private static Boolean JavaDoc defaultI18NProperty;
1485
1486    /**
1487     * Storage for document-wide properties.
1488     */

1489    private Dictionary<Object JavaDoc,Object JavaDoc> documentProperties = null;
1490
1491    /**
1492     * The event listener list for the document.
1493     */

1494    protected EventListenerList listenerList = new EventListenerList();
1495
1496    /**
1497     * Where the text is actually stored, and a set of marks
1498     * that track change as the document is edited are managed.
1499     */

1500    private Content data;
1501
1502    /**
1503     * Factory for the attributes. This is the strategy for
1504     * attribute compression and control of the lifetime of
1505     * a set of attributes as a collection. This may be shared
1506     * with other documents.
1507     */

1508    private AttributeContext context;
1509
1510    /**
1511     * The root of the bidirectional structure for this document. Its children
1512     * represent character runs with the same Unicode bidi level.
1513     */

1514    private transient BranchElement bidiRoot;
1515    
1516    /**
1517     * Filter for inserting/removing of text.
1518     */

1519    private DocumentFilter JavaDoc documentFilter;
1520
1521    /**
1522     * Used by DocumentFilter to do actual insert/remove.
1523     */

1524    private transient DocumentFilter.FilterBypass JavaDoc filterBypass;
1525
1526    private static final String JavaDoc BAD_LOCK_STATE = "document lock failure";
1527
1528    /**
1529     * Error message to indicate a bad location.
1530     */

1531    protected static final String JavaDoc BAD_LOCATION = "document location failure";
1532
1533    /**
1534     * Name of elements used to represent paragraphs
1535     */

1536    public static final String JavaDoc ParagraphElementName = "paragraph";
1537
1538    /**
1539     * Name of elements used to represent content
1540     */

1541    public static final String JavaDoc ContentElementName = "content";
1542
1543    /**
1544     * Name of elements used to hold sections (lines/paragraphs).
1545     */

1546    public static final String JavaDoc SectionElementName = "section";
1547
1548    /**
1549     * Name of elements used to hold a unidirectional run
1550     */

1551    public static final String JavaDoc BidiElementName = "bidi level";
1552
1553    /**
1554     * Name of the attribute used to specify element
1555     * names.
1556     */

1557    public static final String JavaDoc ElementNameAttribute = "$ename";
1558
1559    /**
1560     * Document property that indicates whether internationalization
1561     * functions such as text reordering or reshaping should be
1562     * performed. This property should not be publicly exposed,
1563     * since it is used for implementation convenience only. As a
1564     * side effect, copies of this property may be in its subclasses
1565     * that live in different packages (e.g. HTMLDocument as of now),
1566     * so those copies should also be taken care of when this property
1567     * needs to be modified.
1568     */

1569    static final String JavaDoc I18NProperty = "i18n";
1570
1571    /**
1572     * Document property that indicates if a character has been inserted
1573     * into the document that is more than one byte long. GlyphView uses
1574     * this to determine if it should use BreakIterator.
1575     */

1576    static final Object JavaDoc MultiByteProperty = "multiByte";
1577
1578    /**
1579     * Document property that indicates asynchronous loading is
1580     * desired, with the thread priority given as the value.
1581     */

1582    static final String JavaDoc AsyncLoadPriority = "load priority";
1583
1584    /**
1585     * Interface to describe a sequence of character content that
1586     * can be edited. Implementations may or may not support a
1587     * history mechanism which will be reflected by whether or not
1588     * mutations return an UndoableEdit implementation.
1589     * @see AbstractDocument
1590     */

1591    public interface Content {
1592
1593    /**
1594     * Creates a position within the content that will
1595     * track change as the content is mutated.
1596         *
1597         * @param offset the offset in the content >= 0
1598         * @return a Position
1599         * @exception BadLocationException for an invalid offset
1600     */

1601    public Position JavaDoc createPosition(int offset) throws BadLocationException JavaDoc;
1602
1603    /**
1604     * Current length of the sequence of character content.
1605         *
1606         * @return the length >= 0
1607     */

1608        public int length();
1609
1610    /**
1611     * Inserts a string of characters into the sequence.
1612     *
1613     * @param where offset into the sequence to make the insertion >= 0
1614     * @param str string to insert
1615     * @return if the implementation supports a history mechanism,
1616     * a reference to an <code>Edit</code> implementation will be returned,
1617     * otherwise returns <code>null</code>
1618     * @exception BadLocationException thrown if the area covered by
1619     * the arguments is not contained in the character sequence
1620     */

1621        public UndoableEdit insertString(int where, String JavaDoc str) throws BadLocationException JavaDoc;
1622
1623    /**
1624     * Removes some portion of the sequence.
1625     *
1626     * @param where The offset into the sequence to make the
1627         * insertion >= 0.
1628     * @param nitems The number of items in the sequence to remove >= 0.
1629     * @return If the implementation supports a history mechansim,
1630     * a reference to an Edit implementation will be returned,
1631     * otherwise null.
1632     * @exception BadLocationException Thrown if the area covered by
1633     * the arguments is not contained in the character sequence.
1634     */

1635        public UndoableEdit remove(int where, int nitems) throws BadLocationException JavaDoc;
1636
1637    /**
1638     * Fetches a string of characters contained in the sequence.
1639     *
1640     * @param where Offset into the sequence to fetch >= 0.
1641     * @param len number of characters to copy >= 0.
1642         * @return the string
1643     * @exception BadLocationException Thrown if the area covered by
1644     * the arguments is not contained in the character sequence.
1645     */

1646        public String JavaDoc getString(int where, int len) throws BadLocationException JavaDoc;
1647
1648        /**
1649         * Gets a sequence of characters and copies them into a Segment.
1650         *
1651         * @param where the starting offset >= 0
1652         * @param len the number of characters >= 0
1653         * @param txt the target location to copy into
1654     * @exception BadLocationException Thrown if the area covered by
1655     * the arguments is not contained in the character sequence.
1656         */

1657        public void getChars(int where, int len, Segment JavaDoc txt) throws BadLocationException JavaDoc;
1658    }
1659
1660    /**
1661     * An interface that can be used to allow MutableAttributeSet
1662     * implementations to use pluggable attribute compression
1663     * techniques. Each mutation of the attribute set can be
1664     * used to exchange a previous AttributeSet instance with
1665     * another, preserving the possibility of the AttributeSet
1666     * remaining immutable. An implementation is provided by
1667     * the StyleContext class.
1668     *
1669     * The Element implementations provided by this class use
1670     * this interface to provide their MutableAttributeSet
1671     * implementations, so that different AttributeSet compression
1672     * techniques can be employed. The method
1673     * <code>getAttributeContext</code> should be implemented to
1674     * return the object responsible for implementing the desired
1675     * compression technique.
1676     *
1677     * @see StyleContext
1678     */

1679    public interface AttributeContext {
1680
1681    /**
1682     * Adds an attribute to the given set, and returns
1683     * the new representative set.
1684     *
1685         * @param old the old attribute set
1686     * @param name the non-null attribute name
1687     * @param value the attribute value
1688         * @return the updated attribute set
1689     * @see MutableAttributeSet#addAttribute
1690     */

1691        public AttributeSet JavaDoc addAttribute(AttributeSet JavaDoc old, Object JavaDoc name, Object JavaDoc value);
1692
1693    /**
1694     * Adds a set of attributes to the element.
1695     *
1696         * @param old the old attribute set
1697     * @param attr the attributes to add
1698         * @return the updated attribute set
1699     * @see MutableAttributeSet#addAttribute
1700     */

1701        public AttributeSet JavaDoc addAttributes(AttributeSet JavaDoc old, AttributeSet JavaDoc attr);
1702
1703    /**
1704     * Removes an attribute from the set.
1705     *
1706         * @param old the old attribute set
1707     * @param name the non-null attribute name
1708         * @return the updated attribute set
1709     * @see MutableAttributeSet#removeAttribute
1710     */

1711        public AttributeSet JavaDoc removeAttribute(AttributeSet JavaDoc old, Object JavaDoc name);
1712
1713    /**
1714     * Removes a set of attributes for the element.
1715     *
1716         * @param old the old attribute set
1717     * @param names the attribute names
1718         * @return the updated attribute set
1719     * @see MutableAttributeSet#removeAttributes
1720     */

1721        public AttributeSet JavaDoc removeAttributes(AttributeSet JavaDoc old, Enumeration<?> names);
1722
1723    /**
1724     * Removes a set of attributes for the element.
1725     *
1726         * @param old the old attribute set
1727     * @param attrs the attributes
1728         * @return the updated attribute set
1729     * @see MutableAttributeSet#removeAttributes
1730     */

1731        public AttributeSet JavaDoc removeAttributes(AttributeSet JavaDoc old, AttributeSet JavaDoc attrs);
1732
1733    /**
1734     * Fetches an empty AttributeSet.
1735         *
1736         * @return the attribute set
1737     */

1738    public AttributeSet JavaDoc getEmptySet();
1739
1740    /**
1741         * Reclaims an attribute set.
1742     * This is a way for a MutableAttributeSet to mark that it no
1743     * longer need a particular immutable set. This is only necessary
1744     * in 1.1 where there are no weak references. A 1.1 implementation
1745     * would call this in its finalize method.
1746         *
1747         * @param a the attribute set to reclaim
1748     */

1749    public void reclaim(AttributeSet JavaDoc a);
1750    }
1751
1752    /**
1753     * Implements the abstract part of an element. By default elements
1754     * support attributes by having a field that represents the immutable
1755     * part of the current attribute set for the element. The element itself
1756     * implements MutableAttributeSet which can be used to modify the set
1757     * by fetching a new immutable set. The immutable sets are provided
1758     * by the AttributeContext associated with the document.
1759     * <p>
1760     * <strong>Warning:</strong>
1761     * Serialized objects of this class will not be compatible with
1762     * future Swing releases. The current serialization support is
1763     * appropriate for short term storage or RMI between applications running
1764     * the same version of Swing. As of 1.4, support for long term storage
1765     * of all JavaBeans<sup><font size="-2">TM</font></sup>
1766     * has been added to the <code>java.beans</code> package.
1767     * Please see {@link java.beans.XMLEncoder}.
1768     */

1769    public abstract class AbstractElement implements Element JavaDoc, MutableAttributeSet JavaDoc, Serializable, TreeNode JavaDoc {
1770
1771        /**
1772         * Creates a new AbstractElement.
1773         *
1774         * @param parent the parent element
1775         * @param a the attributes for the element
1776         */

1777        public AbstractElement(Element JavaDoc parent, AttributeSet JavaDoc a) {
1778        this.parent = parent;
1779        attributes = getAttributeContext().getEmptySet();
1780        if (a != null) {
1781        addAttributes(a);
1782        }
1783    }
1784
1785    private final void indent(PrintWriter out, int n) {
1786        for (int i = 0; i < n; i++) {
1787        out.print(" ");
1788        }
1789    }
1790
1791    /**
1792     * Dumps a debugging representation of the element hierarchy.
1793         *
1794         * @param psOut the output stream
1795         * @param indentAmount the indentation level >= 0
1796     */

1797    public void dump(PrintStream psOut, int indentAmount) {
1798            PrintWriter out;
1799            try {
1800                out = new PrintWriter(new OutputStreamWriter(psOut,"JavaEsc"),
1801                                      true);
1802            } catch (UnsupportedEncodingException e){
1803                out = new PrintWriter(psOut,true);
1804            }
1805        indent(out, indentAmount);
1806        if (getName() == null) {
1807        out.print("<??");
1808        } else {
1809        out.print("<" + getName());
1810        }
1811        if (getAttributeCount() > 0) {
1812        out.println("");
1813        // dump the attributes
1814
Enumeration names = attributes.getAttributeNames();
1815        while (names.hasMoreElements()) {
1816            Object JavaDoc name = names.nextElement();
1817            indent(out, indentAmount + 1);
1818            out.println(name + "=" + getAttribute(name));
1819        }
1820        indent(out, indentAmount);
1821        }
1822        out.println(">");
1823
1824        if (isLeaf()) {
1825        indent(out, indentAmount+1);
1826        out.print("[" + getStartOffset() + "," + getEndOffset() + "]");
1827        Content c = getContent();
1828        try {
1829            String JavaDoc contentStr = c.getString(getStartOffset(),
1830                                                    getEndOffset() - getStartOffset())/*.trim()*/;
1831            if (contentStr.length() > 40) {
1832            contentStr = contentStr.substring(0, 40) + "...";
1833            }
1834            out.println("["+contentStr+"]");
1835            } catch (BadLocationException JavaDoc e) {
1836            ;
1837        }
1838
1839        } else {
1840        int n = getElementCount();
1841        for (int i = 0; i < n; i++) {
1842            AbstractElement e = (AbstractElement) getElement(i);
1843            e.dump(psOut, indentAmount+1);
1844        }
1845        }
1846    }
1847
1848    // --- AttributeSet ----------------------------
1849
// delegated to the immutable field "attributes"
1850

1851    /**
1852         * Gets the number of attributes that are defined.
1853         *
1854         * @return the number of attributes >= 0
1855     * @see AttributeSet#getAttributeCount
1856     */

1857        public int getAttributeCount() {
1858        return attributes.getAttributeCount();
1859    }
1860
1861    /**
1862         * Checks whether a given attribute is defined.
1863         *
1864         * @param attrName the non-null attribute name
1865         * @return true if the attribute is defined
1866     * @see AttributeSet#isDefined
1867     */

1868        public boolean isDefined(Object JavaDoc attrName) {
1869        return attributes.isDefined(attrName);
1870    }
1871
1872    /**
1873         * Checks whether two attribute sets are equal.
1874         *
1875         * @param attr the attribute set to check against
1876         * @return true if the same
1877     * @see AttributeSet#isEqual
1878     */

1879        public boolean isEqual(AttributeSet JavaDoc attr) {
1880        return attributes.isEqual(attr);
1881    }
1882
1883    /**
1884         * Copies a set of attributes.
1885         *
1886         * @return the copy
1887     * @see AttributeSet#copyAttributes
1888     */

1889        public AttributeSet JavaDoc copyAttributes() {
1890        return attributes.copyAttributes();
1891    }
1892
1893    /**
1894         * Gets the value of an attribute.
1895         *
1896         * @param attrName the non-null attribute name
1897         * @return the attribute value
1898     * @see AttributeSet#getAttribute
1899     */

1900        public Object JavaDoc getAttribute(Object JavaDoc attrName) {
1901        Object JavaDoc value = attributes.getAttribute(attrName);
1902        if (value == null) {
1903        // The delegate nor it's resolvers had a match,
1904
// so we'll try to resolve through the parent
1905
// element.
1906
AttributeSet JavaDoc a = (parent != null) ? parent.getAttributes() : null;
1907        if (a != null) {
1908            value = a.getAttribute(attrName);
1909        }
1910        }
1911        return value;
1912    }
1913
1914    /**
1915         * Gets the names of all attributes.
1916         *
1917         * @return the attribute names as an enumeration
1918     * @see AttributeSet#getAttributeNames
1919     */

1920        public Enumeration<?> getAttributeNames() {
1921        return attributes.getAttributeNames();
1922    }
1923
1924    /**
1925         * Checks whether a given attribute name/value is defined.
1926         *
1927         * @param name the non-null attribute name
1928         * @param value the attribute value
1929         * @return true if the name/value is defined
1930     * @see AttributeSet#containsAttribute
1931     */

1932        public boolean containsAttribute(Object JavaDoc name, Object JavaDoc value) {
1933        return attributes.containsAttribute(name, value);
1934    }
1935
1936
1937    /**
1938         * Checks whether the element contains all the attributes.
1939         *
1940         * @param attrs the attributes to check
1941         * @return true if the element contains all the attributes
1942     * @see AttributeSet#containsAttributes
1943     */

1944        public boolean containsAttributes(AttributeSet JavaDoc attrs) {
1945        return attributes.containsAttributes(attrs);
1946    }
1947
1948    /**
1949         * Gets the resolving parent.
1950     * If not overridden, the resolving parent defaults to
1951     * the parent element.
1952         *
1953         * @return the attributes from the parent, <code>null</code> if none
1954     * @see AttributeSet#getResolveParent
1955     */

1956        public AttributeSet JavaDoc getResolveParent() {
1957        AttributeSet JavaDoc a = attributes.getResolveParent();
1958        if ((a == null) && (parent != null)) {
1959        a = parent.getAttributes();
1960        }
1961        return a;
1962    }
1963
1964    // --- MutableAttributeSet ----------------------------------
1965
// should fetch a new immutable record for the field
1966
// "attributes".
1967

1968    /**
1969         * Adds an attribute to the element.
1970         *
1971         * @param name the non-null attribute name
1972         * @param value the attribute value
1973     * @see MutableAttributeSet#addAttribute
1974     */

1975        public void addAttribute(Object JavaDoc name, Object JavaDoc value) {
1976        checkForIllegalCast();
1977        AttributeContext context = getAttributeContext();
1978        attributes = context.addAttribute(attributes, name, value);
1979    }
1980
1981    /**
1982         * Adds a set of attributes to the element.
1983         *
1984         * @param attr the attributes to add
1985     * @see MutableAttributeSet#addAttribute
1986     */

1987        public void addAttributes(AttributeSet JavaDoc attr) {
1988        checkForIllegalCast();
1989        AttributeContext context = getAttributeContext();
1990        attributes = context.addAttributes(attributes, attr);
1991    }
1992
1993    /**
1994         * Removes an attribute from the set.
1995         *
1996         * @param name the non-null attribute name
1997     * @see MutableAttributeSet#removeAttribute
1998     */

1999        public void removeAttribute(Object JavaDoc name) {
2000        checkForIllegalCast();
2001        AttributeContext context = getAttributeContext();
2002        attributes = context.removeAttribute(attributes, name);
2003    }
2004
2005    /**
2006         * Removes a set of attributes for the element.
2007         *
2008         * @param names the attribute names
2009     * @see MutableAttributeSet#removeAttributes
2010     */

2011        public void removeAttributes(Enumeration<?> names) {
2012        checkForIllegalCast();
2013        AttributeContext context = getAttributeContext();
2014        attributes = context.removeAttributes(attributes, names);
2015    }
2016
2017    /**
2018         * Removes a set of attributes for the element.
2019         *
2020         * @param attrs the attributes
2021     * @see MutableAttributeSet#removeAttributes
2022     */

2023        public void removeAttributes(AttributeSet JavaDoc attrs) {
2024        checkForIllegalCast();
2025        AttributeContext context = getAttributeContext();
2026        if (attrs == this) {
2027        attributes = context.getEmptySet();
2028        } else {
2029        attributes = context.removeAttributes(attributes, attrs);
2030        }
2031    }
2032
2033    /**
2034         * Sets the resolving parent.
2035         *
2036         * @param parent the parent, null if none
2037     * @see MutableAttributeSet#setResolveParent
2038     */

2039        public void setResolveParent(AttributeSet JavaDoc parent) {
2040        checkForIllegalCast();
2041        AttributeContext context = getAttributeContext();
2042        if (parent != null) {
2043        attributes =
2044            context.addAttribute(attributes, StyleConstants.ResolveAttribute,
2045                     parent);
2046        } else {
2047        attributes =
2048            context.removeAttribute(attributes, StyleConstants.ResolveAttribute);
2049        }
2050    }
2051
2052    private final void checkForIllegalCast() {
2053        Thread JavaDoc t = getCurrentWriter();
2054        if ((t == null) || (t != Thread.currentThread())) {
2055        throw new StateInvariantError JavaDoc("Illegal cast to MutableAttributeSet");
2056        }
2057    }
2058
2059        // --- Element methods -------------------------------------
2060

2061        /**
2062         * Retrieves the underlying model.
2063         *
2064         * @return the model
2065         */

2066    public Document JavaDoc getDocument() {
2067        return AbstractDocument.this;
2068    }
2069
2070        /**
2071         * Gets the parent of the element.
2072         *
2073         * @return the parent
2074         */

2075    public Element JavaDoc getParentElement() {
2076        return parent;
2077    }
2078
2079        /**
2080         * Gets the attributes for the element.
2081         *
2082         * @return the attribute set
2083         */

2084    public AttributeSet JavaDoc getAttributes() {
2085        return this;
2086    }
2087
2088        /**
2089         * Gets the name of the element.
2090         *
2091         * @return the name, null if none
2092         */

2093        public String JavaDoc getName() {
2094        if (attributes.isDefined(ElementNameAttribute)) {
2095        return (String JavaDoc) attributes.getAttribute(ElementNameAttribute);
2096        }
2097        return null;
2098    }
2099
2100        /**
2101         * Gets the starting offset in the model for the element.
2102         *
2103         * @return the offset >= 0
2104         */

2105    public abstract int getStartOffset();
2106
2107        /**
2108         * Gets the ending offset in the model for the element.
2109         *
2110         * @return the offset >= 0
2111         */

2112    public abstract int getEndOffset();
2113    
2114        /**
2115         * Gets a child element.
2116         *
2117         * @param index the child index, >= 0 && < getElementCount()
2118         * @return the child element
2119         */

2120    public abstract Element JavaDoc getElement(int index);
2121
2122        /**
2123         * Gets the number of children for the element.
2124         *
2125         * @return the number of children >= 0
2126         */

2127    public abstract int getElementCount();
2128
2129        /**
2130         * Gets the child element index closest to the given model offset.
2131         *
2132         * @param offset the offset >= 0
2133         * @return the element index >= 0
2134         */

2135    public abstract int getElementIndex(int offset);
2136
2137        /**
2138         * Checks whether the element is a leaf.
2139         *
2140         * @return true if a leaf
2141         */

2142    public abstract boolean isLeaf();
2143
2144        // --- TreeNode methods -------------------------------------
2145

2146    /**
2147     * Returns the child <code>TreeNode</code> at index
2148     * <code>childIndex</code>.
2149     */

2150    public TreeNode JavaDoc getChildAt(int childIndex) {
2151        return (TreeNode JavaDoc)getElement(childIndex);
2152    }
2153
2154    /**
2155     * Returns the number of children <code>TreeNode</code>'s
2156         * receiver contains.
2157         * @return the number of children <code>TreeNodews</code>'s
2158         * receiver contains
2159     */

2160    public int getChildCount() {
2161        return getElementCount();
2162    }
2163
2164    /**
2165     * Returns the parent <code>TreeNode</code> of the receiver.
2166         * @return the parent <code>TreeNode</code> of the receiver
2167     */

2168    public TreeNode JavaDoc getParent() {
2169        return (TreeNode JavaDoc)getParentElement();
2170    }
2171
2172    /**
2173     * Returns the index of <code>node</code> in the receivers children.
2174     * If the receiver does not contain <code>node</code>, -1 will be
2175     * returned.
2176         * @param node the location of interest
2177         * @return the index of <code>node</code> in the receiver's
2178         * children, or -1 if absent
2179     */

2180    public int getIndex(TreeNode JavaDoc node) {
2181        for(int counter = getChildCount() - 1; counter >= 0; counter--)
2182        if(getChildAt(counter) == node)
2183            return counter;
2184        return -1;
2185    }
2186
2187    /**
2188     * Returns true if the receiver allows children.
2189         * @return true if the receiver allows children, otherwise false
2190     */

2191    public abstract boolean getAllowsChildren();
2192
2193
2194    /**
2195     * Returns the children of the receiver as an
2196         * <code>Enumeration</code>.
2197         * @return the children of the receiver as an <code>Enumeration</code>
2198     */

2199    public abstract Enumeration children();
2200
2201
2202    // --- serialization ---------------------------------------------
2203

2204        private void writeObject(ObjectOutputStream s) throws IOException {
2205        s.defaultWriteObject();
2206        StyleContext.writeAttributeSet(s, attributes);
2207    }
2208
2209        private void readObject(ObjectInputStream s)
2210            throws ClassNotFoundException JavaDoc, IOException
2211        {
2212        s.defaultReadObject();
2213        MutableAttributeSet JavaDoc attr = new SimpleAttributeSet JavaDoc();
2214        StyleContext.readAttributeSet(s, attr);
2215        AttributeContext context = getAttributeContext();
2216        attributes = context.addAttributes(SimpleAttributeSet.EMPTY, attr);
2217    }
2218
2219    // ---- variables -----------------------------------------------------
2220

2221    private Element JavaDoc parent;
2222    private transient AttributeSet JavaDoc attributes;
2223
2224    }
2225
2226    /**
2227     * Implements a composite element that contains other elements.
2228     * <p>
2229     * <strong>Warning:</strong>
2230     * Serialized objects of this class will not be compatible with
2231     * future Swing releases. The current serialization support is
2232     * appropriate for short term storage or RMI between applications running
2233     * the same version of Swing. As of 1.4, support for long term storage
2234     * of all JavaBeans<sup><font size="-2">TM</font></sup>
2235     * has been added to the <code>java.beans</code> package.
2236     * Please see {@link java.beans.XMLEncoder}.
2237     */

2238    public class BranchElement extends AbstractElement {
2239
2240    /**
2241     * Constructs a composite element that initially contains
2242     * no children.
2243     *
2244     * @param parent The parent element
2245         * @param a the attributes for the element
2246     */

2247    public BranchElement(Element JavaDoc parent, AttributeSet JavaDoc a) {
2248        super(parent, a);
2249        children = new AbstractElement[1];
2250        nchildren = 0;
2251        lastIndex = -1;
2252    }
2253
2254    /**
2255     * Gets the child element that contains
2256     * the given model position.
2257         *
2258         * @param pos the position >= 0
2259         * @return the element, null if none
2260     */

2261    public Element JavaDoc positionToElement(int pos) {
2262        int index = getElementIndex(pos);
2263        Element JavaDoc child = children[index];
2264        int p0 = child.getStartOffset();
2265        int p1 = child.getEndOffset();
2266        if ((pos >= p0) && (pos < p1)) {
2267        return child;
2268        }
2269        return null;
2270    }
2271
2272        /**
2273         * Replaces content with a new set of elements.
2274         *
2275         * @param offset the starting offset >= 0
2276         * @param length the length to replace >= 0
2277         * @param elems the new elements
2278         */

2279        public void replace(int offset, int length, Element JavaDoc[] elems) {
2280        int delta = elems.length - length;
2281        int src = offset + length;
2282        int nmove = nchildren - src;
2283        int dest = src + delta;
2284        if ((nchildren + delta) >= children.length) {
2285        // need to grow the array
2286
int newLength = Math.max(2*children.length, nchildren + delta);
2287        AbstractElement[] newChildren = new AbstractElement[newLength];
2288        System.arraycopy(children, 0, newChildren, 0, offset);
2289        System.arraycopy(elems, 0, newChildren, offset, elems.length);
2290        System.arraycopy(children, src, newChildren, dest, nmove);
2291        children = newChildren;
2292        } else {
2293        // patch the existing array
2294
System.arraycopy(children, src, children, dest, nmove);
2295        System.arraycopy(elems, 0, children, offset, elems.length);
2296        }
2297        nchildren = nchildren + delta;
2298    }
2299
2300        /**
2301         * Converts the element to a string.
2302         *
2303         * @return the string
2304         */

2305    public String JavaDoc toString() {
2306        return "BranchElement(" + getName() + ") " + getStartOffset() + "," +
2307        getEndOffset() + "\n";
2308    }
2309
2310    // --- Element methods -----------------------------------
2311

2312        /**
2313         * Gets the element name.
2314         *
2315         * @return the element name
2316         */

2317    public String JavaDoc getName() {
2318        String JavaDoc nm = super.getName();
2319        if (nm == null) {
2320        nm = ParagraphElementName;
2321        }
2322        return nm;
2323    }
2324
2325        /**
2326         * Gets the starting offset in the model for the element.
2327         *
2328         * @return the offset >= 0
2329         */

2330        public int getStartOffset() {
2331        return children[0].getStartOffset();
2332    }
2333
2334        /**
2335         * Gets the ending offset in the model for the element.
2336         * @throws NullPointerException if this element has no children
2337         *
2338         * @return the offset >= 0
2339         */

2340        public int getEndOffset() {
2341        Element JavaDoc child =
2342        (nchildren > 0) ? children[nchildren - 1] : children[0];
2343        return child.getEndOffset();
2344    }
2345    
2346        /**
2347         * Gets a child element.
2348         *
2349         * @param index the child index, >= 0 && < getElementCount()
2350         * @return the child element, null if none
2351         */

2352    public Element JavaDoc getElement(int index) {
2353        if (index < nchildren) {
2354        return children[index];
2355        }
2356        return null;
2357    }
2358
2359        /**
2360         * Gets the number of children for the element.
2361         *
2362         * @return the number of children >= 0
2363         */

2364    public int getElementCount() {
2365        return nchildren;
2366    }
2367
2368        /**
2369         * Gets the child element index closest to the given model offset.
2370         *
2371         * @param offset the offset >= 0
2372         * @return the element index >= 0
2373         */

2374    public int getElementIndex(int offset) {
2375        int index;
2376        int lower = 0;
2377        int upper = nchildren - 1;
2378        int mid = 0;
2379        int p0 = getStartOffset();
2380        int p1;
2381
2382        if (nchildren == 0) {
2383        return 0;
2384        }
2385        if (offset >= getEndOffset()) {
2386        return nchildren - 1;
2387        }
2388
2389        // see if the last index can be used.
2390
if ((lastIndex >= lower) && (lastIndex <= upper)) {
2391        Element JavaDoc lastHit = children[lastIndex];
2392        p0 = lastHit.getStartOffset();
2393        p1 = lastHit.getEndOffset();
2394        if ((offset >= p0) && (offset < p1)) {
2395            return lastIndex;
2396        }
2397
2398        // last index wasn't a hit, but it does give useful info about
2399
// where a hit (if any) would be.
2400
if (offset < p0) {
2401            upper = lastIndex;
2402        } else {
2403            lower = lastIndex;
2404        }
2405        }
2406
2407        while (lower <= upper) {
2408        mid = lower + ((upper - lower) / 2);
2409        Element JavaDoc elem = children[mid];
2410        p0 = elem.getStartOffset();
2411        p1 = elem.getEndOffset();
2412        if ((offset >= p0) && (offset < p1)) {
2413            // found the location
2414
index = mid;
2415            lastIndex = index;
2416            return index;
2417        } else if (offset < p0) {
2418            upper = mid - 1;
2419        } else {
2420            lower = mid + 1;
2421        }
2422        }
2423
2424        // didn't find it, but we indicate the index of where it would belong
2425
if (offset < p0) {
2426        index = mid;
2427        } else {
2428        index = mid + 1;
2429        }
2430        lastIndex = index;
2431        return index;
2432    }
2433
2434        /**
2435         * Checks whether the element is a leaf.
2436         *
2437         * @return true if a leaf
2438         */

2439    public boolean isLeaf() {
2440        return false;
2441    }
2442
2443
2444    // ------ TreeNode ----------------------------------------------
2445

2446    /**
2447     * Returns true if the receiver allows children.
2448         * @return true if the receiver allows children, otherwise false
2449     */

2450    public boolean getAllowsChildren() {
2451        return true;
2452    }
2453
2454
2455    /**
2456     * Returns the children of the receiver as an
2457         * <code>Enumeration</code>.
2458         * @return the children of the receiver
2459     */

2460    public Enumeration children() {
2461        if(nchildren == 0)
2462        return null;
2463
2464        Vector tempVector = new Vector(nchildren);
2465
2466        for(int counter = 0; counter < nchildren; counter++)
2467        tempVector.addElement(children[counter]);
2468        return tempVector.elements();
2469    }
2470
2471    // ------ members ----------------------------------------------
2472

2473    private AbstractElement[] children;
2474    private int nchildren;
2475    private int lastIndex;
2476    }
2477    
2478    /**
2479     * Implements an element that directly represents content of
2480     * some kind.
2481     * <p>
2482     * <strong>Warning:</strong>
2483     * Serialized objects of this class will not be compatible with
2484     * future Swing releases. The current serialization support is
2485     * appropriate for short term storage or RMI between applications running
2486     * the same version of Swing. As of 1.4, support for long term storage
2487     * of all JavaBeans<sup><font size="-2">TM</font></sup>
2488     * has been added to the <code>java.beans</code> package.
2489     * Please see {@link java.beans.XMLEncoder}.
2490     *
2491     * @see Element
2492     */

2493    public class LeafElement extends AbstractElement {
2494
2495    /**
2496     * Constructs an element that represents content within the
2497     * document (has no children).
2498     *
2499     * @param parent The parent element
2500     * @param a The element attributes
2501     * @param offs0 The start offset >= 0
2502     * @param offs1 The end offset >= offs0
2503     */

2504    public LeafElement(Element JavaDoc parent, AttributeSet JavaDoc a, int offs0, int offs1) {
2505        super(parent, a);
2506        try {
2507        p0 = createPosition(offs0);
2508        p1 = createPosition(offs1);
2509        } catch (BadLocationException JavaDoc e) {
2510        p0 = null;
2511        p1 = null;
2512        throw new StateInvariantError JavaDoc("Can't create Position references");
2513        }
2514    }
2515
2516        /**
2517         * Converts the element to a string.
2518         *
2519         * @return the string
2520         */

2521    public String JavaDoc toString() {
2522        return "LeafElement(" + getName() + ") " + p0 + "," + p1 + "\n";
2523    }
2524
2525    // --- Element methods ---------------------------------------------
2526

2527        /**
2528         * Gets the starting offset in the model for the element.
2529         *
2530         * @return the offset >= 0
2531         */

2532    public int getStartOffset() {
2533        return p0.getOffset();
2534    }
2535
2536        /**
2537         * Gets the ending offset in the model for the element.
2538         *
2539         * @return the offset >= 0
2540         */

2541    public int getEndOffset() {
2542        return p1.getOffset();
2543    }
2544
2545        /**
2546         * Gets the element name.
2547         *
2548         * @return the name
2549         */

2550    public String JavaDoc getName() {
2551        String JavaDoc nm = super.getName();
2552        if (nm == null) {
2553        nm = ContentElementName;
2554        }
2555        return nm;
2556    }
2557
2558        /**
2559         * Gets the child element index closest to the given model offset.
2560         *
2561         * @param pos the offset >= 0
2562         * @return the element index >= 0
2563         */

2564    public int getElementIndex(int pos) {
2565        return -1;
2566    }
2567
2568        /**
2569         * Gets a child element.
2570         *
2571         * @param index the child index, >= 0 && < getElementCount()
2572         * @return the child element
2573         */

2574    public Element JavaDoc getElement(int index) {
2575        return null;
2576    }
2577
2578        /**
2579         * Returns the number of child elements.
2580         *
2581         * @return the number of children >= 0
2582         */

2583    public int getElementCount() {
2584        return 0;
2585    }
2586
2587        /**
2588         * Checks whether the element is a leaf.
2589         *
2590         * @return true if a leaf
2591         */

2592    public boolean isLeaf() {
2593        return true;
2594    }
2595
2596    // ------ TreeNode ----------------------------------------------
2597

2598    /**
2599     * Returns true if the receiver allows children.
2600         * @return true if the receiver allows children, otherwise false
2601     */

2602    public boolean getAllowsChildren() {
2603        return false;
2604    }
2605
2606
2607    /**
2608     * Returns the children of the receiver as an
2609         * <code>Enumeration</code>.
2610         * @return the children of the receiver
2611     */

2612    public Enumeration children() {
2613        return null;
2614    }
2615
2616    // --- serialization ---------------------------------------------
2617

2618        private void writeObject(ObjectOutputStream s) throws IOException {
2619        s.defaultWriteObject();
2620        s.writeInt(p0.getOffset());
2621        s.writeInt(p1.getOffset());
2622    }
2623
2624        private void readObject(ObjectInputStream s)
2625            throws ClassNotFoundException JavaDoc, IOException
2626        {
2627        s.defaultReadObject();
2628
2629        // set the range with positions that track change
2630
int off0 = s.readInt();
2631        int off1 = s.readInt();
2632        try {
2633        p0 = createPosition(off0);
2634        p1 = createPosition(off1);
2635        } catch (BadLocationException JavaDoc e) {
2636        p0 = null;
2637        p1 = null;
2638        throw new IOException("Can't restore Position references");
2639        }
2640    }
2641
2642    // ---- members -----------------------------------------------------
2643

2644    private transient Position JavaDoc p0;
2645    private transient Position JavaDoc p1;
2646    }
2647
2648    /**
2649     * Represents the root element of the bidirectional element structure.
2650     * The root element is the only element in the bidi element structure
2651     * which contains children.
2652     */

2653    class BidiRootElement extends BranchElement {
2654
2655    BidiRootElement() {
2656            super( null, null );
2657    }
2658
2659        /**
2660         * Gets the name of the element.
2661         * @return the name
2662         */

2663        public String JavaDoc getName() {
2664        return "bidi root";
2665    }
2666    }
2667
2668    /**
2669     * Represents an element of the bidirectional element structure.
2670     */

2671    class BidiElement extends LeafElement {
2672
2673        /**
2674         * Creates a new BidiElement.
2675         */

2676    BidiElement(Element JavaDoc parent, int start, int end, int level) {
2677        super(parent, new SimpleAttributeSet JavaDoc(), start, end);
2678            addAttribute(StyleConstants.BidiLevel, new Integer JavaDoc(level));
2679            //System.out.println("BidiElement: start = " + start
2680
// + " end = " + end + " level = " + level );
2681
}
2682
2683        /**
2684         * Gets the name of the element.
2685         * @return the name
2686         */

2687        public String JavaDoc getName() {
2688        return BidiElementName;
2689    }
2690
2691        int getLevel() {
2692            Integer JavaDoc o = (Integer JavaDoc) getAttribute(StyleConstants.BidiLevel);
2693            if (o != null) {
2694                return o.intValue();
2695            }
2696            return 0; // Level 0 is base level (non-embedded) left-to-right
2697
}
2698
2699        boolean isLeftToRight() {
2700            return ((getLevel() % 2) == 0);
2701        }
2702    }
2703
2704    /**
2705     * Stores document changes as the document is being
2706     * modified. Can subsequently be used for change notification
2707     * when done with the document modification transaction.
2708     * This is used by the AbstractDocument class and its extensions
2709     * for broadcasting change information to the document listeners.
2710     */

2711    public class DefaultDocumentEvent extends CompoundEdit implements DocumentEvent {
2712
2713    /**
2714     * Constructs a change record.
2715     *
2716     * @param offs the offset into the document of the change >= 0
2717     * @param len the length of the change >= 0
2718     * @param type the type of event (DocumentEvent.EventType)
2719     */

2720        public DefaultDocumentEvent(int offs, int len, DocumentEvent.EventType type) {
2721        super();
2722        offset = offs;
2723        length = len;
2724        this.type = type;
2725    }
2726
2727    /**
2728     * Returns a string description of the change event.
2729     *
2730     * @return a string
2731     */

2732        public String JavaDoc toString() {
2733        return edits.toString();
2734    }
2735    
2736    // --- CompoundEdit methods --------------------------
2737

2738    /**
2739     * Adds a document edit. If the number of edits crosses
2740     * a threshold, this switches on a hashtable lookup for
2741     * ElementChange implementations since access of these
2742     * needs to be relatively quick.
2743     *
2744     * @param anEdit a document edit record
2745     * @return true if the edit was added
2746     */

2747        public boolean addEdit(UndoableEdit anEdit) {
2748        // if the number of changes gets too great, start using
2749
// a hashtable for to locate the change for a given element.
2750
if ((changeLookup == null) && (edits.size() > 10)) {
2751        changeLookup = new Hashtable();
2752        int n = edits.size();
2753        for (int i = 0; i < n; i++) {
2754            Object JavaDoc o = edits.elementAt(i);
2755            if (o instanceof DocumentEvent.ElementChange) {
2756            DocumentEvent.ElementChange ec = (DocumentEvent.ElementChange) o;
2757            changeLookup.put(ec.getElement(), ec);
2758            }
2759        }
2760        }
2761
2762        // if we have a hashtable... add the entry if it's
2763
// an ElementChange.
2764
if ((changeLookup != null) && (anEdit instanceof DocumentEvent.ElementChange)) {
2765        DocumentEvent.ElementChange ec = (DocumentEvent.ElementChange) anEdit;
2766        changeLookup.put(ec.getElement(), ec);
2767        }
2768        return super.addEdit(anEdit);
2769    }
2770
2771    /**
2772     * Redoes a change.
2773     *
2774     * @exception CannotRedoException if the change cannot be redone
2775     */

2776        public void redo() throws CannotRedoException {
2777        writeLock();
2778        try {
2779        // change the state
2780
super.redo();
2781        // fire a DocumentEvent to notify the view(s)
2782
UndoRedoDocumentEvent ev = new UndoRedoDocumentEvent(this, false);
2783        if (type == DocumentEvent.EventType.INSERT) {
2784            fireInsertUpdate(ev);
2785        } else if (type == DocumentEvent.EventType.REMOVE) {
2786            fireRemoveUpdate(ev);
2787        } else {
2788            fireChangedUpdate(ev);
2789        }
2790        } finally {
2791        writeUnlock();
2792        }
2793    }
2794
2795    /**
2796     * Undoes a change.
2797     *
2798     * @exception CannotUndoException if the change cannot be undone
2799     */

2800        public void undo() throws CannotUndoException {
2801        writeLock();
2802        try {
2803        // change the state
2804
super.undo();
2805        // fire a DocumentEvent to notify the view(s)
2806
UndoRedoDocumentEvent ev = new UndoRedoDocumentEvent(this, true);
2807        if (type == DocumentEvent.EventType.REMOVE) {
2808            fireInsertUpdate(ev);
2809        } else if (type == DocumentEvent.EventType.INSERT) {
2810            fireRemoveUpdate(ev);
2811        } else {
2812            fireChangedUpdate(ev);
2813        }
2814        } finally {
2815        writeUnlock();
2816        }
2817    }
2818
2819    /**
2820     * DefaultDocument events are significant. If you wish to aggregate
2821     * DefaultDocumentEvents to present them as a single edit to the user
2822     * place them into a CompoundEdit.
2823         *
2824         * @return whether the event is significant for edit undo purposes
2825     */

2826    public boolean isSignificant() {
2827        return true;
2828    }
2829
2830
2831    /**
2832     * Provides a localized, human readable description of this edit
2833     * suitable for use in, say, a change log.
2834         *
2835         * @return the description
2836     */

2837    public String JavaDoc getPresentationName() {
2838        DocumentEvent.EventType type = getType();
2839        if(type == DocumentEvent.EventType.INSERT)
2840        return UIManager.getString("AbstractDocument.additionText");
2841        if(type == DocumentEvent.EventType.REMOVE)
2842        return UIManager.getString("AbstractDocument.deletionText");
2843        return UIManager.getString("AbstractDocument.styleChangeText");
2844    }
2845
2846    /**
2847     * Provides a localized, human readable description of the undoable
2848     * form of this edit, e.g. for use as an Undo menu item. Typically
2849     * derived from getDescription();
2850         *
2851         * @return the description
2852     */

2853    public String JavaDoc getUndoPresentationName() {
2854        return UIManager.getString("AbstractDocument.undoText") + " " +
2855                getPresentationName();
2856    }
2857
2858    /**
2859     * Provides a localized, human readable description of the redoable
2860     * form of this edit, e.g. for use as a Redo menu item. Typically
2861     * derived from getPresentationName();
2862         *
2863         * @return the description
2864     */

2865    public String JavaDoc getRedoPresentationName() {
2866        return UIManager.getString("AbstractDocument.redoText") + " " +
2867                getPresentationName();
2868    }
2869
2870    // --- DocumentEvent methods --------------------------
2871

2872    /**
2873     * Returns the type of event.
2874         *
2875         * @return the event type as a DocumentEvent.EventType
2876     * @see DocumentEvent#getType
2877     */

2878    public DocumentEvent.EventType getType() {
2879        return type;
2880    }
2881
2882    /**
2883     * Returns the offset within the document of the start of the change.
2884     *
2885     * @return the offset >= 0
2886     * @see DocumentEvent#getOffset
2887     */

2888        public int getOffset() {
2889        return offset;
2890    }
2891
2892    /**
2893     * Returns the length of the change.
2894     *
2895     * @return the length >= 0
2896     * @see DocumentEvent#getLength
2897     */

2898        public int getLength() {
2899        return length;
2900    }
2901    
2902    /**
2903     * Gets the document that sourced the change event.
2904     *
2905     * @return the document
2906     * @see DocumentEvent#getDocument
2907     */

2908        public Document JavaDoc getDocument() {
2909        return AbstractDocument.this;
2910    }
2911
2912    /**
2913     * Gets the changes for an element.
2914     *
2915     * @param elem the element
2916     * @return the changes
2917     */

2918        public DocumentEvent.ElementChange getChange(Element JavaDoc elem) {
2919        if (changeLookup != null) {
2920        return (DocumentEvent.ElementChange) changeLookup.get(elem);
2921        }
2922        int n = edits.size();
2923        for (int i = 0; i < n; i++) {
2924        Object JavaDoc o = edits.elementAt(i);
2925        if (o instanceof DocumentEvent.ElementChange) {
2926            DocumentEvent.ElementChange c = (DocumentEvent.ElementChange) o;
2927            if (elem.equals(c.getElement())) {
2928            return c;
2929            }
2930        }
2931        }
2932        return null;
2933    }
2934
2935    // --- member variables ------------------------------------
2936

2937        private int offset;
2938        private int length;
2939        private Hashtable changeLookup;
2940    private DocumentEvent.EventType type;
2941
2942    }
2943
2944    /**
2945     * This event used when firing document changes while Undo/Redo
2946     * operations. It just wraps DefaultDocumentEvent and delegates
2947     * all calls to it except getType() which depends on operation
2948     * (Undo or Redo).
2949     */

2950    class UndoRedoDocumentEvent implements DocumentEvent {
2951        private DefaultDocumentEvent src = null;
2952        private boolean isUndo;
2953        private EventType type = null;
2954
2955        public UndoRedoDocumentEvent(DefaultDocumentEvent src, boolean isUndo) {
2956            this.src = src;
2957            this.isUndo = isUndo;
2958            if(isUndo) {
2959                if(src.getType().equals(EventType.INSERT)) {
2960                    type = EventType.REMOVE;
2961                } else if(src.getType().equals(EventType.REMOVE)) {
2962                    type = EventType.INSERT;
2963                } else {
2964                    type = src.getType();
2965                }
2966            } else {
2967                type = src.getType();
2968            }
2969        }
2970
2971        public DefaultDocumentEvent getSource() {
2972            return src;
2973        }
2974
2975        // DocumentEvent methods delegated to DefaultDocumentEvent source
2976
// except getType() which depends on operation (Undo or Redo).
2977
public int getOffset() {
2978            return src.getOffset();
2979        }
2980
2981        public int getLength() {
2982            return src.getLength();
2983        }
2984
2985        public Document JavaDoc getDocument() {
2986            return src.getDocument();
2987        }
2988
2989        public DocumentEvent.EventType getType() {
2990            return type;
2991        }
2992
2993        public DocumentEvent.ElementChange getChange(Element JavaDoc elem) {
2994            return src.getChange(elem);
2995        }
2996    }
2997
2998    /**
2999     * An implementation of ElementChange that can be added to the document
3000     * event.
3001     */

3002    public static class ElementEdit extends AbstractUndoableEdit implements DocumentEvent.ElementChange {
3003
3004    /**
3005     * Constructs an edit record. This does not modify the element
3006     * so it can safely be used to <em>catch up</em> a view to the
3007     * current model state for views that just attached to a model.
3008     *
3009     * @param e the element
3010     * @param index the index into the model >= 0
3011     * @param removed a set of elements that were removed
3012     * @param added a set of elements that were added
3013     */

3014        public ElementEdit(Element JavaDoc e, int index, Element JavaDoc[] removed, Element JavaDoc[] added) {
3015        super();
3016        this.e = e;
3017        this.index = index;
3018        this.removed = removed;
3019        this.added = added;
3020    }
3021        
3022    /**
3023     * Returns the underlying element.
3024     *
3025     * @return the element
3026     */

3027        public Element JavaDoc getElement() {
3028        return e;
3029    }
3030
3031    /**
3032     * Returns the index into the list of elements.
3033     *
3034     * @return the index >= 0
3035     */

3036        public int getIndex() {
3037        return index;
3038    }
3039
3040    /**
3041     * Gets a list of children that were removed.
3042     *
3043     * @return the list
3044     */

3045        public Element JavaDoc[] getChildrenRemoved() {
3046        return removed;
3047    }
3048
3049    /**
3050     * Gets a list of children that were added.
3051     *
3052     * @return the list
3053     */

3054        public Element JavaDoc[] getChildrenAdded() {
3055        return added;
3056    }
3057
3058    /**
3059     * Redoes a change.
3060     *
3061     * @exception CannotRedoException if the change cannot be redone
3062     */

3063        public void redo() throws CannotRedoException {
3064        super.redo();
3065
3066        // Since this event will be reused, switch around added/removed.
3067
Element JavaDoc[] tmp = removed;
3068        removed = added;
3069        added = tmp;
3070
3071        // PENDING(prinz) need MutableElement interface, canRedo() should check
3072
((AbstractDocument.BranchElement JavaDoc)e).replace(index, removed.length, added);
3073    }
3074
3075    /**
3076     * Undoes a change.
3077     *
3078     * @exception CannotUndoException if the change cannot be undone
3079     */

3080        public void undo() throws CannotUndoException {
3081        super.undo();
3082        // PENDING(prinz) need MutableElement interface, canUndo() should check
3083
((AbstractDocument.BranchElement JavaDoc)e).replace(index, added.length, removed);
3084
3085        // Since this event will be reused, switch around added/removed.
3086
Element JavaDoc[] tmp = removed;
3087        removed = added;
3088        added = tmp;
3089    }
3090
3091        private Element JavaDoc e;
3092    private int index;
3093    private Element JavaDoc[] removed;
3094    private Element JavaDoc[] added;
3095    }
3096
3097
3098    private class DefaultFilterBypass extends DocumentFilter.FilterBypass JavaDoc {
3099        public Document JavaDoc getDocument() {
3100            return AbstractDocument.this;
3101        }
3102
3103        public void remove(int offset, int length) throws
3104            BadLocationException JavaDoc {
3105            handleRemove(offset, length);
3106        }
3107
3108        public void insertString(int offset, String JavaDoc string,
3109                                 AttributeSet JavaDoc attr) throws
3110                                        BadLocationException JavaDoc {
3111            handleInsertString(offset, string, attr);
3112        }
3113
3114        public void replace(int offset, int length, String JavaDoc text,
3115                            AttributeSet JavaDoc attrs) throws BadLocationException JavaDoc {
3116            handleRemove(offset, length);
3117            handleInsertString(offset, text, attrs);
3118        }
3119    }
3120}
3121
3122
3123
3124
3125
3126
3127    
3128
Popular Tags