View Javadoc
1   /*
2    * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  package javax.swing.text;
26  
27  import java.awt.Color;
28  import java.awt.Font;
29  import java.awt.font.TextAttribute;
30  import java.lang.ref.ReferenceQueue;
31  import java.lang.ref.WeakReference;
32  import java.util.Enumeration;
33  import java.util.HashMap;
34  import java.util.List;
35  import java.util.Map;
36  import java.util.Stack;
37  import java.util.Vector;
38  import java.util.ArrayList;
39  import java.io.IOException;
40  import java.io.ObjectInputStream;
41  import java.io.Serializable;
42  import javax.swing.event.*;
43  import javax.swing.undo.AbstractUndoableEdit;
44  import javax.swing.undo.CannotRedoException;
45  import javax.swing.undo.CannotUndoException;
46  import javax.swing.undo.UndoableEdit;
47  import javax.swing.SwingUtilities;
48  import static sun.swing.SwingUtilities2.IMPLIED_CR;
49  
50  /**
51   * A document that can be marked up with character and paragraph
52   * styles in a manner similar to the Rich Text Format.  The element
53   * structure for this document represents style crossings for
54   * style runs.  These style runs are mapped into a paragraph element
55   * structure (which may reside in some other structure).  The
56   * style runs break at paragraph boundaries since logical styles are
57   * assigned to paragraph boundaries.
58   * <p>
59   * <strong>Warning:</strong>
60   * Serialized objects of this class will not be compatible with
61   * future Swing releases. The current serialization support is
62   * appropriate for short term storage or RMI between applications running
63   * the same version of Swing.  As of 1.4, support for long term storage
64   * of all JavaBeans&trade;
65   * has been added to the <code>java.beans</code> package.
66   * Please see {@link java.beans.XMLEncoder}.
67   *
68   * @author  Timothy Prinzing
69   * @see     Document
70   * @see     AbstractDocument
71   */
72  public class DefaultStyledDocument extends AbstractDocument implements StyledDocument {
73  
74      /**
75       * Constructs a styled document.
76       *
77       * @param c  the container for the content
78       * @param styles resources and style definitions which may
79       *  be shared across documents
80       */
81      public DefaultStyledDocument(Content c, StyleContext styles) {
82          super(c, styles);
83          listeningStyles = new Vector<Style>();
84          buffer = new ElementBuffer(createDefaultRoot());
85          Style defaultStyle = styles.getStyle(StyleContext.DEFAULT_STYLE);
86          setLogicalStyle(0, defaultStyle);
87      }
88  
89      /**
90       * Constructs a styled document with the default content
91       * storage implementation and a shared set of styles.
92       *
93       * @param styles the styles
94       */
95      public DefaultStyledDocument(StyleContext styles) {
96          this(new GapContent(BUFFER_SIZE_DEFAULT), styles);
97      }
98  
99      /**
100      * Constructs a default styled document.  This buffers
101      * input content by a size of <em>BUFFER_SIZE_DEFAULT</em>
102      * and has a style context that is scoped by the lifetime
103      * of the document and is not shared with other documents.
104      */
105     public DefaultStyledDocument() {
106         this(new GapContent(BUFFER_SIZE_DEFAULT), new StyleContext());
107     }
108 
109     /**
110      * Gets the default root element.
111      *
112      * @return the root
113      * @see Document#getDefaultRootElement
114      */
115     public Element getDefaultRootElement() {
116         return buffer.getRootElement();
117     }
118 
119     /**
120      * Initialize the document to reflect the given element
121      * structure (i.e. the structure reported by the
122      * <code>getDefaultRootElement</code> method.  If the
123      * document contained any data it will first be removed.
124      */
125     protected void create(ElementSpec[] data) {
126         try {
127             if (getLength() != 0) {
128                 remove(0, getLength());
129             }
130             writeLock();
131 
132             // install the content
133             Content c = getContent();
134             int n = data.length;
135             StringBuilder sb = new StringBuilder();
136             for (int i = 0; i < n; i++) {
137                 ElementSpec es = data[i];
138                 if (es.getLength() > 0) {
139                     sb.append(es.getArray(), es.getOffset(),  es.getLength());
140                 }
141             }
142             UndoableEdit cEdit = c.insertString(0, sb.toString());
143 
144             // build the event and element structure
145             int length = sb.length();
146             DefaultDocumentEvent evnt =
147                 new DefaultDocumentEvent(0, length, DocumentEvent.EventType.INSERT);
148             evnt.addEdit(cEdit);
149             buffer.create(length, data, evnt);
150 
151             // update bidi (possibly)
152             super.insertUpdate(evnt, null);
153 
154             // notify the listeners
155             evnt.end();
156             fireInsertUpdate(evnt);
157             fireUndoableEditUpdate(new UndoableEditEvent(this, evnt));
158         } catch (BadLocationException ble) {
159             throw new StateInvariantError("problem initializing");
160         } finally {
161             writeUnlock();
162         }
163 
164     }
165 
166     /**
167      * Inserts new elements in bulk.  This is useful to allow
168      * parsing with the document in an unlocked state and
169      * prepare an element structure modification.  This method
170      * takes an array of tokens that describe how to update an
171      * element structure so the time within a write lock can
172      * be greatly reduced in an asynchronous update situation.
173      * <p>
174      * This method is thread safe, although most Swing methods
175      * are not. Please see
176      * <A HREF="http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html">Concurrency
177      * in Swing</A> for more information.
178      *
179      * @param offset the starting offset &gt;= 0
180      * @param data the element data
181      * @exception BadLocationException for an invalid starting offset
182      */
183     protected void insert(int offset, ElementSpec[] data) throws BadLocationException {
184         if (data == null || data.length == 0) {
185             return;
186         }
187 
188         try {
189             writeLock();
190 
191             // install the content
192             Content c = getContent();
193             int n = data.length;
194             StringBuilder sb = new StringBuilder();
195             for (int i = 0; i < n; i++) {
196                 ElementSpec es = data[i];
197                 if (es.getLength() > 0) {
198                     sb.append(es.getArray(), es.getOffset(),  es.getLength());
199                 }
200             }
201             if (sb.length() == 0) {
202                 // Nothing to insert, bail.
203                 return;
204             }
205             UndoableEdit cEdit = c.insertString(offset, sb.toString());
206 
207             // create event and build the element structure
208             int length = sb.length();
209             DefaultDocumentEvent evnt =
210                 new DefaultDocumentEvent(offset, length, DocumentEvent.EventType.INSERT);
211             evnt.addEdit(cEdit);
212             buffer.insert(offset, length, data, evnt);
213 
214             // update bidi (possibly)
215             super.insertUpdate(evnt, null);
216 
217             // notify the listeners
218             evnt.end();
219             fireInsertUpdate(evnt);
220             fireUndoableEditUpdate(new UndoableEditEvent(this, evnt));
221         } finally {
222             writeUnlock();
223         }
224     }
225 
226     /**
227      * Removes an element from this document.
228      *
229      * <p>The element is removed from its parent element, as well as
230      * the text in the range identified by the element.  If the
231      * element isn't associated with the document, {@code
232      * IllegalArgumentException} is thrown.</p>
233      *
234      * <p>As empty branch elements are not allowed in the document, if the
235      * element is the sole child, its parent element is removed as well,
236      * recursively.  This means that when replacing all the children of a
237      * particular element, new children should be added <em>before</em>
238      * removing old children.
239      *
240      * <p>Element removal results in two events being fired, the
241      * {@code DocumentEvent} for changes in element structure and {@code
242      * UndoableEditEvent} for changes in document content.</p>
243      *
244      * <p>If the element contains end-of-content mark (the last {@code
245      * "\n"} character in document), this character is not removed;
246      * instead, preceding leaf element is extended to cover the
247      * character.  If the last leaf already ends with {@code "\n",} it is
248      * included in content removal.</p>
249      *
250      * <p>If the element is {@code null,} {@code NullPointerException} is
251      * thrown.  If the element structure would become invalid after the removal,
252      * for example if the element is the document root element, {@code
253      * IllegalArgumentException} is thrown.  If the current element structure is
254      * invalid, {@code IllegalStateException} is thrown.</p>
255      *
256      * @param  elem                      the element to remove
257      * @throws NullPointerException      if the element is {@code null}
258      * @throws IllegalArgumentException  if the element could not be removed
259      * @throws IllegalStateException     if the element structure is invalid
260      *
261      * @since  1.7
262      */
263     public void removeElement(Element elem) {
264         try {
265             writeLock();
266             removeElementImpl(elem);
267         } finally {
268             writeUnlock();
269         }
270     }
271 
272     private void removeElementImpl(Element elem) {
273         if (elem.getDocument() != this) {
274             throw new IllegalArgumentException("element doesn't belong to document");
275         }
276         BranchElement parent = (BranchElement) elem.getParentElement();
277         if (parent == null) {
278             throw new IllegalArgumentException("can't remove the root element");
279         }
280 
281         int startOffset = elem.getStartOffset();
282         int removeFrom = startOffset;
283         int endOffset = elem.getEndOffset();
284         int removeTo = endOffset;
285         int lastEndOffset = getLength() + 1;
286         Content content = getContent();
287         boolean atEnd = false;
288         boolean isComposedText = Utilities.isComposedTextElement(elem);
289 
290         if (endOffset >= lastEndOffset) {
291             // element includes the last "\n" character, needs special handling
292             if (startOffset <= 0) {
293                 throw new IllegalArgumentException("can't remove the whole content");
294             }
295             removeTo = lastEndOffset - 1; // last "\n" must not be removed
296             try {
297                 if (content.getString(startOffset - 1, 1).charAt(0) == '\n') {
298                     removeFrom--; // preceding leaf ends with "\n", remove it
299                 }
300             } catch (BadLocationException ble) { // can't happen
301                 throw new IllegalStateException(ble);
302             }
303             atEnd = true;
304         }
305         int length = removeTo - removeFrom;
306 
307         DefaultDocumentEvent dde = new DefaultDocumentEvent(removeFrom,
308                 length, DefaultDocumentEvent.EventType.REMOVE);
309         UndoableEdit ue = null;
310         // do not leave empty branch elements
311         while (parent.getElementCount() == 1) {
312             elem = parent;
313             parent = (BranchElement) parent.getParentElement();
314             if (parent == null) { // shouldn't happen
315                 throw new IllegalStateException("invalid element structure");
316             }
317         }
318         Element[] removed = { elem };
319         Element[] added = {};
320         int index = parent.getElementIndex(startOffset);
321         parent.replace(index, 1, added);
322         dde.addEdit(new ElementEdit(parent, index, removed, added));
323         if (length > 0) {
324             try {
325                 ue = content.remove(removeFrom, length);
326                 if (ue != null) {
327                     dde.addEdit(ue);
328                 }
329             } catch (BadLocationException ble) {
330                 // can only happen if the element structure is severely broken
331                 throw new IllegalStateException(ble);
332             }
333             lastEndOffset -= length;
334         }
335 
336         if (atEnd) {
337             // preceding leaf element should be extended to cover orphaned "\n"
338             Element prevLeaf = parent.getElement(parent.getElementCount() - 1);
339             while ((prevLeaf != null) && !prevLeaf.isLeaf()) {
340                 prevLeaf = prevLeaf.getElement(prevLeaf.getElementCount() - 1);
341             }
342             if (prevLeaf == null) { // shouldn't happen
343                 throw new IllegalStateException("invalid element structure");
344             }
345             int prevStartOffset = prevLeaf.getStartOffset();
346             BranchElement prevParent = (BranchElement) prevLeaf.getParentElement();
347             int prevIndex = prevParent.getElementIndex(prevStartOffset);
348             Element newElem;
349             newElem = createLeafElement(prevParent, prevLeaf.getAttributes(),
350                                             prevStartOffset, lastEndOffset);
351             Element[] prevRemoved = { prevLeaf };
352             Element[] prevAdded = { newElem };
353             prevParent.replace(prevIndex, 1, prevAdded);
354             dde.addEdit(new ElementEdit(prevParent, prevIndex,
355                                                     prevRemoved, prevAdded));
356         }
357 
358         postRemoveUpdate(dde);
359         dde.end();
360         fireRemoveUpdate(dde);
361         if (! (isComposedText && (ue != null))) {
362             // do not fire UndoabeEdit event for composed text edit (unsupported)
363             fireUndoableEditUpdate(new UndoableEditEvent(this, dde));
364         }
365     }
366 
367     /**
368      * Adds a new style into the logical style hierarchy.  Style attributes
369      * resolve from bottom up so an attribute specified in a child
370      * will override an attribute specified in the parent.
371      *
372      * @param nm   the name of the style (must be unique within the
373      *   collection of named styles).  The name may be null if the style
374      *   is unnamed, but the caller is responsible
375      *   for managing the reference returned as an unnamed style can't
376      *   be fetched by name.  An unnamed style may be useful for things
377      *   like character attribute overrides such as found in a style
378      *   run.
379      * @param parent the parent style.  This may be null if unspecified
380      *   attributes need not be resolved in some other style.
381      * @return the style
382      */
383     public Style addStyle(String nm, Style parent) {
384         StyleContext styles = (StyleContext) getAttributeContext();
385         return styles.addStyle(nm, parent);
386     }
387 
388     /**
389      * Removes a named style previously added to the document.
390      *
391      * @param nm  the name of the style to remove
392      */
393     public void removeStyle(String nm) {
394         StyleContext styles = (StyleContext) getAttributeContext();
395         styles.removeStyle(nm);
396     }
397 
398     /**
399      * Fetches a named style previously added.
400      *
401      * @param nm  the name of the style
402      * @return the style
403      */
404     public Style getStyle(String nm) {
405         StyleContext styles = (StyleContext) getAttributeContext();
406         return styles.getStyle(nm);
407     }
408 
409 
410     /**
411      * Fetches the list of of style names.
412      *
413      * @return all the style names
414      */
415     public Enumeration<?> getStyleNames() {
416         return ((StyleContext) getAttributeContext()).getStyleNames();
417     }
418 
419     /**
420      * Sets the logical style to use for the paragraph at the
421      * given position.  If attributes aren't explicitly set
422      * for character and paragraph attributes they will resolve
423      * through the logical style assigned to the paragraph, which
424      * in turn may resolve through some hierarchy completely
425      * independent of the element hierarchy in the document.
426      * <p>
427      * This method is thread safe, although most Swing methods
428      * are not. Please see
429      * <A HREF="http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html">Concurrency
430      * in Swing</A> for more information.
431      *
432      * @param pos the offset from the start of the document &gt;= 0
433      * @param s  the logical style to assign to the paragraph, null if none
434      */
435     public void setLogicalStyle(int pos, Style s) {
436         Element paragraph = getParagraphElement(pos);
437         if ((paragraph != null) && (paragraph instanceof AbstractElement)) {
438             try {
439                 writeLock();
440                 StyleChangeUndoableEdit edit = new StyleChangeUndoableEdit((AbstractElement)paragraph, s);
441                 ((AbstractElement)paragraph).setResolveParent(s);
442                 int p0 = paragraph.getStartOffset();
443                 int p1 = paragraph.getEndOffset();
444                 DefaultDocumentEvent e =
445                   new DefaultDocumentEvent(p0, p1 - p0, DocumentEvent.EventType.CHANGE);
446                 e.addEdit(edit);
447                 e.end();
448                 fireChangedUpdate(e);
449                 fireUndoableEditUpdate(new UndoableEditEvent(this, e));
450             } finally {
451                 writeUnlock();
452             }
453         }
454     }
455 
456     /**
457      * Fetches the logical style assigned to the paragraph
458      * represented by the given position.
459      *
460      * @param p the location to translate to a paragraph
461      *  and determine the logical style assigned &gt;= 0.  This
462      *  is an offset from the start of the document.
463      * @return the style, null if none
464      */
465     public Style getLogicalStyle(int p) {
466         Style s = null;
467         Element paragraph = getParagraphElement(p);
468         if (paragraph != null) {
469             AttributeSet a = paragraph.getAttributes();
470             AttributeSet parent = a.getResolveParent();
471             if (parent instanceof Style) {
472                 s = (Style) parent;
473             }
474         }
475         return s;
476     }
477 
478     /**
479      * Sets attributes for some part of the document.
480      * A write lock is held by this operation while changes
481      * are being made, and a DocumentEvent is sent to the listeners
482      * after the change has been successfully completed.
483      * <p>
484      * This method is thread safe, although most Swing methods
485      * are not. Please see
486      * <A HREF="http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html">Concurrency
487      * in Swing</A> for more information.
488      *
489      * @param offset the offset in the document &gt;= 0
490      * @param length the length &gt;= 0
491      * @param s the attributes
492      * @param replace true if the previous attributes should be replaced
493      *  before setting the new attributes
494      */
495     public void setCharacterAttributes(int offset, int length, AttributeSet s, boolean replace) {
496         if (length == 0) {
497             return;
498         }
499         try {
500             writeLock();
501             DefaultDocumentEvent changes =
502                 new DefaultDocumentEvent(offset, length, DocumentEvent.EventType.CHANGE);
503 
504             // split elements that need it
505             buffer.change(offset, length, changes);
506 
507             AttributeSet sCopy = s.copyAttributes();
508 
509             // PENDING(prinz) - this isn't a very efficient way to iterate
510             int lastEnd;
511             for (int pos = offset; pos < (offset + length); pos = lastEnd) {
512                 Element run = getCharacterElement(pos);
513                 lastEnd = run.getEndOffset();
514                 if (pos == lastEnd) {
515                     // offset + length beyond length of document, bail.
516                     break;
517                 }
518                 MutableAttributeSet attr = (MutableAttributeSet) run.getAttributes();
519                 changes.addEdit(new AttributeUndoableEdit(run, sCopy, replace));
520                 if (replace) {
521                     attr.removeAttributes(attr);
522                 }
523                 attr.addAttributes(s);
524             }
525             changes.end();
526             fireChangedUpdate(changes);
527             fireUndoableEditUpdate(new UndoableEditEvent(this, changes));
528         } finally {
529             writeUnlock();
530         }
531 
532     }
533 
534     /**
535      * Sets attributes for a paragraph.
536      * <p>
537      * This method is thread safe, although most Swing methods
538      * are not. Please see
539      * <A HREF="http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html">Concurrency
540      * in Swing</A> for more information.
541      *
542      * @param offset the offset into the paragraph &gt;= 0
543      * @param length the number of characters affected &gt;= 0
544      * @param s the attributes
545      * @param replace whether to replace existing attributes, or merge them
546      */
547     public void setParagraphAttributes(int offset, int length, AttributeSet s,
548                                        boolean replace) {
549         try {
550             writeLock();
551             DefaultDocumentEvent changes =
552                 new DefaultDocumentEvent(offset, length, DocumentEvent.EventType.CHANGE);
553 
554             AttributeSet sCopy = s.copyAttributes();
555 
556             // PENDING(prinz) - this assumes a particular element structure
557             Element section = getDefaultRootElement();
558             int index0 = section.getElementIndex(offset);
559             int index1 = section.getElementIndex(offset + ((length > 0) ? length - 1 : 0));
560             boolean isI18N = Boolean.TRUE.equals(getProperty(I18NProperty));
561             boolean hasRuns = false;
562             for (int i = index0; i <= index1; i++) {
563                 Element paragraph = section.getElement(i);
564                 MutableAttributeSet attr = (MutableAttributeSet) paragraph.getAttributes();
565                 changes.addEdit(new AttributeUndoableEdit(paragraph, sCopy, replace));
566                 if (replace) {
567                     attr.removeAttributes(attr);
568                 }
569                 attr.addAttributes(s);
570                 if (isI18N && !hasRuns) {
571                     hasRuns = (attr.getAttribute(TextAttribute.RUN_DIRECTION) != null);
572                 }
573             }
574 
575             if (hasRuns) {
576                 updateBidi( changes );
577             }
578 
579             changes.end();
580             fireChangedUpdate(changes);
581             fireUndoableEditUpdate(new UndoableEditEvent(this, changes));
582         } finally {
583             writeUnlock();
584         }
585     }
586 
587     /**
588      * Gets the paragraph element at the offset <code>pos</code>.
589      * A paragraph consists of at least one child Element, which is usually
590      * a leaf.
591      *
592      * @param pos the starting offset &gt;= 0
593      * @return the element
594      */
595     public Element getParagraphElement(int pos) {
596         Element e;
597         for (e = getDefaultRootElement(); ! e.isLeaf(); ) {
598             int index = e.getElementIndex(pos);
599             e = e.getElement(index);
600         }
601         if(e != null)
602             return e.getParentElement();
603         return e;
604     }
605 
606     /**
607      * Gets a character element based on a position.
608      *
609      * @param pos the position in the document &gt;= 0
610      * @return the element
611      */
612     public Element getCharacterElement(int pos) {
613         Element e;
614         for (e = getDefaultRootElement(); ! e.isLeaf(); ) {
615             int index = e.getElementIndex(pos);
616             e = e.getElement(index);
617         }
618         return e;
619     }
620 
621     // --- local methods -------------------------------------------------
622 
623     /**
624      * Updates document structure as a result of text insertion.  This
625      * will happen within a write lock.  This implementation simply
626      * parses the inserted content for line breaks and builds up a set
627      * of instructions for the element buffer.
628      *
629      * @param chng a description of the document change
630      * @param attr the attributes
631      */
632     protected void insertUpdate(DefaultDocumentEvent chng, AttributeSet attr) {
633         int offset = chng.getOffset();
634         int length = chng.getLength();
635         if (attr == null) {
636             attr = SimpleAttributeSet.EMPTY;
637         }
638 
639         // Paragraph attributes should come from point after insertion.
640         // You really only notice this when inserting at a paragraph
641         // boundary.
642         Element paragraph = getParagraphElement(offset + length);
643         AttributeSet pattr = paragraph.getAttributes();
644         // Character attributes should come from actual insertion point.
645         Element pParagraph = getParagraphElement(offset);
646         Element run = pParagraph.getElement(pParagraph.getElementIndex
647                                             (offset));
648         int endOffset = offset + length;
649         boolean insertingAtBoundry = (run.getEndOffset() == endOffset);
650         AttributeSet cattr = run.getAttributes();
651 
652         try {
653             Segment s = new Segment();
654             Vector<ElementSpec> parseBuffer = new Vector<ElementSpec>();
655             ElementSpec lastStartSpec = null;
656             boolean insertingAfterNewline = false;
657             short lastStartDirection = ElementSpec.OriginateDirection;
658             // Check if the previous character was a newline.
659             if (offset > 0) {
660                 getText(offset - 1, 1, s);
661                 if (s.array[s.offset] == '\n') {
662                     // Inserting after a newline.
663                     insertingAfterNewline = true;
664                     lastStartDirection = createSpecsForInsertAfterNewline
665                                   (paragraph, pParagraph, pattr, parseBuffer,
666                                    offset, endOffset);
667                     for(int counter = parseBuffer.size() - 1; counter >= 0;
668                         counter--) {
669                         ElementSpec spec = parseBuffer.elementAt(counter);
670                         if(spec.getType() == ElementSpec.StartTagType) {
671                             lastStartSpec = spec;
672                             break;
673                         }
674                     }
675                 }
676             }
677             // If not inserting after a new line, pull the attributes for
678             // new paragraphs from the paragraph under the insertion point.
679             if(!insertingAfterNewline)
680                 pattr = pParagraph.getAttributes();
681 
682             getText(offset, length, s);
683             char[] txt = s.array;
684             int n = s.offset + s.count;
685             int lastOffset = s.offset;
686 
687             for (int i = s.offset; i < n; i++) {
688                 if (txt[i] == '\n') {
689                     int breakOffset = i + 1;
690                     parseBuffer.addElement(
691                         new ElementSpec(attr, ElementSpec.ContentType,
692                                                breakOffset - lastOffset));
693                     parseBuffer.addElement(
694                         new ElementSpec(null, ElementSpec.EndTagType));
695                     lastStartSpec = new ElementSpec(pattr, ElementSpec.
696                                                    StartTagType);
697                     parseBuffer.addElement(lastStartSpec);
698                     lastOffset = breakOffset;
699                 }
700             }
701             if (lastOffset < n) {
702                 parseBuffer.addElement(
703                     new ElementSpec(attr, ElementSpec.ContentType,
704                                            n - lastOffset));
705             }
706 
707             ElementSpec first = parseBuffer.firstElement();
708 
709             int docLength = getLength();
710 
711             // Check for join previous of first content.
712             if(first.getType() == ElementSpec.ContentType &&
713                cattr.isEqual(attr)) {
714                 first.setDirection(ElementSpec.JoinPreviousDirection);
715             }
716 
717             // Do a join fracture/next for last start spec if necessary.
718             if(lastStartSpec != null) {
719                 if(insertingAfterNewline) {
720                     lastStartSpec.setDirection(lastStartDirection);
721                 }
722                 // Join to the fracture if NOT inserting at the end
723                 // (fracture only happens when not inserting at end of
724                 // paragraph).
725                 else if(pParagraph.getEndOffset() != endOffset) {
726                     lastStartSpec.setDirection(ElementSpec.
727                                                JoinFractureDirection);
728                 }
729                 // Join to next if parent of pParagraph has another
730                 // element after pParagraph, and it isn't a leaf.
731                 else {
732                     Element parent = pParagraph.getParentElement();
733                     int pParagraphIndex = parent.getElementIndex(offset);
734                     if((pParagraphIndex + 1) < parent.getElementCount() &&
735                        !parent.getElement(pParagraphIndex + 1).isLeaf()) {
736                         lastStartSpec.setDirection(ElementSpec.
737                                                    JoinNextDirection);
738                     }
739                 }
740             }
741 
742             // Do a JoinNext for last spec if it is content, it doesn't
743             // already have a direction set, no new paragraphs have been
744             // inserted or a new paragraph has been inserted and its join
745             // direction isn't originate, and the element at endOffset
746             // is a leaf.
747             if(insertingAtBoundry && endOffset < docLength) {
748                 ElementSpec last = parseBuffer.lastElement();
749                 if(last.getType() == ElementSpec.ContentType &&
750                    last.getDirection() != ElementSpec.JoinPreviousDirection &&
751                    ((lastStartSpec == null && (paragraph == pParagraph ||
752                                                insertingAfterNewline)) ||
753                     (lastStartSpec != null && lastStartSpec.getDirection() !=
754                      ElementSpec.OriginateDirection))) {
755                     Element nextRun = paragraph.getElement(paragraph.
756                                            getElementIndex(endOffset));
757                     // Don't try joining to a branch!
758                     if(nextRun.isLeaf() &&
759                        attr.isEqual(nextRun.getAttributes())) {
760                         last.setDirection(ElementSpec.JoinNextDirection);
761                     }
762                 }
763             }
764             // If not inserting at boundary and there is going to be a
765             // fracture, then can join next on last content if cattr
766             // matches the new attributes.
767             else if(!insertingAtBoundry && lastStartSpec != null &&
768                     lastStartSpec.getDirection() ==
769                     ElementSpec.JoinFractureDirection) {
770                 ElementSpec last = parseBuffer.lastElement();
771                 if(last.getType() == ElementSpec.ContentType &&
772                    last.getDirection() != ElementSpec.JoinPreviousDirection &&
773                    attr.isEqual(cattr)) {
774                     last.setDirection(ElementSpec.JoinNextDirection);
775                 }
776             }
777 
778             // Check for the composed text element. If it is, merge the character attributes
779             // into this element as well.
780             if (Utilities.isComposedTextAttributeDefined(attr)) {
781                 MutableAttributeSet mattr = (MutableAttributeSet) attr;
782                 mattr.addAttributes(cattr);
783                 mattr.addAttribute(AbstractDocument.ElementNameAttribute,
784                         AbstractDocument.ContentElementName);
785 
786                 // Assure that the composed text element is named properly
787                 // and doesn't have the CR attribute defined.
788                 mattr.addAttribute(StyleConstants.NameAttribute,
789                         AbstractDocument.ContentElementName);
790                 if (mattr.isDefined(IMPLIED_CR)) {
791                     mattr.removeAttribute(IMPLIED_CR);
792                 }
793             }
794 
795             ElementSpec[] spec = new ElementSpec[parseBuffer.size()];
796             parseBuffer.copyInto(spec);
797             buffer.insert(offset, length, spec, chng);
798         } catch (BadLocationException bl) {
799         }
800 
801         super.insertUpdate( chng, attr );
802     }
803 
804     /**
805      * This is called by insertUpdate when inserting after a new line.
806      * It generates, in <code>parseBuffer</code>, ElementSpecs that will
807      * position the stack in <code>paragraph</code>.<p>
808      * It returns the direction the last StartSpec should have (this don't
809      * necessarily create the last start spec).
810      */
811     short createSpecsForInsertAfterNewline(Element paragraph,
812             Element pParagraph, AttributeSet pattr, Vector<ElementSpec> parseBuffer,
813                                                  int offset, int endOffset) {
814         // Need to find the common parent of pParagraph and paragraph.
815         if(paragraph.getParentElement() == pParagraph.getParentElement()) {
816             // The simple (and common) case that pParagraph and
817             // paragraph have the same parent.
818             ElementSpec spec = new ElementSpec(pattr, ElementSpec.EndTagType);
819             parseBuffer.addElement(spec);
820             spec = new ElementSpec(pattr, ElementSpec.StartTagType);
821             parseBuffer.addElement(spec);
822             if(pParagraph.getEndOffset() != endOffset)
823                 return ElementSpec.JoinFractureDirection;
824 
825             Element parent = pParagraph.getParentElement();
826             if((parent.getElementIndex(offset) + 1) < parent.getElementCount())
827                 return ElementSpec.JoinNextDirection;
828         }
829         else {
830             // Will only happen for text with more than 2 levels.
831             // Find the common parent of a paragraph and pParagraph
832             Vector<Element> leftParents = new Vector<Element>();
833             Vector<Element> rightParents = new Vector<Element>();
834             Element e = pParagraph;
835             while(e != null) {
836                 leftParents.addElement(e);
837                 e = e.getParentElement();
838             }
839             e = paragraph;
840             int leftIndex = -1;
841             while(e != null && (leftIndex = leftParents.indexOf(e)) == -1) {
842                 rightParents.addElement(e);
843                 e = e.getParentElement();
844             }
845             if(e != null) {
846                 // e identifies the common parent.
847                 // Build the ends.
848                 for(int counter = 0; counter < leftIndex;
849                     counter++) {
850                     parseBuffer.addElement(new ElementSpec
851                                               (null, ElementSpec.EndTagType));
852                 }
853                 // And the starts.
854                 ElementSpec spec;
855                 for(int counter = rightParents.size() - 1;
856                     counter >= 0; counter--) {
857                     spec = new ElementSpec(rightParents.elementAt(counter).getAttributes(),
858                                    ElementSpec.StartTagType);
859                     if(counter > 0)
860                         spec.setDirection(ElementSpec.JoinNextDirection);
861                     parseBuffer.addElement(spec);
862                 }
863                 // If there are right parents, then we generated starts
864                 // down the right subtree and there will be an element to
865                 // join to.
866                 if(rightParents.size() > 0)
867                     return ElementSpec.JoinNextDirection;
868                 // No right subtree, e.getElement(endOffset) is a
869                 // leaf. There will be a facture.
870                 return ElementSpec.JoinFractureDirection;
871             }
872             // else: Could throw an exception here, but should never get here!
873         }
874         return ElementSpec.OriginateDirection;
875     }
876 
877     /**
878      * Updates document structure as a result of text removal.
879      *
880      * @param chng a description of the document change
881      */
882     protected void removeUpdate(DefaultDocumentEvent chng) {
883         super.removeUpdate(chng);
884         buffer.remove(chng.getOffset(), chng.getLength(), chng);
885     }
886 
887     /**
888      * Creates the root element to be used to represent the
889      * default document structure.
890      *
891      * @return the element base
892      */
893     protected AbstractElement createDefaultRoot() {
894         // grabs a write-lock for this initialization and
895         // abandon it during initialization so in normal
896         // operation we can detect an illegitimate attempt
897         // to mutate attributes.
898         writeLock();
899         BranchElement section = new SectionElement();
900         BranchElement paragraph = new BranchElement(section, null);
901 
902         LeafElement brk = new LeafElement(paragraph, null, 0, 1);
903         Element[] buff = new Element[1];
904         buff[0] = brk;
905         paragraph.replace(0, 0, buff);
906 
907         buff[0] = paragraph;
908         section.replace(0, 0, buff);
909         writeUnlock();
910         return section;
911     }
912 
913     /**
914      * Gets the foreground color from an attribute set.
915      *
916      * @param attr the attribute set
917      * @return the color
918      */
919     public Color getForeground(AttributeSet attr) {
920         StyleContext styles = (StyleContext) getAttributeContext();
921         return styles.getForeground(attr);
922     }
923 
924     /**
925      * Gets the background color from an attribute set.
926      *
927      * @param attr the attribute set
928      * @return the color
929      */
930     public Color getBackground(AttributeSet attr) {
931         StyleContext styles = (StyleContext) getAttributeContext();
932         return styles.getBackground(attr);
933     }
934 
935     /**
936      * Gets the font from an attribute set.
937      *
938      * @param attr the attribute set
939      * @return the font
940      */
941     public Font getFont(AttributeSet attr) {
942         StyleContext styles = (StyleContext) getAttributeContext();
943         return styles.getFont(attr);
944     }
945 
946     /**
947      * Called when any of this document's styles have changed.
948      * Subclasses may wish to be intelligent about what gets damaged.
949      *
950      * @param style The Style that has changed.
951      */
952     protected void styleChanged(Style style) {
953         // Only propagate change updated if have content
954         if (getLength() != 0) {
955             // lazily create a ChangeUpdateRunnable
956             if (updateRunnable == null) {
957                 updateRunnable = new ChangeUpdateRunnable();
958             }
959 
960             // We may get a whole batch of these at once, so only
961             // queue the runnable if it is not already pending
962             synchronized(updateRunnable) {
963                 if (!updateRunnable.isPending) {
964                     SwingUtilities.invokeLater(updateRunnable);
965                     updateRunnable.isPending = true;
966                 }
967             }
968         }
969     }
970 
971     /**
972      * Adds a document listener for notification of any changes.
973      *
974      * @param listener the listener
975      * @see Document#addDocumentListener
976      */
977     public void addDocumentListener(DocumentListener listener) {
978         synchronized(listeningStyles) {
979             int oldDLCount = listenerList.getListenerCount
980                                           (DocumentListener.class);
981             super.addDocumentListener(listener);
982             if (oldDLCount == 0) {
983                 if (styleContextChangeListener == null) {
984                     styleContextChangeListener =
985                                       createStyleContextChangeListener();
986                 }
987                 if (styleContextChangeListener != null) {
988                     StyleContext styles = (StyleContext)getAttributeContext();
989                     List<ChangeListener> staleListeners =
990                         AbstractChangeHandler.getStaleListeners(styleContextChangeListener);
991                     for (ChangeListener l: staleListeners) {
992                         styles.removeChangeListener(l);
993                     }
994                     styles.addChangeListener(styleContextChangeListener);
995                 }
996                 updateStylesListeningTo();
997             }
998         }
999     }
1000 
1001     /**
1002      * Removes a document listener.
1003      *
1004      * @param listener the listener
1005      * @see Document#removeDocumentListener
1006      */
1007     public void removeDocumentListener(DocumentListener listener) {
1008         synchronized(listeningStyles) {
1009             super.removeDocumentListener(listener);
1010             if (listenerList.getListenerCount(DocumentListener.class) == 0) {
1011                 for (int counter = listeningStyles.size() - 1; counter >= 0;
1012                      counter--) {
1013                     listeningStyles.elementAt(counter).
1014                                     removeChangeListener(styleChangeListener);
1015                 }
1016                 listeningStyles.removeAllElements();
1017                 if (styleContextChangeListener != null) {
1018                     StyleContext styles = (StyleContext)getAttributeContext();
1019                     styles.removeChangeListener(styleContextChangeListener);
1020                 }
1021             }
1022         }
1023     }
1024 
1025     /**
1026      * Returns a new instance of StyleChangeHandler.
1027      */
1028     ChangeListener createStyleChangeListener() {
1029         return new StyleChangeHandler(this);
1030     }
1031 
1032     /**
1033      * Returns a new instance of StyleContextChangeHandler.
1034      */
1035     ChangeListener createStyleContextChangeListener() {
1036         return new StyleContextChangeHandler(this);
1037     }
1038 
1039     /**
1040      * Adds a ChangeListener to new styles, and removes ChangeListener from
1041      * old styles.
1042      */
1043     void updateStylesListeningTo() {
1044         synchronized(listeningStyles) {
1045             StyleContext styles = (StyleContext)getAttributeContext();
1046             if (styleChangeListener == null) {
1047                 styleChangeListener = createStyleChangeListener();
1048             }
1049             if (styleChangeListener != null && styles != null) {
1050                 Enumeration styleNames = styles.getStyleNames();
1051                 Vector v = (Vector)listeningStyles.clone();
1052                 listeningStyles.removeAllElements();
1053                 List<ChangeListener> staleListeners =
1054                     AbstractChangeHandler.getStaleListeners(styleChangeListener);
1055                 while (styleNames.hasMoreElements()) {
1056                     String name = (String)styleNames.nextElement();
1057                     Style aStyle = styles.getStyle(name);
1058                     int index = v.indexOf(aStyle);
1059                     listeningStyles.addElement(aStyle);
1060                     if (index == -1) {
1061                         for (ChangeListener l: staleListeners) {
1062                             aStyle.removeChangeListener(l);
1063                         }
1064                         aStyle.addChangeListener(styleChangeListener);
1065                     }
1066                     else {
1067                         v.removeElementAt(index);
1068                     }
1069                 }
1070                 for (int counter = v.size() - 1; counter >= 0; counter--) {
1071                     Style aStyle = (Style)v.elementAt(counter);
1072                     aStyle.removeChangeListener(styleChangeListener);
1073                 }
1074                 if (listeningStyles.size() == 0) {
1075                     styleChangeListener = null;
1076                 }
1077             }
1078         }
1079     }
1080 
1081     private void readObject(ObjectInputStream s)
1082             throws ClassNotFoundException, IOException {
1083         listeningStyles = new Vector<Style>();
1084         s.defaultReadObject();
1085         // Reinstall style listeners.
1086         if (styleContextChangeListener == null &&
1087             listenerList.getListenerCount(DocumentListener.class) > 0) {
1088             styleContextChangeListener = createStyleContextChangeListener();
1089             if (styleContextChangeListener != null) {
1090                 StyleContext styles = (StyleContext)getAttributeContext();
1091                 styles.addChangeListener(styleContextChangeListener);
1092             }
1093             updateStylesListeningTo();
1094         }
1095     }
1096 
1097     // --- member variables -----------------------------------------------------------
1098 
1099     /**
1100      * The default size of the initial content buffer.
1101      */
1102     public static final int BUFFER_SIZE_DEFAULT = 4096;
1103 
1104     protected ElementBuffer buffer;
1105 
1106     /** Styles listening to. */
1107     private transient Vector<Style> listeningStyles;
1108 
1109     /** Listens to Styles. */
1110     private transient ChangeListener styleChangeListener;
1111 
1112     /** Listens to Styles. */
1113     private transient ChangeListener styleContextChangeListener;
1114 
1115     /** Run to create a change event for the document */
1116     private transient ChangeUpdateRunnable updateRunnable;
1117 
1118     /**
1119      * Default root element for a document... maps out the
1120      * paragraphs/lines contained.
1121      * <p>
1122      * <strong>Warning:</strong>
1123      * Serialized objects of this class will not be compatible with
1124      * future Swing releases. The current serialization support is
1125      * appropriate for short term storage or RMI between applications running
1126      * the same version of Swing.  As of 1.4, support for long term storage
1127      * of all JavaBeans&trade;
1128      * has been added to the <code>java.beans</code> package.
1129      * Please see {@link java.beans.XMLEncoder}.
1130      */
1131     protected class SectionElement extends BranchElement {
1132 
1133         /**
1134          * Creates a new SectionElement.
1135          */
1136         public SectionElement() {
1137             super(null, null);
1138         }
1139 
1140         /**
1141          * Gets the name of the element.
1142          *
1143          * @return the name
1144          */
1145         public String getName() {
1146             return SectionElementName;
1147         }
1148     }
1149 
1150     /**
1151      * Specification for building elements.
1152      * <p>
1153      * <strong>Warning:</strong>
1154      * Serialized objects of this class will not be compatible with
1155      * future Swing releases. The current serialization support is
1156      * appropriate for short term storage or RMI between applications running
1157      * the same version of Swing.  As of 1.4, support for long term storage
1158      * of all JavaBeans&trade;
1159      * has been added to the <code>java.beans</code> package.
1160      * Please see {@link java.beans.XMLEncoder}.
1161      */
1162     public static class ElementSpec {
1163 
1164         /**
1165          * A possible value for getType.  This specifies
1166          * that this record type is a start tag and
1167          * represents markup that specifies the start
1168          * of an element.
1169          */
1170         public static final short StartTagType = 1;
1171 
1172         /**
1173          * A possible value for getType.  This specifies
1174          * that this record type is a end tag and
1175          * represents markup that specifies the end
1176          * of an element.
1177          */
1178         public static final short EndTagType = 2;
1179 
1180         /**
1181          * A possible value for getType.  This specifies
1182          * that this record type represents content.
1183          */
1184         public static final short ContentType = 3;
1185 
1186         /**
1187          * A possible value for getDirection.  This specifies
1188          * that the data associated with this record should
1189          * be joined to what precedes it.
1190          */
1191         public static final short JoinPreviousDirection = 4;
1192 
1193         /**
1194          * A possible value for getDirection.  This specifies
1195          * that the data associated with this record should
1196          * be joined to what follows it.
1197          */
1198         public static final short JoinNextDirection = 5;
1199 
1200         /**
1201          * A possible value for getDirection.  This specifies
1202          * that the data associated with this record should
1203          * be used to originate a new element.  This would be
1204          * the normal value.
1205          */
1206         public static final short OriginateDirection = 6;
1207 
1208         /**
1209          * A possible value for getDirection.  This specifies
1210          * that the data associated with this record should
1211          * be joined to the fractured element.
1212          */
1213         public static final short JoinFractureDirection = 7;
1214 
1215 
1216         /**
1217          * Constructor useful for markup when the markup will not
1218          * be stored in the document.
1219          *
1220          * @param a the attributes for the element
1221          * @param type the type of the element (StartTagType, EndTagType,
1222          *  ContentType)
1223          */
1224         public ElementSpec(AttributeSet a, short type) {
1225             this(a, type, null, 0, 0);
1226         }
1227 
1228         /**
1229          * Constructor for parsing inside the document when
1230          * the data has already been added, but len information
1231          * is needed.
1232          *
1233          * @param a the attributes for the element
1234          * @param type the type of the element (StartTagType, EndTagType,
1235          *  ContentType)
1236          * @param len the length &gt;= 0
1237          */
1238         public ElementSpec(AttributeSet a, short type, int len) {
1239             this(a, type, null, 0, len);
1240         }
1241 
1242         /**
1243          * Constructor for creating a spec externally for batch
1244          * input of content and markup into the document.
1245          *
1246          * @param a the attributes for the element
1247          * @param type the type of the element (StartTagType, EndTagType,
1248          *  ContentType)
1249          * @param txt the text for the element
1250          * @param offs the offset into the text &gt;= 0
1251          * @param len the length of the text &gt;= 0
1252          */
1253         public ElementSpec(AttributeSet a, short type, char[] txt,
1254                                   int offs, int len) {
1255             attr = a;
1256             this.type = type;
1257             this.data = txt;
1258             this.offs = offs;
1259             this.len = len;
1260             this.direction = OriginateDirection;
1261         }
1262 
1263         /**
1264          * Sets the element type.
1265          *
1266          * @param type the type of the element (StartTagType, EndTagType,
1267          *  ContentType)
1268          */
1269         public void setType(short type) {
1270             this.type = type;
1271         }
1272 
1273         /**
1274          * Gets the element type.
1275          *
1276          * @return  the type of the element (StartTagType, EndTagType,
1277          *  ContentType)
1278          */
1279         public short getType() {
1280             return type;
1281         }
1282 
1283         /**
1284          * Sets the direction.
1285          *
1286          * @param direction the direction (JoinPreviousDirection,
1287          *   JoinNextDirection)
1288          */
1289         public void setDirection(short direction) {
1290             this.direction = direction;
1291         }
1292 
1293         /**
1294          * Gets the direction.
1295          *
1296          * @return the direction (JoinPreviousDirection, JoinNextDirection)
1297          */
1298         public short getDirection() {
1299             return direction;
1300         }
1301 
1302         /**
1303          * Gets the element attributes.
1304          *
1305          * @return the attribute set
1306          */
1307         public AttributeSet getAttributes() {
1308             return attr;
1309         }
1310 
1311         /**
1312          * Gets the array of characters.
1313          *
1314          * @return the array
1315          */
1316         public char[] getArray() {
1317             return data;
1318         }
1319 
1320 
1321         /**
1322          * Gets the starting offset.
1323          *
1324          * @return the offset &gt;= 0
1325          */
1326         public int getOffset() {
1327             return offs;
1328         }
1329 
1330         /**
1331          * Gets the length.
1332          *
1333          * @return the length &gt;= 0
1334          */
1335         public int getLength() {
1336             return len;
1337         }
1338 
1339         /**
1340          * Converts the element to a string.
1341          *
1342          * @return the string
1343          */
1344         public String toString() {
1345             String tlbl = "??";
1346             String plbl = "??";
1347             switch(type) {
1348             case StartTagType:
1349                 tlbl = "StartTag";
1350                 break;
1351             case ContentType:
1352                 tlbl = "Content";
1353                 break;
1354             case EndTagType:
1355                 tlbl = "EndTag";
1356                 break;
1357             }
1358             switch(direction) {
1359             case JoinPreviousDirection:
1360                 plbl = "JoinPrevious";
1361                 break;
1362             case JoinNextDirection:
1363                 plbl = "JoinNext";
1364                 break;
1365             case OriginateDirection:
1366                 plbl = "Originate";
1367                 break;
1368             case JoinFractureDirection:
1369                 plbl = "Fracture";
1370                 break;
1371             }
1372             return tlbl + ":" + plbl + ":" + getLength();
1373         }
1374 
1375         private AttributeSet attr;
1376         private int len;
1377         private short type;
1378         private short direction;
1379 
1380         private int offs;
1381         private char[] data;
1382     }
1383 
1384     /**
1385      * Class to manage changes to the element
1386      * hierarchy.
1387      * <p>
1388      * <strong>Warning:</strong>
1389      * Serialized objects of this class will not be compatible with
1390      * future Swing releases. The current serialization support is
1391      * appropriate for short term storage or RMI between applications running
1392      * the same version of Swing.  As of 1.4, support for long term storage
1393      * of all JavaBeans&trade;
1394      * has been added to the <code>java.beans</code> package.
1395      * Please see {@link java.beans.XMLEncoder}.
1396      */
1397     public class ElementBuffer implements Serializable {
1398 
1399         /**
1400          * Creates a new ElementBuffer.
1401          *
1402          * @param root the root element
1403          * @since 1.4
1404          */
1405         public ElementBuffer(Element root) {
1406             this.root = root;
1407             changes = new Vector<ElemChanges>();
1408             path = new Stack<ElemChanges>();
1409         }
1410 
1411         /**
1412          * Gets the root element.
1413          *
1414          * @return the root element
1415          */
1416         public Element getRootElement() {
1417             return root;
1418         }
1419 
1420         /**
1421          * Inserts new content.
1422          *
1423          * @param offset the starting offset &gt;= 0
1424          * @param length the length &gt;= 0
1425          * @param data the data to insert
1426          * @param de the event capturing this edit
1427          */
1428         public void insert(int offset, int length, ElementSpec[] data,
1429                                  DefaultDocumentEvent de) {
1430             if (length == 0) {
1431                 // Nothing was inserted, no structure change.
1432                 return;
1433             }
1434             insertOp = true;
1435             beginEdits(offset, length);
1436             insertUpdate(data);
1437             endEdits(de);
1438 
1439             insertOp = false;
1440         }
1441 
1442         void create(int length, ElementSpec[] data, DefaultDocumentEvent de) {
1443             insertOp = true;
1444             beginEdits(offset, length);
1445 
1446             // PENDING(prinz) this needs to be fixed to create a new
1447             // root element as well, but requires changes to the
1448             // DocumentEvent to inform the views that there is a new
1449             // root element.
1450 
1451             // Recreate the ending fake element to have the correct offsets.
1452             Element elem = root;
1453             int index = elem.getElementIndex(0);
1454             while (! elem.isLeaf()) {
1455                 Element child = elem.getElement(index);
1456                 push(elem, index);
1457                 elem = child;
1458                 index = elem.getElementIndex(0);
1459             }
1460             ElemChanges ec = path.peek();
1461             Element child = ec.parent.getElement(ec.index);
1462             ec.added.addElement(createLeafElement(ec.parent,
1463                                 child.getAttributes(), getLength(),
1464                                 child.getEndOffset()));
1465             ec.removed.addElement(child);
1466             while (path.size() > 1) {
1467                 pop();
1468             }
1469 
1470             int n = data.length;
1471 
1472             // Reset the root elements attributes.
1473             AttributeSet newAttrs = null;
1474             if (n > 0 && data[0].getType() == ElementSpec.StartTagType) {
1475                 newAttrs = data[0].getAttributes();
1476             }
1477             if (newAttrs == null) {
1478                 newAttrs = SimpleAttributeSet.EMPTY;
1479             }
1480             MutableAttributeSet attr = (MutableAttributeSet)root.
1481                                        getAttributes();
1482             de.addEdit(new AttributeUndoableEdit(root, newAttrs, true));
1483             attr.removeAttributes(attr);
1484             attr.addAttributes(newAttrs);
1485 
1486             // fold in the specified subtree
1487             for (int i = 1; i < n; i++) {
1488                 insertElement(data[i]);
1489             }
1490 
1491             // pop the remaining path
1492             while (path.size() != 0) {
1493                 pop();
1494             }
1495 
1496             endEdits(de);
1497             insertOp = false;
1498         }
1499 
1500         /**
1501          * Removes content.
1502          *
1503          * @param offset the starting offset &gt;= 0
1504          * @param length the length &gt;= 0
1505          * @param de the event capturing this edit
1506          */
1507         public void remove(int offset, int length, DefaultDocumentEvent de) {
1508             beginEdits(offset, length);
1509             removeUpdate();
1510             endEdits(de);
1511         }
1512 
1513         /**
1514          * Changes content.
1515          *
1516          * @param offset the starting offset &gt;= 0
1517          * @param length the length &gt;= 0
1518          * @param de the event capturing this edit
1519          */
1520         public void change(int offset, int length, DefaultDocumentEvent de) {
1521             beginEdits(offset, length);
1522             changeUpdate();
1523             endEdits(de);
1524         }
1525 
1526         /**
1527          * Inserts an update into the document.
1528          *
1529          * @param data the elements to insert
1530          */
1531         protected void insertUpdate(ElementSpec[] data) {
1532             // push the path
1533             Element elem = root;
1534             int index = elem.getElementIndex(offset);
1535             while (! elem.isLeaf()) {
1536                 Element child = elem.getElement(index);
1537                 push(elem, (child.isLeaf() ? index : index+1));
1538                 elem = child;
1539                 index = elem.getElementIndex(offset);
1540             }
1541 
1542             // Build a copy of the original path.
1543             insertPath = new ElemChanges[path.size()];
1544             path.copyInto(insertPath);
1545 
1546             // Haven't created the fracture yet.
1547             createdFracture = false;
1548 
1549             // Insert the first content.
1550             int i;
1551 
1552             recreateLeafs = false;
1553             if(data[0].getType() == ElementSpec.ContentType) {
1554                 insertFirstContent(data);
1555                 pos += data[0].getLength();
1556                 i = 1;
1557             }
1558             else {
1559                 fractureDeepestLeaf(data);
1560                 i = 0;
1561             }
1562 
1563             // fold in the specified subtree
1564             int n = data.length;
1565             for (; i < n; i++) {
1566                 insertElement(data[i]);
1567             }
1568 
1569             // Fracture, if we haven't yet.
1570             if(!createdFracture)
1571                 fracture(-1);
1572 
1573             // pop the remaining path
1574             while (path.size() != 0) {
1575                 pop();
1576             }
1577 
1578             // Offset the last index if necessary.
1579             if(offsetLastIndex && offsetLastIndexOnReplace) {
1580                 insertPath[insertPath.length - 1].index++;
1581             }
1582 
1583             // Make sure an edit is going to be created for each of the
1584             // original path items that have a change.
1585             for(int counter = insertPath.length - 1; counter >= 0;
1586                 counter--) {
1587                 ElemChanges change = insertPath[counter];
1588                 if(change.parent == fracturedParent)
1589                     change.added.addElement(fracturedChild);
1590                 if((change.added.size() > 0 ||
1591                     change.removed.size() > 0) && !changes.contains(change)) {
1592                     // PENDING(sky): Do I need to worry about order here?
1593                     changes.addElement(change);
1594                 }
1595             }
1596 
1597             // An insert at 0 with an initial end implies some elements
1598             // will have no children (the bottomost leaf would have length 0)
1599             // this will find what element need to be removed and remove it.
1600             if (offset == 0 && fracturedParent != null &&
1601                 data[0].getType() == ElementSpec.EndTagType) {
1602                 int counter = 0;
1603                 while (counter < data.length &&
1604                        data[counter].getType() == ElementSpec.EndTagType) {
1605                     counter++;
1606                 }
1607                 ElemChanges change = insertPath[insertPath.length -
1608                                                counter - 1];
1609                 change.removed.insertElementAt(change.parent.getElement
1610                                                (--change.index), 0);
1611             }
1612         }
1613 
1614         /**
1615          * Updates the element structure in response to a removal from the
1616          * associated sequence in the document.  Any elements consumed by the
1617          * span of the removal are removed.
1618          */
1619         protected void removeUpdate() {
1620             removeElements(root, offset, offset + length);
1621         }
1622 
1623         /**
1624          * Updates the element structure in response to a change in the
1625          * document.
1626          */
1627         protected void changeUpdate() {
1628             boolean didEnd = split(offset, length);
1629             if (! didEnd) {
1630                 // need to do the other end
1631                 while (path.size() != 0) {
1632                     pop();
1633                 }
1634                 split(offset + length, 0);
1635             }
1636             while (path.size() != 0) {
1637                 pop();
1638             }
1639         }
1640 
1641         boolean split(int offs, int len) {
1642             boolean splitEnd = false;
1643             // push the path
1644             Element e = root;
1645             int index = e.getElementIndex(offs);
1646             while (! e.isLeaf()) {
1647                 push(e, index);
1648                 e = e.getElement(index);
1649                 index = e.getElementIndex(offs);
1650             }
1651 
1652             ElemChanges ec = path.peek();
1653             Element child = ec.parent.getElement(ec.index);
1654             // make sure there is something to do... if the
1655             // offset is already at a boundary then there is
1656             // nothing to do.
1657             if (child.getStartOffset() < offs && offs < child.getEndOffset()) {
1658                 // we need to split, now see if the other end is within
1659                 // the same parent.
1660                 int index0 = ec.index;
1661                 int index1 = index0;
1662                 if (((offs + len) < ec.parent.getEndOffset()) && (len != 0)) {
1663                     // it's a range split in the same parent
1664                     index1 = ec.parent.getElementIndex(offs+len);
1665                     if (index1 == index0) {
1666                         // it's a three-way split
1667                         ec.removed.addElement(child);
1668                         e = createLeafElement(ec.parent, child.getAttributes(),
1669                                               child.getStartOffset(), offs);
1670                         ec.added.addElement(e);
1671                         e = createLeafElement(ec.parent, child.getAttributes(),
1672                                           offs, offs + len);
1673                         ec.added.addElement(e);
1674                         e = createLeafElement(ec.parent, child.getAttributes(),
1675                                               offs + len, child.getEndOffset());
1676                         ec.added.addElement(e);
1677                         return true;
1678                     } else {
1679                         child = ec.parent.getElement(index1);
1680                         if ((offs + len) == child.getStartOffset()) {
1681                             // end is already on a boundary
1682                             index1 = index0;
1683                         }
1684                     }
1685                     splitEnd = true;
1686                 }
1687 
1688                 // split the first location
1689                 pos = offs;
1690                 child = ec.parent.getElement(index0);
1691                 ec.removed.addElement(child);
1692                 e = createLeafElement(ec.parent, child.getAttributes(),
1693                                       child.getStartOffset(), pos);
1694                 ec.added.addElement(e);
1695                 e = createLeafElement(ec.parent, child.getAttributes(),
1696                                       pos, child.getEndOffset());
1697                 ec.added.addElement(e);
1698 
1699                 // pick up things in the middle
1700                 for (int i = index0 + 1; i < index1; i++) {
1701                     child = ec.parent.getElement(i);
1702                     ec.removed.addElement(child);
1703                     ec.added.addElement(child);
1704                 }
1705 
1706                 if (index1 != index0) {
1707                     child = ec.parent.getElement(index1);
1708                     pos = offs + len;
1709                     ec.removed.addElement(child);
1710                     e = createLeafElement(ec.parent, child.getAttributes(),
1711                                           child.getStartOffset(), pos);
1712                     ec.added.addElement(e);
1713                     e = createLeafElement(ec.parent, child.getAttributes(),
1714                                           pos, child.getEndOffset());
1715                     ec.added.addElement(e);
1716                 }
1717             }
1718             return splitEnd;
1719         }
1720 
1721         /**
1722          * Creates the UndoableEdit record for the edits made
1723          * in the buffer.
1724          */
1725         void endEdits(DefaultDocumentEvent de) {
1726             int n = changes.size();
1727             for (int i = 0; i < n; i++) {
1728                 ElemChanges ec = changes.elementAt(i);
1729                 Element[] removed = new Element[ec.removed.size()];
1730                 ec.removed.copyInto(removed);
1731                 Element[] added = new Element[ec.added.size()];
1732                 ec.added.copyInto(added);
1733                 int index = ec.index;
1734                 ((BranchElement) ec.parent).replace(index, removed.length, added);
1735                 ElementEdit ee = new ElementEdit(ec.parent, index, removed, added);
1736                 de.addEdit(ee);
1737             }
1738 
1739             changes.removeAllElements();
1740             path.removeAllElements();
1741 
1742             /*
1743             for (int i = 0; i < n; i++) {
1744                 ElemChanges ec = (ElemChanges) changes.elementAt(i);
1745                 System.err.print("edited: " + ec.parent + " at: " + ec.index +
1746                     " removed " + ec.removed.size());
1747                 if (ec.removed.size() > 0) {
1748                     int r0 = ((Element) ec.removed.firstElement()).getStartOffset();
1749                     int r1 = ((Element) ec.removed.lastElement()).getEndOffset();
1750                     System.err.print("[" + r0 + "," + r1 + "]");
1751                 }
1752                 System.err.print(" added " + ec.added.size());
1753                 if (ec.added.size() > 0) {
1754                     int p0 = ((Element) ec.added.firstElement()).getStartOffset();
1755                     int p1 = ((Element) ec.added.lastElement()).getEndOffset();
1756                     System.err.print("[" + p0 + "," + p1 + "]");
1757                 }
1758                 System.err.println("");
1759             }
1760             */
1761         }
1762 
1763         /**
1764          * Initialize the buffer
1765          */
1766         void beginEdits(int offset, int length) {
1767             this.offset = offset;
1768             this.length = length;
1769             this.endOffset = offset + length;
1770             pos = offset;
1771             if (changes == null) {
1772                 changes = new Vector<ElemChanges>();
1773             } else {
1774                 changes.removeAllElements();
1775             }
1776             if (path == null) {
1777                 path = new Stack<ElemChanges>();
1778             } else {
1779                 path.removeAllElements();
1780             }
1781             fracturedParent = null;
1782             fracturedChild = null;
1783             offsetLastIndex = offsetLastIndexOnReplace = false;
1784         }
1785 
1786         /**
1787          * Pushes a new element onto the stack that represents
1788          * the current path.
1789          * @param record Whether or not the push should be
1790          *  recorded as an element change or not.
1791          * @param isFracture true if pushing on an element that was created
1792          * as the result of a fracture.
1793          */
1794         void push(Element e, int index, boolean isFracture) {
1795             ElemChanges ec = new ElemChanges(e, index, isFracture);
1796             path.push(ec);
1797         }
1798 
1799         void push(Element e, int index) {
1800             push(e, index, false);
1801         }
1802 
1803         void pop() {
1804             ElemChanges ec = path.peek();
1805             path.pop();
1806             if ((ec.added.size() > 0) || (ec.removed.size() > 0)) {
1807                 changes.addElement(ec);
1808             } else if (! path.isEmpty()) {
1809                 Element e = ec.parent;
1810                 if(e.getElementCount() == 0) {
1811                     // if we pushed a branch element that didn't get
1812                     // used, make sure its not marked as having been added.
1813                     ec = path.peek();
1814                     ec.added.removeElement(e);
1815                 }
1816             }
1817         }
1818 
1819         /**
1820          * move the current offset forward by n.
1821          */
1822         void advance(int n) {
1823             pos += n;
1824         }
1825 
1826         void insertElement(ElementSpec es) {
1827             ElemChanges ec = path.peek();
1828             switch(es.getType()) {
1829             case ElementSpec.StartTagType:
1830                 switch(es.getDirection()) {
1831                 case ElementSpec.JoinNextDirection:
1832                     // Don't create a new element, use the existing one
1833                     // at the specified location.
1834                     Element parent = ec.parent.getElement(ec.index);
1835 
1836                     if(parent.isLeaf()) {
1837                         // This happens if inserting into a leaf, followed
1838                         // by a join next where next sibling is not a leaf.
1839                         if((ec.index + 1) < ec.parent.getElementCount())
1840                             parent = ec.parent.getElement(ec.index + 1);
1841                         else
1842                             throw new StateInvariantError("Join next to leaf");
1843                     }
1844                     // Not really a fracture, but need to treat it like
1845                     // one so that content join next will work correctly.
1846                     // We can do this because there will never be a join
1847                     // next followed by a join fracture.
1848                     push(parent, 0, true);
1849                     break;
1850                 case ElementSpec.JoinFractureDirection:
1851                     if(!createdFracture) {
1852                         // Should always be something on the stack!
1853                         fracture(path.size() - 1);
1854                     }
1855                     // If parent isn't a fracture, fracture will be
1856                     // fracturedChild.
1857                     if(!ec.isFracture) {
1858                         push(fracturedChild, 0, true);
1859                     }
1860                     else
1861                         // Parent is a fracture, use 1st element.
1862                         push(ec.parent.getElement(0), 0, true);
1863                     break;
1864                 default:
1865                     Element belem = createBranchElement(ec.parent,
1866                                                         es.getAttributes());
1867                     ec.added.addElement(belem);
1868                     push(belem, 0);
1869                     break;
1870                 }
1871                 break;
1872             case ElementSpec.EndTagType:
1873                 pop();
1874                 break;
1875             case ElementSpec.ContentType:
1876               int len = es.getLength();
1877                 if (es.getDirection() != ElementSpec.JoinNextDirection) {
1878                     Element leaf = createLeafElement(ec.parent, es.getAttributes(),
1879                                                      pos, pos + len);
1880                     ec.added.addElement(leaf);
1881                 }
1882                 else {
1883                     // JoinNext on tail is only applicable if last element
1884                     // and attributes come from that of first element.
1885                     // With a little extra testing it would be possible
1886                     // to NOT due this again, as more than likely fracture()
1887                     // created this element.
1888                     if(!ec.isFracture) {
1889                         Element first = null;
1890                         if(insertPath != null) {
1891                             for(int counter = insertPath.length - 1;
1892                                 counter >= 0; counter--) {
1893                                 if(insertPath[counter] == ec) {
1894                                     if(counter != (insertPath.length - 1))
1895                                         first = ec.parent.getElement(ec.index);
1896                                     break;
1897                                 }
1898                             }
1899                         }
1900                         if(first == null)
1901                             first = ec.parent.getElement(ec.index + 1);
1902                         Element leaf = createLeafElement(ec.parent, first.
1903                                  getAttributes(), pos, first.getEndOffset());
1904                         ec.added.addElement(leaf);
1905                         ec.removed.addElement(first);
1906                     }
1907                     else {
1908                         // Parent was fractured element.
1909                         Element first = ec.parent.getElement(0);
1910                         Element leaf = createLeafElement(ec.parent, first.
1911                                  getAttributes(), pos, first.getEndOffset());
1912                         ec.added.addElement(leaf);
1913                         ec.removed.addElement(first);
1914                     }
1915                 }
1916                 pos += len;
1917                 break;
1918             }
1919         }
1920 
1921         /**
1922          * Remove the elements from <code>elem</code> in range
1923          * <code>rmOffs0</code>, <code>rmOffs1</code>. This uses
1924          * <code>canJoin</code> and <code>join</code> to handle joining
1925          * the endpoints of the insertion.
1926          *
1927          * @return true if elem will no longer have any elements.
1928          */
1929         boolean removeElements(Element elem, int rmOffs0, int rmOffs1) {
1930             if (! elem.isLeaf()) {
1931                 // update path for changes
1932                 int index0 = elem.getElementIndex(rmOffs0);
1933                 int index1 = elem.getElementIndex(rmOffs1);
1934                 push(elem, index0);
1935                 ElemChanges ec = path.peek();
1936 
1937                 // if the range is contained by one element,
1938                 // we just forward the request
1939                 if (index0 == index1) {
1940                     Element child0 = elem.getElement(index0);
1941                     if(rmOffs0 <= child0.getStartOffset() &&
1942                        rmOffs1 >= child0.getEndOffset()) {
1943                         // Element totally removed.
1944                         ec.removed.addElement(child0);
1945                     }
1946                     else if(removeElements(child0, rmOffs0, rmOffs1)) {
1947                         ec.removed.addElement(child0);
1948                     }
1949                 } else {
1950                     // the removal range spans elements.  If we can join
1951                     // the two endpoints, do it.  Otherwise we remove the
1952                     // interior and forward to the endpoints.
1953                     Element child0 = elem.getElement(index0);
1954                     Element child1 = elem.getElement(index1);
1955                     boolean containsOffs1 = (rmOffs1 < elem.getEndOffset());
1956                     if (containsOffs1 && canJoin(child0, child1)) {
1957                         // remove and join
1958                         for (int i = index0; i <= index1; i++) {
1959                             ec.removed.addElement(elem.getElement(i));
1960                         }
1961                         Element e = join(elem, child0, child1, rmOffs0, rmOffs1);
1962                         ec.added.addElement(e);
1963                     } else {
1964                         // remove interior and forward
1965                         int rmIndex0 = index0 + 1;
1966                         int rmIndex1 = index1 - 1;
1967                         if (child0.getStartOffset() == rmOffs0 ||
1968                             (index0 == 0 &&
1969                              child0.getStartOffset() > rmOffs0 &&
1970                              child0.getEndOffset() <= rmOffs1)) {
1971                             // start element completely consumed
1972                             child0 = null;
1973                             rmIndex0 = index0;
1974                         }
1975                         if (!containsOffs1) {
1976                             child1 = null;
1977                             rmIndex1++;
1978                         }
1979                         else if (child1.getStartOffset() == rmOffs1) {
1980                             // end element not touched
1981                             child1 = null;
1982                         }
1983                         if (rmIndex0 <= rmIndex1) {
1984                             ec.index = rmIndex0;
1985                         }
1986                         for (int i = rmIndex0; i <= rmIndex1; i++) {
1987                             ec.removed.addElement(elem.getElement(i));
1988                         }
1989                         if (child0 != null) {
1990                             if(removeElements(child0, rmOffs0, rmOffs1)) {
1991                                 ec.removed.insertElementAt(child0, 0);
1992                                 ec.index = index0;
1993                             }
1994                         }
1995                         if (child1 != null) {
1996                             if(removeElements(child1, rmOffs0, rmOffs1)) {
1997                                 ec.removed.addElement(child1);
1998                             }
1999                         }
2000                     }
2001                 }
2002 
2003                 // publish changes
2004                 pop();
2005 
2006                 // Return true if we no longer have any children.
2007                 if(elem.getElementCount() == (ec.removed.size() -
2008                                               ec.added.size())) {
2009                     return true;
2010                 }
2011             }
2012             return false;
2013         }
2014 
2015         /**
2016          * Can the two given elements be coelesced together
2017          * into one element?
2018          */
2019         boolean canJoin(Element e0, Element e1) {
2020             if ((e0 == null) || (e1 == null)) {
2021                 return false;
2022             }
2023             // Don't join a leaf to a branch.
2024             boolean leaf0 = e0.isLeaf();
2025             boolean leaf1 = e1.isLeaf();
2026             if(leaf0 != leaf1) {
2027                 return false;
2028             }
2029             if (leaf0) {
2030                 // Only join leaves if the attributes match, otherwise
2031                 // style information will be lost.
2032                 return e0.getAttributes().isEqual(e1.getAttributes());
2033             }
2034             // Only join non-leafs if the names are equal. This may result
2035             // in loss of style information, but this is typically acceptable
2036             // for non-leafs.
2037             String name0 = e0.getName();
2038             String name1 = e1.getName();
2039             if (name0 != null) {
2040                 return name0.equals(name1);
2041             }
2042             if (name1 != null) {
2043                 return name1.equals(name0);
2044             }
2045             // Both names null, treat as equal.
2046             return true;
2047         }
2048 
2049         /**
2050          * Joins the two elements carving out a hole for the
2051          * given removed range.
2052          */
2053         Element join(Element p, Element left, Element right, int rmOffs0, int rmOffs1) {
2054             if (left.isLeaf() && right.isLeaf()) {
2055                 return createLeafElement(p, left.getAttributes(), left.getStartOffset(),
2056                                          right.getEndOffset());
2057             } else if ((!left.isLeaf()) && (!right.isLeaf())) {
2058                 // join two branch elements.  This copies the children before
2059                 // the removal range on the left element, and after the removal
2060                 // range on the right element.  The two elements on the edge
2061                 // are joined if possible and needed.
2062                 Element to = createBranchElement(p, left.getAttributes());
2063                 int ljIndex = left.getElementIndex(rmOffs0);
2064                 int rjIndex = right.getElementIndex(rmOffs1);
2065                 Element lj = left.getElement(ljIndex);
2066                 if (lj.getStartOffset() >= rmOffs0) {
2067                     lj = null;
2068                 }
2069                 Element rj = right.getElement(rjIndex);
2070                 if (rj.getStartOffset() == rmOffs1) {
2071                     rj = null;
2072                 }
2073                 Vector<Element> children = new Vector<Element>();
2074 
2075                 // transfer the left
2076                 for (int i = 0; i < ljIndex; i++) {
2077                     children.addElement(clone(to, left.getElement(i)));
2078                 }
2079 
2080                 // transfer the join/middle
2081                 if (canJoin(lj, rj)) {
2082                     Element e = join(to, lj, rj, rmOffs0, rmOffs1);
2083                     children.addElement(e);
2084                 } else {
2085                     if (lj != null) {
2086                         children.addElement(cloneAsNecessary(to, lj, rmOffs0, rmOffs1));
2087                     }
2088                     if (rj != null) {
2089                         children.addElement(cloneAsNecessary(to, rj, rmOffs0, rmOffs1));
2090                     }
2091                 }
2092 
2093                 // transfer the right
2094                 int n = right.getElementCount();
2095                 for (int i = (rj == null) ? rjIndex : rjIndex + 1; i < n; i++) {
2096                     children.addElement(clone(to, right.getElement(i)));
2097                 }
2098 
2099                 // install the children
2100                 Element[] c = new Element[children.size()];
2101                 children.copyInto(c);
2102                 ((BranchElement)to).replace(0, 0, c);
2103                 return to;
2104             } else {
2105                 throw new StateInvariantError(
2106                     "No support to join leaf element with non-leaf element");
2107             }
2108         }
2109 
2110         /**
2111          * Creates a copy of this element, with a different
2112          * parent.
2113          *
2114          * @param parent the parent element
2115          * @param clonee the element to be cloned
2116          * @return the copy
2117          */
2118         public Element clone(Element parent, Element clonee) {
2119             if (clonee.isLeaf()) {
2120                 return createLeafElement(parent, clonee.getAttributes(),
2121                                          clonee.getStartOffset(),
2122                                          clonee.getEndOffset());
2123             }
2124             Element e = createBranchElement(parent, clonee.getAttributes());
2125             int n = clonee.getElementCount();
2126             Element[] children = new Element[n];
2127             for (int i = 0; i < n; i++) {
2128                 children[i] = clone(e, clonee.getElement(i));
2129             }
2130             ((BranchElement)e).replace(0, 0, children);
2131             return e;
2132         }
2133 
2134         /**
2135          * Creates a copy of this element, with a different
2136          * parent. Children of this element included in the
2137          * removal range will be discarded.
2138          */
2139         Element cloneAsNecessary(Element parent, Element clonee, int rmOffs0, int rmOffs1) {
2140             if (clonee.isLeaf()) {
2141                 return createLeafElement(parent, clonee.getAttributes(),
2142                                          clonee.getStartOffset(),
2143                                          clonee.getEndOffset());
2144             }
2145             Element e = createBranchElement(parent, clonee.getAttributes());
2146             int n = clonee.getElementCount();
2147             ArrayList<Element> childrenList = new ArrayList<Element>(n);
2148             for (int i = 0; i < n; i++) {
2149                 Element elem = clonee.getElement(i);
2150                 if (elem.getStartOffset() < rmOffs0 || elem.getEndOffset() > rmOffs1) {
2151                     childrenList.add(cloneAsNecessary(e, elem, rmOffs0, rmOffs1));
2152                 }
2153             }
2154             Element[] children = new Element[childrenList.size()];
2155             children = childrenList.toArray(children);
2156             ((BranchElement)e).replace(0, 0, children);
2157             return e;
2158         }
2159 
2160         /**
2161          * Determines if a fracture needs to be performed. A fracture
2162          * can be thought of as moving the right part of a tree to a
2163          * new location, where the right part is determined by what has
2164          * been inserted. <code>depth</code> is used to indicate a
2165          * JoinToFracture is needed to an element at a depth
2166          * of <code>depth</code>. Where the root is 0, 1 is the children
2167          * of the root...
2168          * <p>This will invoke <code>fractureFrom</code> if it is determined
2169          * a fracture needs to happen.
2170          */
2171         void fracture(int depth) {
2172             int cLength = insertPath.length;
2173             int lastIndex = -1;
2174             boolean needRecreate = recreateLeafs;
2175             ElemChanges lastChange = insertPath[cLength - 1];
2176             // Use childAltered to determine when a child has been altered,
2177             // that is the point of insertion is less than the element count.
2178             boolean childAltered = ((lastChange.index + 1) <
2179                                     lastChange.parent.getElementCount());
2180             int deepestAlteredIndex = (needRecreate) ? cLength : -1;
2181             int lastAlteredIndex = cLength - 1;
2182 
2183             createdFracture = true;
2184             // Determine where to start recreating from.
2185             // Start at - 2, as first one is indicated by recreateLeafs and
2186             // childAltered.
2187             for(int counter = cLength - 2; counter >= 0; counter--) {
2188                 ElemChanges change = insertPath[counter];
2189                 if(change.added.size() > 0 || counter == depth) {
2190                     lastIndex = counter;
2191                     if(!needRecreate && childAltered) {
2192                         needRecreate = true;
2193                         if(deepestAlteredIndex == -1)
2194                             deepestAlteredIndex = lastAlteredIndex + 1;
2195                     }
2196                 }
2197                 if(!childAltered && change.index <
2198                    change.parent.getElementCount()) {
2199                     childAltered = true;
2200                     lastAlteredIndex = counter;
2201                 }
2202             }
2203             if(needRecreate) {
2204                 // Recreate all children to right of parent starting
2205                 // at lastIndex.
2206                 if(lastIndex == -1)
2207                     lastIndex = cLength - 1;
2208                 fractureFrom(insertPath, lastIndex, deepestAlteredIndex);
2209             }
2210         }
2211 
2212         /**
2213          * Recreates the elements to the right of the insertion point.
2214          * This starts at <code>startIndex</code> in <code>changed</code>,
2215          * and calls duplicate to duplicate existing elements.
2216          * This will also duplicate the elements along the insertion
2217          * point, until a depth of <code>endFractureIndex</code> is
2218          * reached, at which point only the elements to the right of
2219          * the insertion point are duplicated.
2220          */
2221         void fractureFrom(ElemChanges[] changed, int startIndex,
2222                           int endFractureIndex) {
2223             // Recreate the element representing the inserted index.
2224             ElemChanges change = changed[startIndex];
2225             Element child;
2226             Element newChild;
2227             int changeLength = changed.length;
2228 
2229             if((startIndex + 1) == changeLength)
2230                 child = change.parent.getElement(change.index);
2231             else
2232                 child = change.parent.getElement(change.index - 1);
2233             if(child.isLeaf()) {
2234                 newChild = createLeafElement(change.parent,
2235                                child.getAttributes(), Math.max(endOffset,
2236                                child.getStartOffset()), child.getEndOffset());
2237             }
2238             else {
2239                 newChild = createBranchElement(change.parent,
2240                                                child.getAttributes());
2241             }
2242             fracturedParent = change.parent;
2243             fracturedChild = newChild;
2244 
2245             // Recreate all the elements to the right of the
2246             // insertion point.
2247             Element parent = newChild;
2248 
2249             while(++startIndex < endFractureIndex) {
2250                 boolean isEnd = ((startIndex + 1) == endFractureIndex);
2251                 boolean isEndLeaf = ((startIndex + 1) == changeLength);
2252 
2253                 // Create the newChild, a duplicate of the elment at
2254                 // index. This isn't done if isEnd and offsetLastIndex are true
2255                 // indicating a join previous was done.
2256                 change = changed[startIndex];
2257 
2258                 // Determine the child to duplicate, won't have to duplicate
2259                 // if at end of fracture, or offseting index.
2260                 if(isEnd) {
2261                     if(offsetLastIndex || !isEndLeaf)
2262                         child = null;
2263                     else
2264                         child = change.parent.getElement(change.index);
2265                 }
2266                 else {
2267                     child = change.parent.getElement(change.index - 1);
2268                 }
2269                 // Duplicate it.
2270                 if(child != null) {
2271                     if(child.isLeaf()) {
2272                         newChild = createLeafElement(parent,
2273                                child.getAttributes(), Math.max(endOffset,
2274                                child.getStartOffset()), child.getEndOffset());
2275                     }
2276                     else {
2277                         newChild = createBranchElement(parent,
2278                                                    child.getAttributes());
2279                     }
2280                 }
2281                 else
2282                     newChild = null;
2283 
2284                 // Recreate the remaining children (there may be none).
2285                 int kidsToMove = change.parent.getElementCount() -
2286                                  change.index;
2287                 Element[] kids;
2288                 int moveStartIndex;
2289                 int kidStartIndex = 1;
2290 
2291                 if(newChild == null) {
2292                     // Last part of fracture.
2293                     if(isEndLeaf) {
2294                         kidsToMove--;
2295                         moveStartIndex = change.index + 1;
2296                     }
2297                     else {
2298                         moveStartIndex = change.index;
2299                     }
2300                     kidStartIndex = 0;
2301                     kids = new Element[kidsToMove];
2302                 }
2303                 else {
2304                     if(!isEnd) {
2305                         // Branch.
2306                         kidsToMove++;
2307                         moveStartIndex = change.index;
2308                     }
2309                     else {
2310                         // Last leaf, need to recreate part of it.
2311                         moveStartIndex = change.index + 1;
2312                     }
2313                     kids = new Element[kidsToMove];
2314                     kids[0] = newChild;
2315                 }
2316 
2317                 for(int counter = kidStartIndex; counter < kidsToMove;
2318                     counter++) {
2319                     Element toMove =change.parent.getElement(moveStartIndex++);
2320                     kids[counter] = recreateFracturedElement(parent, toMove);
2321                     change.removed.addElement(toMove);
2322                 }
2323                 ((BranchElement)parent).replace(0, 0, kids);
2324                 parent = newChild;
2325             }
2326         }
2327 
2328         /**
2329          * Recreates <code>toDuplicate</code>. This is called when an
2330          * element needs to be created as the result of an insertion. This
2331          * will recurse and create all the children. This is similar to
2332          * <code>clone</code>, but deteremines the offsets differently.
2333          */
2334         Element recreateFracturedElement(Element parent, Element toDuplicate) {
2335             if(toDuplicate.isLeaf()) {
2336                 return createLeafElement(parent, toDuplicate.getAttributes(),
2337                                          Math.max(toDuplicate.getStartOffset(),
2338                                                   endOffset),
2339                                          toDuplicate.getEndOffset());
2340             }
2341             // Not a leaf
2342             Element newParent = createBranchElement(parent, toDuplicate.
2343                                                     getAttributes());
2344             int childCount = toDuplicate.getElementCount();
2345             Element[] newKids = new Element[childCount];
2346             for(int counter = 0; counter < childCount; counter++) {
2347                 newKids[counter] = recreateFracturedElement(newParent,
2348                                              toDuplicate.getElement(counter));
2349             }
2350             ((BranchElement)newParent).replace(0, 0, newKids);
2351             return newParent;
2352         }
2353 
2354         /**
2355          * Splits the bottommost leaf in <code>path</code>.
2356          * This is called from insert when the first element is NOT content.
2357          */
2358         void fractureDeepestLeaf(ElementSpec[] specs) {
2359             // Split the bottommost leaf. It will be recreated elsewhere.
2360             ElemChanges ec = path.peek();
2361             Element child = ec.parent.getElement(ec.index);
2362             // Inserts at offset 0 do not need to recreate child (it would
2363             // have a length of 0!).
2364             if (offset != 0) {
2365                 Element newChild = createLeafElement(ec.parent,
2366                                                  child.getAttributes(),
2367                                                  child.getStartOffset(),
2368                                                  offset);
2369 
2370                 ec.added.addElement(newChild);
2371             }
2372             ec.removed.addElement(child);
2373             if(child.getEndOffset() != endOffset)
2374                 recreateLeafs = true;
2375             else
2376                 offsetLastIndex = true;
2377         }
2378 
2379         /**
2380          * Inserts the first content. This needs to be separate to handle
2381          * joining.
2382          */
2383         void insertFirstContent(ElementSpec[] specs) {
2384             ElementSpec firstSpec = specs[0];
2385             ElemChanges ec = path.peek();
2386             Element child = ec.parent.getElement(ec.index);
2387             int firstEndOffset = offset + firstSpec.getLength();
2388             boolean isOnlyContent = (specs.length == 1);
2389 
2390             switch(firstSpec.getDirection()) {
2391             case ElementSpec.JoinPreviousDirection:
2392                 if(child.getEndOffset() != firstEndOffset &&
2393                     !isOnlyContent) {
2394                     // Create the left split part containing new content.
2395                     Element newE = createLeafElement(ec.parent,
2396                             child.getAttributes(), child.getStartOffset(),
2397                             firstEndOffset);
2398                     ec.added.addElement(newE);
2399                     ec.removed.addElement(child);
2400                     // Remainder will be created later.
2401                     if(child.getEndOffset() != endOffset)
2402                         recreateLeafs = true;
2403                     else
2404                         offsetLastIndex = true;
2405                 }
2406                 else {
2407                     offsetLastIndex = true;
2408                     offsetLastIndexOnReplace = true;
2409                 }
2410                 // else Inserted at end, and is total length.
2411                 // Update index incase something added/removed.
2412                 break;
2413             case ElementSpec.JoinNextDirection:
2414                 if(offset != 0) {
2415                     // Recreate the first element, its offset will have
2416                     // changed.
2417                     Element newE = createLeafElement(ec.parent,
2418                             child.getAttributes(), child.getStartOffset(),
2419                             offset);
2420                     ec.added.addElement(newE);
2421                     // Recreate the second, merge part. We do no checking
2422                     // to see if JoinNextDirection is valid here!
2423                     Element nextChild = ec.parent.getElement(ec.index + 1);
2424                     if(isOnlyContent)
2425                         newE = createLeafElement(ec.parent, nextChild.
2426                             getAttributes(), offset, nextChild.getEndOffset());
2427                     else
2428                         newE = createLeafElement(ec.parent, nextChild.
2429                             getAttributes(), offset, firstEndOffset);
2430                     ec.added.addElement(newE);
2431                     ec.removed.addElement(child);
2432                     ec.removed.addElement(nextChild);
2433                 }
2434                 // else nothin to do.
2435                 // PENDING: if !isOnlyContent could raise here!
2436                 break;
2437             default:
2438                 // Inserted into middle, need to recreate split left
2439                 // new content, and split right.
2440                 if(child.getStartOffset() != offset) {
2441                     Element newE = createLeafElement(ec.parent,
2442                             child.getAttributes(), child.getStartOffset(),
2443                             offset);
2444                     ec.added.addElement(newE);
2445                 }
2446                 ec.removed.addElement(child);
2447                 // new content
2448                 Element newE = createLeafElement(ec.parent,
2449                                                  firstSpec.getAttributes(),
2450                                                  offset, firstEndOffset);
2451                 ec.added.addElement(newE);
2452                 if(child.getEndOffset() != endOffset) {
2453                     // Signals need to recreate right split later.
2454                     recreateLeafs = true;
2455                 }
2456                 else {
2457                     offsetLastIndex = true;
2458                 }
2459                 break;
2460             }
2461         }
2462 
2463         Element root;
2464         transient int pos;          // current position
2465         transient int offset;
2466         transient int length;
2467         transient int endOffset;
2468         transient Vector<ElemChanges> changes;
2469         transient Stack<ElemChanges> path;
2470         transient boolean insertOp;
2471 
2472         transient boolean recreateLeafs; // For insert.
2473 
2474         /** For insert, path to inserted elements. */
2475         transient ElemChanges[] insertPath;
2476         /** Only for insert, set to true when the fracture has been created. */
2477         transient boolean createdFracture;
2478         /** Parent that contains the fractured child. */
2479         transient Element fracturedParent;
2480         /** Fractured child. */
2481         transient Element fracturedChild;
2482         /** Used to indicate when fracturing that the last leaf should be
2483          * skipped. */
2484         transient boolean offsetLastIndex;
2485         /** Used to indicate that the parent of the deepest leaf should
2486          * offset the index by 1 when adding/removing elements in an
2487          * insert. */
2488         transient boolean offsetLastIndexOnReplace;
2489 
2490         /*
2491          * Internal record used to hold element change specifications
2492          */
2493         class ElemChanges {
2494 
2495             ElemChanges(Element parent, int index, boolean isFracture) {
2496                 this.parent = parent;
2497                 this.index = index;
2498                 this.isFracture = isFracture;
2499                 added = new Vector<Element>();
2500                 removed = new Vector<Element>();
2501             }
2502 
2503             public String toString() {
2504                 return "added: " + added + "\nremoved: " + removed + "\n";
2505             }
2506 
2507             Element parent;
2508             int index;
2509             Vector<Element> added;
2510             Vector<Element> removed;
2511             boolean isFracture;
2512         }
2513 
2514     }
2515 
2516     /**
2517      * An UndoableEdit used to remember AttributeSet changes to an
2518      * Element.
2519      */
2520     public static class AttributeUndoableEdit extends AbstractUndoableEdit {
2521         public AttributeUndoableEdit(Element element, AttributeSet newAttributes,
2522                               boolean isReplacing) {
2523             super();
2524             this.element = element;
2525             this.newAttributes = newAttributes;
2526             this.isReplacing = isReplacing;
2527             // If not replacing, it may be more efficient to only copy the
2528             // changed values...
2529             copy = element.getAttributes().copyAttributes();
2530         }
2531 
2532         /**
2533          * Redoes a change.
2534          *
2535          * @exception CannotRedoException if the change cannot be redone
2536          */
2537         public void redo() throws CannotRedoException {
2538             super.redo();
2539             MutableAttributeSet as = (MutableAttributeSet)element
2540                                      .getAttributes();
2541             if(isReplacing)
2542                 as.removeAttributes(as);
2543             as.addAttributes(newAttributes);
2544         }
2545 
2546         /**
2547          * Undoes a change.
2548          *
2549          * @exception CannotUndoException if the change cannot be undone
2550          */
2551         public void undo() throws CannotUndoException {
2552             super.undo();
2553             MutableAttributeSet as = (MutableAttributeSet)element.getAttributes();
2554             as.removeAttributes(as);
2555             as.addAttributes(copy);
2556         }
2557 
2558         // AttributeSet containing additional entries, must be non-mutable!
2559         protected AttributeSet newAttributes;
2560         // Copy of the AttributeSet the Element contained.
2561         protected AttributeSet copy;
2562         // true if all the attributes in the element were removed first.
2563         protected boolean isReplacing;
2564         // Efected Element.
2565         protected Element element;
2566     }
2567 
2568     /**
2569      * UndoableEdit for changing the resolve parent of an Element.
2570      */
2571     static class StyleChangeUndoableEdit extends AbstractUndoableEdit {
2572         public StyleChangeUndoableEdit(AbstractElement element,
2573                                        Style newStyle) {
2574             super();
2575             this.element = element;
2576             this.newStyle = newStyle;
2577             oldStyle = element.getResolveParent();
2578         }
2579 
2580         /**
2581          * Redoes a change.
2582          *
2583          * @exception CannotRedoException if the change cannot be redone
2584          */
2585         public void redo() throws CannotRedoException {
2586             super.redo();
2587             element.setResolveParent(newStyle);
2588         }
2589 
2590         /**
2591          * Undoes a change.
2592          *
2593          * @exception CannotUndoException if the change cannot be undone
2594          */
2595         public void undo() throws CannotUndoException {
2596             super.undo();
2597             element.setResolveParent(oldStyle);
2598         }
2599 
2600         /** Element to change resolve parent of. */
2601         protected AbstractElement element;
2602         /** New style. */
2603         protected Style newStyle;
2604         /** Old style, before setting newStyle. */
2605         protected AttributeSet oldStyle;
2606     }
2607 
2608     /**
2609      * Base class for style change handlers with support for stale objects detection.
2610      */
2611     abstract static class AbstractChangeHandler implements ChangeListener {
2612 
2613         /* This has an implicit reference to the handler object.  */
2614         private class DocReference extends WeakReference<DefaultStyledDocument> {
2615 
2616             DocReference(DefaultStyledDocument d, ReferenceQueue<DefaultStyledDocument> q) {
2617                 super(d, q);
2618             }
2619 
2620             /**
2621              * Return a reference to the style change handler object.
2622              */
2623             ChangeListener getListener() {
2624                 return AbstractChangeHandler.this;
2625             }
2626         }
2627 
2628         /** Class-specific reference queues.  */
2629         private final static Map<Class, ReferenceQueue<DefaultStyledDocument>> queueMap
2630                 = new HashMap<Class, ReferenceQueue<DefaultStyledDocument>>();
2631 
2632         /** A weak reference to the document object.  */
2633         private DocReference doc;
2634 
2635         AbstractChangeHandler(DefaultStyledDocument d) {
2636             Class c = getClass();
2637             ReferenceQueue<DefaultStyledDocument> q;
2638             synchronized (queueMap) {
2639                 q = queueMap.get(c);
2640                 if (q == null) {
2641                     q = new ReferenceQueue<DefaultStyledDocument>();
2642                     queueMap.put(c, q);
2643                 }
2644             }
2645             doc = new DocReference(d, q);
2646         }
2647 
2648         /**
2649          * Return a list of stale change listeners.
2650          *
2651          * A change listener becomes "stale" when its document is cleaned by GC.
2652          */
2653         static List<ChangeListener> getStaleListeners(ChangeListener l) {
2654             List<ChangeListener> staleListeners = new ArrayList<ChangeListener>();
2655             ReferenceQueue<DefaultStyledDocument> q = queueMap.get(l.getClass());
2656 
2657             if (q != null) {
2658                 DocReference r;
2659                 synchronized (q) {
2660                     while ((r = (DocReference) q.poll()) != null) {
2661                         staleListeners.add(r.getListener());
2662                     }
2663                 }
2664             }
2665 
2666             return staleListeners;
2667         }
2668 
2669         /**
2670          * The ChangeListener wrapper which guards against dead documents.
2671          */
2672         public void stateChanged(ChangeEvent e) {
2673             DefaultStyledDocument d = doc.get();
2674             if (d != null) {
2675                 fireStateChanged(d, e);
2676             }
2677         }
2678 
2679         /** Run the actual class-specific stateChanged() method.  */
2680         abstract void fireStateChanged(DefaultStyledDocument d, ChangeEvent e);
2681     }
2682 
2683     /**
2684      * Added to all the Styles. When instances of this receive a
2685      * stateChanged method, styleChanged is invoked.
2686      */
2687     static class StyleChangeHandler extends AbstractChangeHandler {
2688 
2689         StyleChangeHandler(DefaultStyledDocument d) {
2690             super(d);
2691         }
2692 
2693         void fireStateChanged(DefaultStyledDocument d, ChangeEvent e) {
2694             Object source = e.getSource();
2695             if (source instanceof Style) {
2696                 d.styleChanged((Style) source);
2697             } else {
2698                 d.styleChanged(null);
2699             }
2700         }
2701     }
2702 
2703 
2704     /**
2705      * Added to the StyleContext. When the StyleContext changes, this invokes
2706      * <code>updateStylesListeningTo</code>.
2707      */
2708     static class StyleContextChangeHandler extends AbstractChangeHandler {
2709 
2710         StyleContextChangeHandler(DefaultStyledDocument d) {
2711             super(d);
2712         }
2713 
2714         void fireStateChanged(DefaultStyledDocument d, ChangeEvent e) {
2715             d.updateStylesListeningTo();
2716         }
2717     }
2718 
2719 
2720     /**
2721      * When run this creates a change event for the complete document
2722      * and fires it.
2723      */
2724     class ChangeUpdateRunnable implements Runnable {
2725         boolean isPending = false;
2726 
2727         public void run() {
2728             synchronized(this) {
2729                 isPending = false;
2730             }
2731 
2732             try {
2733                 writeLock();
2734                 DefaultDocumentEvent dde = new DefaultDocumentEvent(0,
2735                                               getLength(),
2736                                               DocumentEvent.EventType.CHANGE);
2737                 dde.end();
2738                 fireChangedUpdate(dde);
2739             } finally {
2740                 writeUnlock();
2741             }
2742         }
2743     }
2744 }