OSDN Git Service

Merged gcj-eclipse branch to trunk.
[pf3gnuchains/gcc-fork.git] / libjava / classpath / javax / swing / text / DefaultStyledDocument.java
1 /* DefaultStyledDocument.java --
2    Copyright (C) 2004, 2005 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38
39 package javax.swing.text;
40
41 import java.awt.Color;
42 import java.awt.Font;
43 import java.io.Serializable;
44 import java.util.ArrayList;
45 import java.util.Enumeration;
46 import java.util.Iterator;
47 import java.util.Stack;
48 import java.util.Vector;
49
50 import javax.swing.event.ChangeEvent;
51 import javax.swing.event.ChangeListener;
52 import javax.swing.event.DocumentEvent;
53 import javax.swing.event.UndoableEditEvent;
54 import javax.swing.undo.AbstractUndoableEdit;
55 import javax.swing.undo.UndoableEdit;
56
57 /**
58  * The default implementation of {@link StyledDocument}. The document is
59  * modeled as an {@link Element} tree, which has a {@link SectionElement} as
60  * single root, which has one or more {@link AbstractDocument.BranchElement}s
61  * as paragraph nodes and each paragraph node having one or more
62  * {@link AbstractDocument.LeafElement}s as content nodes.
63  * 
64  * @author Michael Koch (konqueror@gmx.de)
65  * @author Roman Kennke (roman@kennke.org)
66  */
67 public class DefaultStyledDocument extends AbstractDocument implements
68     StyledDocument
69 {
70
71   /**
72    * An {@link UndoableEdit} that can undo attribute changes to an element.
73    * 
74    * @author Roman Kennke (kennke@aicas.com)
75    */
76   public static class AttributeUndoableEdit extends AbstractUndoableEdit
77   {
78     /**
79      * A copy of the old attributes.
80      */
81     protected AttributeSet copy;
82
83     /**
84      * The new attributes.
85      */
86     protected AttributeSet newAttributes;
87
88     /**
89      * If the new attributes replaced the old attributes or if they only were
90      * added to them.
91      */
92     protected boolean isReplacing;
93
94     /**
95      * The element that has changed.
96      */
97     protected Element element;
98
99     /**
100      * Creates a new <code>AttributeUndoableEdit</code>.
101      * 
102      * @param el
103      *          the element that changes attributes
104      * @param newAtts
105      *          the new attributes
106      * @param replacing
107      *          if the new attributes replace the old or only append to them
108      */
109     public AttributeUndoableEdit(Element el, AttributeSet newAtts,
110                                  boolean replacing)
111     {
112       element = el;
113       newAttributes = newAtts;
114       isReplacing = replacing;
115       copy = el.getAttributes().copyAttributes();
116     }
117
118     /**
119      * Undos the attribute change. The <code>copy</code> field is set as
120      * attributes on <code>element</code>.
121      */
122     public void undo()
123     {
124       super.undo();
125       AttributeSet atts = element.getAttributes();
126       if (atts instanceof MutableAttributeSet)
127         {
128           MutableAttributeSet mutable = (MutableAttributeSet) atts;
129           mutable.removeAttributes(atts);
130           mutable.addAttributes(copy);
131         }
132     }
133
134     /**
135      * Redos an attribute change. This adds <code>newAttributes</code> to the
136      * <code>element</code>'s attribute set, possibly clearing all attributes
137      * if <code>isReplacing</code> is true.
138      */
139     public void redo()
140     {
141       super.undo();
142       AttributeSet atts = element.getAttributes();
143       if (atts instanceof MutableAttributeSet)
144         {
145           MutableAttributeSet mutable = (MutableAttributeSet) atts;
146           if (isReplacing)
147             mutable.removeAttributes(atts);
148           mutable.addAttributes(newAttributes);
149         }
150     }
151   }
152
153   /**
154    * Carries specification information for new {@link Element}s that should be
155    * created in {@link ElementBuffer}. This allows the parsing process to be
156    * decoupled from the <code>Element</code> creation process.
157    */
158   public static class ElementSpec
159   {
160     /**
161      * This indicates a start tag. This is a possible value for {@link #getType}.
162      */
163     public static final short StartTagType = 1;
164
165     /**
166      * This indicates an end tag. This is a possible value for {@link #getType}.
167      */
168     public static final short EndTagType = 2;
169
170     /**
171      * This indicates a content element. This is a possible value for
172      * {@link #getType}.
173      */
174     public static final short ContentType = 3;
175
176     /**
177      * This indicates that the data associated with this spec should be joined
178      * with what precedes it. This is a possible value for {@link #getDirection}.
179      */
180     public static final short JoinPreviousDirection = 4;
181
182     /**
183      * This indicates that the data associated with this spec should be joined
184      * with what follows it. This is a possible value for {@link #getDirection}.
185      */
186     public static final short JoinNextDirection = 5;
187
188     /**
189      * This indicates that the data associated with this spec should be used to
190      * create a new element. This is a possible value for {@link #getDirection}.
191      */
192     public static final short OriginateDirection = 6;
193
194     /**
195      * This indicates that the data associated with this spec should be joined
196      * to the fractured element. This is a possible value for
197      * {@link #getDirection}.
198      */
199     public static final short JoinFractureDirection = 7;
200
201     /**
202      * The type of the tag.
203      */
204     short type;
205
206     /**
207      * The direction of the tag.
208      */
209     short direction;
210
211     /**
212      * The offset of the content.
213      */
214     int offset;
215
216     /**
217      * The length of the content.
218      */
219     int length;
220
221     /**
222      * The actual content.
223      */
224     char[] content;
225
226     /**
227      * The attributes for the tag.
228      */
229     AttributeSet attributes;
230
231     /**
232      * Creates a new <code>ElementSpec</code> with no content, length or
233      * offset. This is most useful for start and end tags.
234      * 
235      * @param a
236      *          the attributes for the element to be created
237      * @param type
238      *          the type of the tag
239      */
240     public ElementSpec(AttributeSet a, short type)
241     {
242       this(a, type, 0);
243     }
244
245     /**
246      * Creates a new <code>ElementSpec</code> that specifies the length but
247      * not the offset of an element. Such <code>ElementSpec</code>s are
248      * processed sequentially from a known starting point.
249      * 
250      * @param a
251      *          the attributes for the element to be created
252      * @param type
253      *          the type of the tag
254      * @param len
255      *          the length of the element
256      */
257     public ElementSpec(AttributeSet a, short type, int len)
258     {
259       this(a, type, null, 0, len);
260     }
261
262     /**
263      * Creates a new <code>ElementSpec</code> with document content.
264      * 
265      * @param a
266      *          the attributes for the element to be created
267      * @param type
268      *          the type of the tag
269      * @param txt
270      *          the actual content
271      * @param offs
272      *          the offset into the <code>txt</code> array
273      * @param len
274      *          the length of the element
275      */
276     public ElementSpec(AttributeSet a, short type, char[] txt, int offs, int len)
277     {
278       attributes = a;
279       this.type = type;
280       offset = offs;
281       length = len;
282       content = txt;
283       direction = OriginateDirection;
284     }
285
286     /**
287      * Sets the type of the element.
288      * 
289      * @param type
290      *          the type of the element to be set
291      */
292     public void setType(short type)
293     {
294       this.type = type;
295     }
296
297     /**
298      * Returns the type of the element.
299      * 
300      * @return the type of the element
301      */
302     public short getType()
303     {
304       return type;
305     }
306
307     /**
308      * Sets the direction of the element.
309      * 
310      * @param dir
311      *          the direction of the element to be set
312      */
313     public void setDirection(short dir)
314     {
315       direction = dir;
316     }
317
318     /**
319      * Returns the direction of the element.
320      * 
321      * @return the direction of the element
322      */
323     public short getDirection()
324     {
325       return direction;
326     }
327
328     /**
329      * Returns the attributes of the element.
330      * 
331      * @return the attributes of the element
332      */
333     public AttributeSet getAttributes()
334     {
335       return attributes;
336     }
337
338     /**
339      * Returns the actual content of the element.
340      * 
341      * @return the actual content of the element
342      */
343     public char[] getArray()
344     {
345       return content;
346     }
347
348     /**
349      * Returns the offset of the content.
350      * 
351      * @return the offset of the content
352      */
353     public int getOffset()
354     {
355       return offset;
356     }
357
358     /**
359      * Returns the length of the content.
360      * 
361      * @return the length of the content
362      */
363     public int getLength()
364     {
365       return length;
366     }
367
368     /**
369      * Returns a String representation of this <code>ElementSpec</code>
370      * describing the type, direction and length of this
371      * <code>ElementSpec</code>.
372      * 
373      * @return a String representation of this <code>ElementSpec</code>
374      */
375     public String toString()
376     {
377       StringBuilder b = new StringBuilder();
378       switch (type)
379         {
380         case StartTagType:
381           b.append("StartTag");
382           break;
383         case EndTagType:
384           b.append("EndTag");
385           break;
386         case ContentType:
387           b.append("Content");
388           break;
389         default:
390           b.append("??");
391           break;
392         }
393
394       b.append(':');
395
396       switch (direction)
397         {
398         case JoinPreviousDirection:
399           b.append("JoinPrevious");
400           break;
401         case JoinNextDirection:
402           b.append("JoinNext");
403           break;
404         case OriginateDirection:
405           b.append("Originate");
406           break;
407         case JoinFractureDirection:
408           b.append("Fracture");
409           break;
410         default:
411           b.append("??");
412           break;
413         }
414
415       b.append(':');
416       b.append(length);
417
418       return b.toString();
419     }
420   }
421
422   /**
423    * Performs all <em>structural</code> changes to the <code>Element</code>
424    * hierarchy.  This class was implemented with much help from the document:
425    * http://java.sun.com/products/jfc/tsc/articles/text/element_buffer/index.html.
426    */
427   public class ElementBuffer implements Serializable
428   {
429     /**
430      * Instance of all editing information for an object in the Vector. This class
431      * is used to add information to the DocumentEvent associated with an
432      * insertion/removal/change as well as to store the changes that need to be
433      * made so they can be made all at the same (appropriate) time.
434      */
435     class Edit
436     {
437       /** The element to edit . */
438       Element e;
439
440       /** The index of the change. */
441       int index;
442
443       /** The removed elements. */
444       ArrayList removed = new ArrayList();
445
446       /** The added elements. */
447       ArrayList added = new ArrayList();
448
449       /**
450        * Indicates if this edit contains a fracture.
451        */
452       boolean isFracture;
453
454       /**
455        * Creates a new Edit for the specified element at index i.
456        *
457        * @param el the element
458        * @param i the index
459        */
460       Edit(Element el, int i)
461       {
462         this(el, i, false);
463       }
464
465       /**
466        * Creates a new Edit for the specified element at index i.
467        *
468        * @param el the element
469        * @param i the index
470        * @param frac if this is a fracture edit or not
471        */
472       Edit(Element el, int i, boolean frac)
473       {
474         e = el;
475         index = i;
476         isFracture = frac;
477       }
478
479     }
480
481     /** The serialization UID (compatible with JDK1.5). */
482     private static final long serialVersionUID = 1688745877691146623L;
483
484     /** The root element of the hierarchy. */
485     private Element root;
486
487     /** Holds the offset for structural changes. */
488     private int offset;
489
490     /** Holds the end offset for structural changes. */
491     private int endOffset;
492
493     /** Holds the length of structural changes. */
494     private int length;
495
496     /** Holds the position of the change. */
497     private int pos;
498
499     /**
500      * The parent of the fracture.
501      */
502     private Element fracturedParent;
503
504     /**
505      * The fractured child.
506      */
507     private Element fracturedChild;
508
509     /**
510      * Indicates if a fracture has been created.
511      */
512     private boolean createdFracture;
513
514     /**
515      * The current position in the element tree. This is used for bulk inserts
516      * using ElementSpecs.
517      */
518     private Stack elementStack;
519
520     private Edit[] insertPath;
521
522     private boolean recreateLeafs;
523
524     /**
525      * Vector that contains all the edits. Maybe replace by a HashMap.
526      */
527     private ArrayList edits;
528
529     private boolean offsetLastIndex;
530     private boolean offsetLastIndexReplace;
531
532     /**
533      * Creates a new <code>ElementBuffer</code> for the specified
534      * <code>root</code> element.
535      * 
536      * @param root
537      *          the root element for this <code>ElementBuffer</code>
538      */
539     public ElementBuffer(Element root)
540     {
541       this.root = root;
542     }
543
544     /**
545      * Returns the root element of this <code>ElementBuffer</code>.
546      * 
547      * @return the root element of this <code>ElementBuffer</code>
548      */
549     public Element getRootElement()
550     {
551       return root;
552     }
553
554     /**
555      * Removes the content. This method sets some internal parameters and
556      * delegates the work to {@link #removeUpdate}.
557      * 
558      * @param offs
559      *          the offset from which content is remove
560      * @param len
561      *          the length of the removed content
562      * @param ev
563      *          the document event that records the changes
564      */
565     public void remove(int offs, int len, DefaultDocumentEvent ev)
566     {
567       prepareEdit(offs, len);
568       removeUpdate();
569       finishEdit(ev);
570     }
571
572     /**
573      * Updates the element structure of the document in response to removal of
574      * content. It removes the affected {@link Element}s from the document
575      * structure.
576      */
577     protected void removeUpdate()
578     {
579       removeElements(root, offset, endOffset);
580     }
581
582     private boolean removeElements(Element elem, int rmOffs0, int rmOffs1)
583     {
584       boolean ret = false; 
585       if (! elem.isLeaf())
586         {
587           // Update stack for changes.
588           int index0 = elem.getElementIndex(rmOffs0);
589           int index1 = elem.getElementIndex(rmOffs1);
590           elementStack.push(new Edit(elem, index0));
591           Edit ec = (Edit) elementStack.peek();
592
593           // If the range is contained by one element,
594           // we just forward the request
595           if (index0 == index1)
596             {
597               Element child0 = elem.getElement(index0);
598               if(rmOffs0 <= child0.getStartOffset()
599                   && rmOffs1 >= child0.getEndOffset())
600                 {
601                   // Element totally removed.
602                   ec.removed.add(child0);
603                 }
604               else if (removeElements(child0, rmOffs0, rmOffs1))
605                 {
606                   ec.removed.add(child0);
607                 }
608             }
609           else
610             {
611               // The removal range spans elements.  If we can join
612               // the two endpoints, do it.  Otherwise we remove the
613               // interior and forward to the endpoints.
614               Element child0 = elem.getElement(index0);
615               Element child1 = elem.getElement(index1);
616               boolean containsOffs1 = (rmOffs1 < elem.getEndOffset());
617           if (containsOffs1 && canJoin(child0, child1))
618             {
619               // Remove and join.
620               for (int i = index0; i <= index1; i++)
621                 {
622                   ec.removed.add(elem.getElement(i));
623                 }
624               Element e = join(elem, child0, child1, rmOffs0, rmOffs1);
625               ec.added.add(e);
626             }
627           else
628             {
629               // Remove interior and forward.
630               int rmIndex0 = index0 + 1;
631               int rmIndex1 = index1 - 1;
632               if (child0.getStartOffset() == rmOffs0
633                   || (index0 == 0 && child0.getStartOffset() > rmOffs0
634                       && child0.getEndOffset() <= rmOffs1))
635                 {
636                   // Start element completely consumed.
637                   child0 = null;
638                   rmIndex0 = index0;
639                 }
640               if (! containsOffs1)
641                 {
642                   child1 = null;
643                   rmIndex1++;
644               }
645               else if (child1.getStartOffset() == rmOffs1)
646                 {
647                   // End element not touched.
648                   child1 = null;
649                 }
650               if (rmIndex0 <= rmIndex1)
651                 {
652                   ec.index = rmIndex0;
653                 }
654               for (int i = rmIndex0; i <= rmIndex1; i++)
655                 {
656                   ec.removed.add(elem.getElement(i));
657                 }
658               if (child0 != null)
659                 {
660                   if(removeElements(child0, rmOffs0, rmOffs1))
661                     {
662                       ec.removed.add(0, child0);
663                       ec.index = index0;
664                     }
665                 }
666               if (child1 != null)
667                 {
668                   if(removeElements(child1, rmOffs0, rmOffs1))
669                     {
670                       ec.removed.add(child1);
671                     }
672                 }
673             }
674             }
675
676           // Perform changes.
677           pop();
678
679           // Return true if we no longer have any children.
680           if(elem.getElementCount() == (ec.removed.size() - ec.added.size()))
681             ret = true;
682         }
683       return ret;
684     }
685
686     /**
687      * Creates a document in response to a call to
688      * {@link DefaultStyledDocument#create(ElementSpec[])}.
689      *
690      * @param len the length of the inserted text
691      * @param data the specs for the elements
692      * @param ev the document event
693      */
694     void create(int len, ElementSpec[] data, DefaultDocumentEvent ev)
695     {
696       prepareEdit(offset, len);
697       Element el = root;
698       int index = el.getElementIndex(0);
699       while (! el.isLeaf())
700         {
701           Element child = el.getElement(index);
702           Edit edit = new Edit(el, index, false);
703           elementStack.push(edit);
704           el = child;
705           index = el.getElementIndex(0);
706         }
707       Edit ed = (Edit) elementStack.peek();
708       Element child = ed.e.getElement(ed.index);
709       ed.added.add(createLeafElement(ed.e, child.getAttributes(), getLength(),
710                                      child.getEndOffset()));
711       ed.removed.add(child);
712       while (elementStack.size() > 1)
713         pop();
714       int n = data.length;
715
716       // Reset root element's attributes.
717       AttributeSet newAtts = null;
718       if (n > 0 && data[0].getType() == ElementSpec.StartTagType)
719         newAtts = data[0].getAttributes();
720       if (newAtts == null)
721         newAtts = SimpleAttributeSet.EMPTY;
722       MutableAttributeSet mAtts = (MutableAttributeSet) root.getAttributes();
723       ev.addEdit(new AttributeUndoableEdit(root, newAtts, true));
724       mAtts.removeAttributes(mAtts);
725       mAtts.addAttributes(newAtts);
726
727       // Insert the specified elements.
728       for (int i = 1; i < n; i++)
729         insertElement(data[i]);
730
731       // Pop remaining stack.
732       while (elementStack.size() > 0)
733         pop();
734
735       finishEdit(ev);
736     }
737
738     private boolean canJoin(Element e0, Element e1)
739     {
740       boolean ret = false;
741       if ((e0 != null) && (e1 != null))
742         {
743           // Don't join a leaf to a branch.
744           boolean isLeaf0 = e0.isLeaf();
745           boolean isLeaf1 = e1.isLeaf();
746           if(isLeaf0 == isLeaf1)
747             {
748               if (isLeaf0)
749                 {
750                   // Only join leaves if the attributes match, otherwise
751                   // style information will be lost.
752                   ret = e0.getAttributes().isEqual(e1.getAttributes());
753                 }
754               else
755                 {
756                   // Only join non-leafs if the names are equal. This may result
757                   // in loss of style information, but this is typically
758                   // acceptable for non-leafs.
759                   String name0 = e0.getName();
760                   String name1 = e1.getName();
761                   if (name0 != null)
762                     ret = name0.equals(name1);
763                   else if (name1 != null)
764                     ret = name1.equals(name0);
765                   else // Both names null.
766                     ret = true;
767                 }
768             }
769         }
770       return ret;
771     }
772
773     private Element join(Element p, Element left, Element right, int rmOffs0,
774                          int rmOffs1)
775     {
776       Element joined = null;
777       if (left.isLeaf() && right.isLeaf())
778         {
779           joined = createLeafElement(p, left.getAttributes(),
780                                      left.getStartOffset(),
781                                      right.getEndOffset());
782         }
783       else if ((! left.isLeaf()) && (! right.isLeaf()))
784         {
785           // Join two branch elements.  This copies the children before
786           // the removal range on the left element, and after the removal
787           // range on the right element.  The two elements on the edge
788           // are joined if possible and needed.
789           joined = createBranchElement(p, left.getAttributes());
790           int ljIndex = left.getElementIndex(rmOffs0);
791           int rjIndex = right.getElementIndex(rmOffs1);
792           Element lj = left.getElement(ljIndex);
793           if (lj.getStartOffset() >= rmOffs0)
794             {
795               lj = null;
796             }
797           Element rj = right.getElement(rjIndex);
798           if (rj.getStartOffset() == rmOffs1)
799             {
800               rj = null;
801             }
802           ArrayList children = new ArrayList();
803           // Transfer the left.
804           for (int i = 0; i < ljIndex; i++)
805             {
806               children.add(clone(joined, left.getElement(i)));
807             }
808
809           // Transfer the join/middle.
810           if (canJoin(lj, rj))
811             {
812               Element e = join(joined, lj, rj, rmOffs0, rmOffs1);
813               children.add(e);
814             }
815           else
816             {
817               if (lj != null)
818                 {
819                   children.add(cloneAsNecessary(joined, lj, rmOffs0, rmOffs1));
820                 }
821               if (rj != null)
822                 {
823                   children.add(cloneAsNecessary(joined, rj, rmOffs0, rmOffs1));
824                 }
825             }
826
827           // Transfer the right.
828           int n = right.getElementCount();
829           for (int i = (rj == null) ? rjIndex : rjIndex + 1; i < n; i++)
830             {
831               children.add(clone(joined, right.getElement(i)));
832             }
833
834           // Install the children.
835           Element[] c = new Element[children.size()];
836           c = (Element[]) children.toArray(c);
837           ((BranchElement) joined).replace(0, 0, c);
838         }
839       else
840         {
841           assert false : "Must not happen";
842         }
843       return joined;
844     }
845
846     /**
847      * Performs the actual work for {@link #change}. The elements at the
848      * interval boundaries are split up (if necessary) so that the interval
849      * boundaries are located at element boundaries.
850      */
851     protected void changeUpdate()
852     {
853       boolean didEnd = split(offset, length);
854       if (! didEnd)
855         {
856           // need to do the other end
857           while (elementStack.size() != 0)
858             {
859               pop();
860             }
861           split(offset + length, 0);
862         }
863       while (elementStack.size() != 0)
864         {
865           pop();
866         }
867     }
868
869     /**
870      * Modifies the element structure so that the specified interval starts and
871      * ends at an element boundary. Content and paragraph elements are split and
872      * created as necessary. This also updates the
873      * <code>DefaultDocumentEvent</code> to reflect the structural changes.
874      * The bulk work is delegated to {@link #changeUpdate()}.
875      * 
876      * @param offset
877      *          the start index of the interval to be changed
878      * @param length
879      *          the length of the interval to be changed
880      * @param ev
881      *          the <code>DefaultDocumentEvent</code> describing the change
882      */
883     public void change(int offset, int length, DefaultDocumentEvent ev)
884     {
885       prepareEdit(offset, length);
886       changeUpdate();
887       finishEdit(ev);
888     }
889
890     /**
891      * Creates and returns a deep clone of the specified <code>clonee</code>
892      * with the specified parent as new parent.
893      *
894      * This method can only clone direct instances of {@link BranchElement}
895      * or {@link LeafElement}.
896      *
897      * @param parent the new parent
898      * @param clonee the element to be cloned
899      *
900      * @return the cloned element with the new parent
901      */
902     public Element clone(Element parent, Element clonee)
903     {
904       Element clone = clonee;
905       // We can only handle AbstractElements here.
906       if (clonee instanceof BranchElement)
907         {
908           BranchElement branchEl = (BranchElement) clonee;
909           BranchElement branchClone =
910             new BranchElement(parent, branchEl.getAttributes());
911           // Also clone all of the children.
912           int numChildren = branchClone.getElementCount();
913           Element[] cloneChildren = new Element[numChildren];
914           for (int i = 0; i < numChildren; ++i)
915             {
916               cloneChildren[i] = clone(branchClone,
917                                        branchClone.getElement(i));
918             }
919           branchClone.replace(0, 0, cloneChildren);
920           clone = branchClone;
921         }
922       else if (clonee instanceof LeafElement)
923         {
924           clone = new LeafElement(parent, clonee.getAttributes(),
925                                   clonee.getStartOffset(),
926                                   clonee.getEndOffset());
927         }
928       return clone;
929     }
930
931     private Element cloneAsNecessary(Element parent, Element clonee,
932                                      int rmOffs0, int rmOffs1)
933     {
934       Element cloned;
935       if (clonee.isLeaf())
936         {
937           cloned = createLeafElement(parent, clonee.getAttributes(),
938                                      clonee.getStartOffset(),
939                                      clonee.getEndOffset());
940         }
941       else
942         {
943           Element e = createBranchElement(parent, clonee.getAttributes());
944           int n = clonee.getElementCount();
945           ArrayList childrenList = new ArrayList(n);
946           for (int i = 0; i < n; i++)
947             {
948               Element elem = clonee.getElement(i);
949               if (elem.getStartOffset() < rmOffs0
950                   || elem.getEndOffset() > rmOffs1)
951                 {
952                   childrenList.add(cloneAsNecessary(e, elem, rmOffs0,
953                                                     rmOffs1));
954                 }
955             }
956           Element[] children = new Element[childrenList.size()];
957           children = (Element[]) childrenList.toArray(children);
958           ((BranchElement) e).replace(0, 0, children);
959           cloned = e;
960         }
961       return cloned;
962     }
963
964     /**
965      * Inserts new <code>Element</code> in the document at the specified
966      * position. Most of the work is done by {@link #insertUpdate}, after some
967      * fields have been prepared for it.
968      * 
969      * @param offset
970      *          the location in the document at which the content is inserted
971      * @param length
972      *          the length of the inserted content
973      * @param data
974      *          the element specifications for the content to be inserted
975      * @param ev
976      *          the document event that is updated to reflect the structural
977      *          changes
978      */
979     public void insert(int offset, int length, ElementSpec[] data,
980                        DefaultDocumentEvent ev)
981     {
982       if (length > 0)
983         {
984           prepareEdit(offset, length);
985           insertUpdate(data);
986           finishEdit(ev);
987         }
988     }
989
990     /**
991      * Prepares the state of this object for performing an insert.
992      *
993      * @param offset the offset at which is inserted
994      * @param length the length of the inserted region
995      */
996     private void prepareEdit(int offset, int length)
997     {
998       this.offset = offset;
999       this.pos = offset;
1000       this.endOffset = offset + length;
1001       this.length = length;
1002
1003       if (edits == null)
1004         edits = new ArrayList();
1005       else
1006         edits.clear();
1007
1008       if (elementStack == null)
1009         elementStack = new Stack();
1010       else
1011         elementStack.clear();
1012
1013       fracturedParent = null;
1014       fracturedChild = null;
1015       offsetLastIndex = false;
1016       offsetLastIndexReplace = false;
1017     }
1018
1019     /**
1020      * Finishes an insert. This applies all changes and updates
1021      * the DocumentEvent.
1022      *
1023      * @param ev the document event
1024      */
1025     private void finishEdit(DefaultDocumentEvent ev)
1026     {
1027       // This for loop applies all the changes that were made and updates the
1028       // DocumentEvent.
1029       for (Iterator i = edits.iterator(); i.hasNext();)
1030         {
1031           Edit edits = (Edit) i.next();
1032           Element[] removed = new Element[edits.removed.size()];
1033           removed = (Element[]) edits.removed.toArray(removed);
1034           Element[] added = new Element[edits.added.size()];
1035           added = (Element[]) edits.added.toArray(added);
1036           int index = edits.index;
1037           BranchElement parent = (BranchElement) edits.e;
1038           parent.replace(index, removed.length, added);
1039           ElementEdit ee = new ElementEdit(parent, index, removed, added);
1040           ev.addEdit(ee);
1041         }
1042       edits.clear();
1043       elementStack.clear();
1044     }
1045
1046     /**
1047      * Inserts new content.
1048      * 
1049      * @param data the element specifications for the elements to be inserted
1050      */
1051     protected void insertUpdate(ElementSpec[] data)
1052     {
1053       // Push the current path to the stack.
1054       Element current = root;
1055       int index = current.getElementIndex(offset);
1056       while (! current.isLeaf())
1057         {
1058           Element child = current.getElement(index);
1059           int editIndex = child.isLeaf() ? index : index + 1;
1060           Edit edit = new Edit(current, editIndex);
1061           elementStack.push(edit);
1062           current = child;
1063           index = current.getElementIndex(offset);
1064         }
1065
1066       // Create a copy of the original path.
1067       insertPath = new Edit[elementStack.size()];
1068       insertPath = (Edit[]) elementStack.toArray(insertPath);
1069
1070       // No fracture yet.
1071       createdFracture = false;
1072
1073       // Insert first content tag.
1074       int i = 0;
1075       recreateLeafs = false;
1076       int type = data[0].getType();
1077       if (type == ElementSpec.ContentType)
1078         {
1079           // If the first tag is content we must treat it separately to allow
1080           // for joining properly to previous Elements and to ensure that
1081           // no extra LeafElements are erroneously inserted.
1082           insertFirstContentTag(data);
1083           pos += data[0].length;
1084           i = 1;
1085         }
1086       else
1087         {
1088           createFracture(data);
1089           i = 0;
1090         }
1091
1092       // Handle each ElementSpec individually.
1093       for (; i < data.length; i++)
1094         {
1095           insertElement(data[i]);
1096         }
1097
1098       // Fracture if we haven't done yet.
1099       if (! createdFracture)
1100         fracture(-1);
1101
1102       // Pop the remaining stack.
1103       while (elementStack.size() != 0)
1104         pop();
1105
1106       // Offset last index if necessary.
1107       if (offsetLastIndex && offsetLastIndexReplace)
1108         insertPath[insertPath.length - 1].index++;
1109
1110       // Make sure we havea an Edit for each path item that has a change.
1111       for (int p = insertPath.length - 1; p >= 0; p--)
1112         {
1113           Edit edit = insertPath[p];
1114           if (edit.e == fracturedParent)
1115             edit.added.add(fracturedChild);
1116           if ((edit.added.size() > 0 || edit.removed.size() > 0)
1117               && ! edits.contains(edit))
1118             edits.add(edit);
1119         }
1120
1121       // Remove element that would be created by an insert at 0 with
1122       // an initial end tag.
1123       if (offset == 0 && fracturedParent != null
1124           && data[0].getType() == ElementSpec.EndTagType)
1125         {
1126           int p;
1127           for (p = 0;
1128                p < data.length && data[p].getType() == ElementSpec.EndTagType;
1129                p++);
1130           Edit edit = insertPath[insertPath.length - p - 1];
1131           edit.index--;
1132           edit.removed.add(0, edit.e.getElement(edit.index));
1133         }
1134     }
1135
1136     private void pop()
1137     {
1138       Edit edit = (Edit) elementStack.peek();
1139       elementStack.pop();
1140       if ((edit.added.size() > 0) || (edit.removed.size() > 0))
1141         {
1142           edits.add(edit);
1143         }
1144       else if (! elementStack.isEmpty())
1145         {
1146           Element e = edit.e;
1147           if (e.getElementCount() == 0)
1148             {
1149               // If we pushed a branch element that didn't get
1150               // used, make sure its not marked as having been added.
1151               edit = (Edit) elementStack.peek();
1152               edit.added.remove(e);
1153           }
1154       }
1155     }
1156
1157     private void insertElement(ElementSpec spec)
1158     {
1159       Edit edit = (Edit) elementStack.peek();
1160       switch (spec.getType())
1161         {
1162         case ElementSpec.StartTagType:
1163           switch (spec.getDirection())
1164             {
1165             case ElementSpec.JoinFractureDirection:
1166               // Fracture the tree and ensure the appropriate element
1167               // is on top of the stack.
1168               if (! createdFracture)
1169                 {
1170                   fracture(elementStack.size() - 1);
1171                 }
1172               if (! edit.isFracture)
1173                 {
1174                   // If the parent isn't a fracture, then the fracture is
1175                   // in fracturedChild.
1176                   Edit newEdit = new Edit(fracturedChild, 0, true);
1177                   elementStack.push(newEdit);
1178                 }
1179               else
1180                 {
1181                   // Otherwise use the parent's first child.
1182                   Element el = edit.e.getElement(0);
1183                   Edit newEdit = new Edit(el, 0, true);
1184                   elementStack.push(newEdit);
1185                 }
1186               break;
1187             case ElementSpec.JoinNextDirection:
1188               // Push the next paragraph element onto the stack so
1189               // future insertions are added to it.
1190               Element parent = edit.e.getElement(edit.index);
1191               if (parent.isLeaf())
1192                 {
1193                   if (edit.index + 1 < edit.e.getElementCount())
1194                     parent = edit.e.getElement(edit.index + 1);
1195                   else
1196                     assert false; // Must not happen.
1197                 }
1198               elementStack.push(new Edit(parent, 0, true));
1199               break;
1200             default:
1201               Element branch = createBranchElement(edit.e,
1202                                                    spec.getAttributes());
1203               edit.added.add(branch);
1204               elementStack.push(new Edit(branch, 0));
1205               break;
1206             }
1207           break;
1208         case ElementSpec.EndTagType:
1209           pop();
1210           break;
1211         case ElementSpec.ContentType:
1212           insertContentTag(spec, edit);
1213           break;
1214         }
1215     }
1216
1217     /**
1218      * Inserts the first tag into the document.
1219      * 
1220      * @param data -
1221      *          the data to be inserted.
1222      */
1223     private void insertFirstContentTag(ElementSpec[] data)
1224     {
1225       ElementSpec first = data[0];
1226       Edit edit = (Edit) elementStack.peek();
1227       Element current = edit.e.getElement(edit.index);
1228       int firstEndOffset = offset + first.length;
1229       boolean onlyContent = data.length == 1;
1230       switch (first.getDirection())
1231         {
1232         case ElementSpec.JoinPreviousDirection:
1233           if (current.getEndOffset() != firstEndOffset && ! onlyContent)
1234             {
1235               Element newEl1 = createLeafElement(edit.e,
1236                                                  current.getAttributes(),
1237                                                  current.getStartOffset(),
1238                                                  firstEndOffset);
1239               edit.added.add(newEl1);
1240               edit.removed.add(current);
1241               if (current.getEndOffset() != endOffset)
1242                 recreateLeafs = true;
1243               else
1244                 offsetLastIndex = true;
1245             }
1246           else
1247             {
1248               offsetLastIndex = true;
1249               offsetLastIndexReplace = true;
1250             }
1251           break;
1252         case ElementSpec.JoinNextDirection:
1253           if (offset != 0)
1254             {
1255               Element newEl1 = createLeafElement(edit.e,
1256                                                  current.getAttributes(),
1257                                                  current.getStartOffset(),
1258                                                  offset);
1259               edit.added.add(newEl1);
1260               Element next = edit.e.getElement(edit.index + 1);
1261               if (onlyContent)
1262                 newEl1 = createLeafElement(edit.e, next.getAttributes(),
1263                                            offset, next.getEndOffset());
1264               else
1265                 {
1266                   newEl1 = createLeafElement(edit.e, next.getAttributes(),
1267                                              offset, firstEndOffset);
1268                 }
1269               edit.added.add(newEl1);
1270               edit.removed.add(current);
1271               edit.removed.add(next);
1272             }
1273           break;
1274         default: // OriginateDirection.
1275           if (current.getStartOffset() != offset)
1276             {
1277               Element newEl = createLeafElement(edit.e,
1278                                                 current.getAttributes(),
1279                                                 current.getStartOffset(),
1280                                                 offset);
1281               edit.added.add(newEl);
1282             }
1283           edit.removed.add(current);
1284           Element newEl1 = createLeafElement(edit.e, first.getAttributes(),
1285                                              offset, firstEndOffset);
1286           edit.added.add(newEl1);
1287           if (current.getEndOffset() != endOffset)
1288             recreateLeafs = true;
1289           else
1290             offsetLastIndex = true;
1291           break;
1292         }
1293     }
1294
1295     /**
1296      * Inserts a content element into the document structure.
1297      * 
1298      * @param tag -
1299      *          the element spec
1300      */
1301     private void insertContentTag(ElementSpec tag, Edit edit)
1302     {
1303       int len = tag.getLength();
1304       int dir = tag.getDirection();
1305       if (dir == ElementSpec.JoinNextDirection)
1306         {
1307           if (! edit.isFracture)
1308             {
1309               Element first = null;
1310               if (insertPath != null)
1311                 {
1312                   for (int p = insertPath.length - 1; p >= 0; p--)
1313                     {
1314                       if (insertPath[p] == edit)
1315                         {
1316                           if (p != insertPath.length - 1)
1317                             first = edit.e.getElement(edit.index);
1318                           break;
1319                         }
1320                     }
1321                 }
1322               if (first == null)
1323                 first = edit.e.getElement(edit.index + 1);
1324               Element leaf = createLeafElement(edit.e, first.getAttributes(),
1325                                                pos, first.getEndOffset());
1326               edit.added.add(leaf);
1327               edit.removed.add(first);
1328             }
1329           else
1330             {
1331               Element first = edit.e.getElement(0);
1332               Element leaf = createLeafElement(edit.e, first.getAttributes(),
1333                                                pos, first.getEndOffset());
1334               edit.added.add(leaf);
1335               edit.removed.add(first);
1336             }
1337         }
1338       else 
1339         {
1340           Element leaf = createLeafElement(edit.e, tag.getAttributes(), pos,
1341                                            pos + len);
1342           edit.added.add(leaf);
1343         }
1344
1345       pos += len;
1346       
1347     }
1348
1349     /**
1350      * This method fractures bottomost leaf in the elementStack. This
1351      * happens when the first inserted tag is not content.
1352      * 
1353      * @param data
1354      *          the ElementSpecs used for the entire insertion
1355      */
1356     private void createFracture(ElementSpec[] data)
1357     {
1358       Edit edit = (Edit) elementStack.peek();
1359       Element child = edit.e.getElement(edit.index);
1360       if (offset != 0)
1361         {
1362           Element newChild = createLeafElement(edit.e, child.getAttributes(),
1363                                                child.getStartOffset(), offset);
1364           edit.added.add(newChild);
1365         }
1366       edit.removed.add(child);
1367       if (child.getEndOffset() != endOffset)
1368         recreateLeafs = true;
1369       else
1370         offsetLastIndex = true;
1371     }
1372
1373     private void fracture(int depth)
1374     {
1375       int len = insertPath.length;
1376       int lastIndex = -1;
1377       boolean recreate = recreateLeafs;
1378       Edit lastEdit = insertPath[len - 1];
1379       boolean childChanged = lastEdit.index + 1 < lastEdit.e.getElementCount();
1380       int deepestChangedIndex = recreate ? len : - 1;
1381       int lastChangedIndex = len - 1;
1382       createdFracture = true;
1383       for (int i = len - 2; i >= 0; i--)
1384         {
1385           Edit edit = insertPath[i];
1386           if (edit.added.size() > 0 || i == depth)
1387             {
1388               lastIndex = i;
1389               if (! recreate && childChanged)
1390                 {
1391                   recreate = true;
1392                   if (deepestChangedIndex == -1)
1393                     deepestChangedIndex = lastChangedIndex + 1;
1394                 }
1395             }
1396           if (! childChanged && edit.index < edit.e.getElementCount())
1397             {
1398               childChanged = true;
1399               lastChangedIndex = i;
1400             }
1401         }
1402       if (recreate)
1403         {
1404           if (lastIndex == -1)
1405             lastIndex = len - 1;
1406           recreate(lastIndex, deepestChangedIndex);
1407         }
1408     }
1409
1410     private void recreate(int startIndex, int endIndex)
1411     {
1412       // Recreate the element representing the inserted index.
1413       Edit edit = insertPath[startIndex];
1414       Element child;
1415       Element newChild;
1416       int changeLength = insertPath.length;
1417
1418       if (startIndex + 1 == changeLength)
1419         child = edit.e.getElement(edit.index);
1420       else
1421         child = edit.e.getElement(edit.index - 1);
1422
1423       if(child.isLeaf())
1424         {
1425           newChild = createLeafElement(edit.e, child.getAttributes(),
1426                                    Math.max(endOffset, child.getStartOffset()),
1427                                    child.getEndOffset());
1428         }
1429       else
1430         {
1431           newChild = createBranchElement(edit.e, child.getAttributes());
1432         }
1433       fracturedParent = edit.e;
1434       fracturedChild = newChild;
1435
1436       // Recreate all the elements to the right of the insertion point.
1437       Element parent = newChild;
1438       while (++startIndex < endIndex)
1439         {
1440           boolean isEnd = (startIndex + 1) == endIndex;
1441           boolean isEndLeaf = (startIndex + 1) == changeLength;
1442
1443           // Create the newChild, a duplicate of the elment at
1444           // index. This isn't done if isEnd and offsetLastIndex are true
1445           // indicating a join previous was done.
1446           edit = insertPath[startIndex];
1447
1448           // Determine the child to duplicate, won't have to duplicate
1449           // if at end of fracture, or offseting index.
1450           if(isEnd)
1451             {
1452               if(offsetLastIndex || ! isEndLeaf)
1453                 child = null;
1454               else
1455                 child = edit.e.getElement(edit.index);
1456             }
1457           else
1458             {
1459               child = edit.e.getElement(edit.index - 1);
1460             }
1461
1462           // Duplicate it.
1463           if(child != null)
1464             {
1465               if(child.isLeaf())
1466                 {
1467                   newChild = createLeafElement(parent, child.getAttributes(),
1468                                    Math.max(endOffset, child.getStartOffset()),
1469                                    child.getEndOffset());
1470                 }
1471               else
1472                 {
1473                   newChild = createBranchElement(parent,
1474                                                  child.getAttributes());
1475                 }
1476             }
1477           else
1478             newChild = null;
1479
1480         // Recreate the remaining children (there may be none).
1481         int childrenToMove = edit.e.getElementCount() - edit.index;
1482         Element[] children;
1483         int moveStartIndex;
1484         int childStartIndex = 1;
1485
1486         if (newChild == null)
1487           {
1488             // Last part of fracture.
1489             if (isEndLeaf)
1490               {
1491                 childrenToMove--;
1492                 moveStartIndex = edit.index + 1;
1493               }
1494             else
1495               {
1496                 moveStartIndex = edit.index;
1497               }
1498             childStartIndex = 0;
1499             children = new Element[childrenToMove];
1500           }
1501         else
1502           {
1503             if (! isEnd)
1504               {
1505                 // Branch.
1506                 childrenToMove++;
1507                 moveStartIndex = edit.index;
1508             }
1509             else
1510               {
1511                 // Last leaf, need to recreate part of it.
1512                 moveStartIndex = edit.index + 1;
1513               }
1514             children = new Element[childrenToMove];
1515             children[0] = newChild;
1516         }
1517
1518         for (int c = childStartIndex; c < childrenToMove; c++)
1519           {
1520             Element toMove = edit.e.getElement(moveStartIndex++);
1521             children[c] = recreateFracturedElement(parent, toMove);
1522             edit.removed.add(toMove);
1523           }
1524         ((BranchElement) parent).replace(0, 0, children);
1525         parent = newChild;
1526       }
1527
1528     }
1529
1530     private Element recreateFracturedElement(Element parent, Element toCopy)
1531     {
1532       Element recreated;
1533       if(toCopy.isLeaf())
1534         {
1535           recreated = createLeafElement(parent, toCopy.getAttributes(),
1536                                   Math.max(toCopy.getStartOffset(), endOffset),
1537                                   toCopy.getEndOffset());
1538         }
1539       else
1540         {
1541           Element newParent = createBranchElement(parent,
1542                                                   toCopy.getAttributes());
1543           int childCount = toCopy.getElementCount();
1544           Element[] newChildren = new Element[childCount];
1545           for (int i = 0; i < childCount; i++)
1546             {
1547               newChildren[i] = recreateFracturedElement(newParent,
1548                                                         toCopy.getElement(i));
1549             }
1550           ((BranchElement) newParent).replace(0, 0, newChildren);
1551           recreated = newParent;
1552         }
1553       return recreated;
1554     }
1555
1556     private boolean split(int offs, int len)
1557     {
1558       boolean splitEnd = false;
1559       // Push the path to the stack.
1560       Element e = root;
1561       int index = e.getElementIndex(offs);
1562       while (! e.isLeaf())
1563         {
1564           elementStack.push(new Edit(e, index));
1565           e = e.getElement(index);
1566           index = e.getElementIndex(offs);
1567         }
1568
1569       Edit ec = (Edit) elementStack.peek();
1570       Element child = ec.e.getElement(ec.index);
1571       // Make sure there is something to do. If the
1572       // offset is already at a boundary then there is
1573       // nothing to do.
1574       if (child.getStartOffset() < offs && offs < child.getEndOffset())
1575         {
1576           // We need to split, now see if the other end is within
1577           // the same parent.
1578           int index0 = ec.index;
1579           int index1 = index0;
1580           if (((offs + len) < ec.e.getEndOffset()) && (len != 0))
1581             {
1582               // It's a range split in the same parent.
1583               index1 = ec.e.getElementIndex(offs+len);
1584               if (index1 == index0)
1585                 {
1586                   // It's a three-way split.
1587                   ec.removed.add(child);
1588                   e = createLeafElement(ec.e, child.getAttributes(),
1589                                         child.getStartOffset(), offs);
1590                   ec.added.add(e);
1591                   e = createLeafElement(ec.e, child.getAttributes(),
1592                                         offs, offs + len);
1593                   ec.added.add(e);
1594                   e = createLeafElement(ec.e, child.getAttributes(),
1595                                         offs + len, child.getEndOffset());
1596                   ec.added.add(e);
1597                   return true;
1598                 }
1599               else
1600                 {
1601                   child = ec.e.getElement(index1);
1602                   if ((offs + len) == child.getStartOffset())
1603                     {
1604                       // End is already on a boundary.
1605                       index1 = index0;
1606                     }
1607                 }
1608               splitEnd = true;
1609             }
1610
1611           // Split the first location.
1612           pos = offs;
1613           child = ec.e.getElement(index0);
1614           ec.removed.add(child);
1615           e = createLeafElement(ec.e, child.getAttributes(),
1616                                 child.getStartOffset(), pos);
1617           ec.added.add(e);
1618           e = createLeafElement(ec.e, child.getAttributes(),
1619                                 pos, child.getEndOffset());
1620           ec.added.add(e);
1621
1622           // Pick up things in the middle.
1623           for (int i = index0 + 1; i < index1; i++)
1624             {
1625               child = ec.e.getElement(i);
1626               ec.removed.add(child);
1627               ec.added.add(child);
1628             }
1629
1630           if (index1 != index0)
1631             {
1632               child = ec.e.getElement(index1);
1633               pos = offs + len;
1634               ec.removed.add(child);
1635               e = createLeafElement(ec.e, child.getAttributes(),
1636                                     child.getStartOffset(), pos);
1637               ec.added.add(e);
1638               e = createLeafElement(ec.e, child.getAttributes(),
1639                                     pos, child.getEndOffset());
1640               
1641               ec.added.add(e);
1642             }
1643         }
1644       return splitEnd;
1645       
1646     }
1647
1648   }
1649
1650
1651   /**
1652    * An element type for sections. This is a simple BranchElement with a unique
1653    * name.
1654    */
1655   protected class SectionElement extends BranchElement
1656   {
1657     /**
1658      * Creates a new SectionElement.
1659      */
1660     public SectionElement()
1661     {
1662       super(null, null);
1663     }
1664
1665     /**
1666      * Returns the name of the element. This method always returns
1667      * &quot;section&quot;.
1668      * 
1669      * @return the name of the element
1670      */
1671     public String getName()
1672     {
1673       return SectionElementName;
1674     }
1675   }
1676
1677   /**
1678    * Receives notification when any of the document's style changes and calls
1679    * {@link DefaultStyledDocument#styleChanged(Style)}.
1680    * 
1681    * @author Roman Kennke (kennke@aicas.com)
1682    */
1683   private class StyleChangeListener implements ChangeListener
1684   {
1685
1686     /**
1687      * Receives notification when any of the document's style changes and calls
1688      * {@link DefaultStyledDocument#styleChanged(Style)}.
1689      * 
1690      * @param event
1691      *          the change event
1692      */
1693     public void stateChanged(ChangeEvent event)
1694     {
1695       Style style = (Style) event.getSource();
1696       styleChanged(style);
1697     }
1698   }
1699
1700   /** The serialization UID (compatible with JDK1.5). */
1701   private static final long serialVersionUID = 940485415728614849L;
1702
1703   /**
1704    * The default size to use for new content buffers.
1705    */
1706   public static final int BUFFER_SIZE_DEFAULT = 4096;
1707
1708   /**
1709    * The <code>EditorBuffer</code> that is used to manage to
1710    * <code>Element</code> hierarchy.
1711    */
1712   protected DefaultStyledDocument.ElementBuffer buffer;
1713
1714   /**
1715    * Listens for changes on this document's styles and notifies styleChanged().
1716    */
1717   private StyleChangeListener styleChangeListener;
1718
1719   /**
1720    * Creates a new <code>DefaultStyledDocument</code>.
1721    */
1722   public DefaultStyledDocument()
1723   {
1724     this(new GapContent(BUFFER_SIZE_DEFAULT), new StyleContext());
1725   }
1726
1727   /**
1728    * Creates a new <code>DefaultStyledDocument</code> that uses the specified
1729    * {@link StyleContext}.
1730    * 
1731    * @param context
1732    *          the <code>StyleContext</code> to use
1733    */
1734   public DefaultStyledDocument(StyleContext context)
1735   {
1736     this(new GapContent(BUFFER_SIZE_DEFAULT), context);
1737   }
1738
1739   /**
1740    * Creates a new <code>DefaultStyledDocument</code> that uses the specified
1741    * {@link StyleContext} and {@link Content} buffer.
1742    * 
1743    * @param content
1744    *          the <code>Content</code> buffer to use
1745    * @param context
1746    *          the <code>StyleContext</code> to use
1747    */
1748   public DefaultStyledDocument(AbstractDocument.Content content,
1749                                StyleContext context)
1750   {
1751     super(content, context);
1752     buffer = new ElementBuffer(createDefaultRoot());
1753     setLogicalStyle(0, context.getStyle(StyleContext.DEFAULT_STYLE));
1754   }
1755
1756   /**
1757    * Adds a style into the style hierarchy. Unspecified style attributes can be
1758    * resolved in the <code>parent</code> style, if one is specified. While it
1759    * is legal to add nameless styles (<code>nm == null</code),
1760    * you must be aware that the client application is then responsible
1761    * for managing the style hierarchy, since unnamed styles cannot be
1762    * looked up by their name.
1763    *
1764    * @param nm the name of the style or <code>null</code> if the style should
1765    *           be unnamed
1766    * @param parent the parent in which unspecified style attributes are
1767    *           resolved, or <code>null</code> if that is not necessary
1768    *
1769    * @return the newly created <code>Style</code>
1770    */
1771   public Style addStyle(String nm, Style parent)
1772   {
1773     StyleContext context = (StyleContext) getAttributeContext();
1774     Style newStyle = context.addStyle(nm, parent);
1775
1776     // Register change listener.
1777     if (styleChangeListener == null)
1778       styleChangeListener = new StyleChangeListener();
1779     newStyle.addChangeListener(styleChangeListener);
1780
1781     return newStyle;
1782   }
1783
1784   /**
1785    * Create the default root element for this kind of <code>Document</code>.
1786    * 
1787    * @return the default root element for this kind of <code>Document</code>
1788    */
1789   protected AbstractDocument.AbstractElement createDefaultRoot()
1790   {
1791     Element[] tmp;
1792     SectionElement section = new SectionElement();
1793
1794     BranchElement paragraph = new BranchElement(section, null);
1795     tmp = new Element[1];
1796     tmp[0] = paragraph;
1797     section.replace(0, 0, tmp);
1798
1799     Element leaf = new LeafElement(paragraph, null, 0, 1);
1800     tmp = new Element[1];
1801     tmp[0] = leaf;
1802     paragraph.replace(0, 0, tmp);
1803
1804     return section;
1805   }
1806
1807   /**
1808    * Returns the <code>Element</code> that corresponds to the character at the
1809    * specified position.
1810    * 
1811    * @param position
1812    *          the position of which we query the corresponding
1813    *          <code>Element</code>
1814    * @return the <code>Element</code> that corresponds to the character at the
1815    *         specified position
1816    */
1817   public Element getCharacterElement(int position)
1818   {
1819     Element element = getDefaultRootElement();
1820
1821     while (!element.isLeaf())
1822       {
1823         int index = element.getElementIndex(position);
1824         element = element.getElement(index);
1825       }
1826
1827     return element;
1828   }
1829
1830   /**
1831    * Extracts a background color from a set of attributes.
1832    * 
1833    * @param attributes
1834    *          the attributes from which to get a background color
1835    * @return the background color that correspond to the attributes
1836    */
1837   public Color getBackground(AttributeSet attributes)
1838   {
1839     StyleContext context = (StyleContext) getAttributeContext();
1840     return context.getBackground(attributes);
1841   }
1842
1843   /**
1844    * Returns the default root element.
1845    * 
1846    * @return the default root element
1847    */
1848   public Element getDefaultRootElement()
1849   {
1850     return buffer.getRootElement();
1851   }
1852
1853   /**
1854    * Extracts a font from a set of attributes.
1855    * 
1856    * @param attributes
1857    *          the attributes from which to get a font
1858    * @return the font that correspond to the attributes
1859    */
1860   public Font getFont(AttributeSet attributes)
1861   {
1862     StyleContext context = (StyleContext) getAttributeContext();
1863     return context.getFont(attributes);
1864   }
1865
1866   /**
1867    * Extracts a foreground color from a set of attributes.
1868    * 
1869    * @param attributes
1870    *          the attributes from which to get a foreground color
1871    * @return the foreground color that correspond to the attributes
1872    */
1873   public Color getForeground(AttributeSet attributes)
1874   {
1875     StyleContext context = (StyleContext) getAttributeContext();
1876     return context.getForeground(attributes);
1877   }
1878
1879   /**
1880    * Returns the logical <code>Style</code> for the specified position.
1881    * 
1882    * @param position
1883    *          the position from which to query to logical style
1884    * @return the logical <code>Style</code> for the specified position
1885    */
1886   public Style getLogicalStyle(int position)
1887   {
1888     Element paragraph = getParagraphElement(position);
1889     AttributeSet attributes = paragraph.getAttributes();
1890     AttributeSet a = attributes.getResolveParent();
1891     // If the resolve parent is not of type Style, we return null.
1892     if (a instanceof Style)
1893       return (Style) a;
1894     return null;
1895   }
1896
1897   /**
1898    * Returns the paragraph element for the specified position. If the position
1899    * is outside the bounds of the document's root element, then the closest
1900    * element is returned. That is the last paragraph if
1901    * <code>position >= endIndex</code> or the first paragraph if
1902    * <code>position < startIndex</code>.
1903    * 
1904    * @param position
1905    *          the position for which to query the paragraph element
1906    * @return the paragraph element for the specified position
1907    */
1908   public Element getParagraphElement(int position)
1909   {
1910     Element e = getDefaultRootElement();
1911     while (!e.isLeaf())
1912       e = e.getElement(e.getElementIndex(position));
1913
1914     if (e != null)
1915       return e.getParentElement();
1916     return e;
1917   }
1918
1919   /**
1920    * Looks up and returns a named <code>Style</code>.
1921    * 
1922    * @param nm
1923    *          the name of the <code>Style</code>
1924    * @return the found <code>Style</code> of <code>null</code> if no such
1925    *         <code>Style</code> exists
1926    */
1927   public Style getStyle(String nm)
1928   {
1929     StyleContext context = (StyleContext) getAttributeContext();
1930     return context.getStyle(nm);
1931   }
1932
1933   /**
1934    * Removes a named <code>Style</code> from the style hierarchy.
1935    * 
1936    * @param nm
1937    *          the name of the <code>Style</code> to be removed
1938    */
1939   public void removeStyle(String nm)
1940   {
1941     StyleContext context = (StyleContext) getAttributeContext();
1942     context.removeStyle(nm);
1943   }
1944
1945   /**
1946    * Sets text attributes for the fragment specified by <code>offset</code>
1947    * and <code>length</code>.
1948    * 
1949    * @param offset
1950    *          the start offset of the fragment
1951    * @param length
1952    *          the length of the fragment
1953    * @param attributes
1954    *          the text attributes to set
1955    * @param replace
1956    *          if <code>true</code>, the attributes of the current selection
1957    *          are overridden, otherwise they are merged
1958    */
1959   public void setCharacterAttributes(int offset, int length,
1960                                      AttributeSet attributes, boolean replace)
1961   {
1962     // Exit early if length is 0, so no DocumentEvent is created or fired.
1963     if (length == 0)
1964       return;
1965     try
1966       {
1967         // Must obtain a write lock for this method. writeLock() and
1968         // writeUnlock() should always be in try/finally block to make
1969         // sure that locking happens in a balanced manner.
1970         writeLock();
1971         DefaultDocumentEvent ev = new DefaultDocumentEvent(offset,
1972                                                            length,
1973                                                            DocumentEvent.EventType.CHANGE);
1974
1975         // Modify the element structure so that the interval begins at an
1976         // element
1977         // start and ends at an element end.
1978         buffer.change(offset, length, ev);
1979
1980         // Visit all paragraph elements within the specified interval
1981         int end = offset + length;
1982         Element curr;
1983         for (int pos = offset; pos < end;)
1984           {
1985             // Get the CharacterElement at offset pos.
1986             curr = getCharacterElement(pos);
1987             if (pos == curr.getEndOffset())
1988               break;
1989
1990             MutableAttributeSet a = (MutableAttributeSet) curr.getAttributes();
1991             ev.addEdit(new AttributeUndoableEdit(curr, attributes, replace));
1992             // If replace is true, remove all the old attributes.
1993             if (replace)
1994               a.removeAttributes(a);
1995             // Add all the new attributes.
1996             a.addAttributes(attributes);
1997             // Increment pos so we can check the next CharacterElement.
1998             pos = curr.getEndOffset();
1999           }
2000         fireChangedUpdate(ev);
2001         fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
2002       }
2003     finally
2004       {
2005         writeUnlock();
2006       }
2007   }
2008
2009   /**
2010    * Sets the logical style for the paragraph at the specified position.
2011    * 
2012    * @param position
2013    *          the position at which the logical style is added
2014    * @param style
2015    *          the style to set for the current paragraph
2016    */
2017   public void setLogicalStyle(int position, Style style)
2018   {
2019     Element el = getParagraphElement(position);
2020     // getParagraphElement doesn't return null but subclasses might so
2021     // we check for null here.
2022     if (el == null)
2023       return;
2024     try
2025       {
2026         writeLock();
2027         if (el instanceof AbstractElement)
2028           {
2029             AbstractElement ael = (AbstractElement) el;
2030             ael.setResolveParent(style);
2031             int start = el.getStartOffset();
2032             int end = el.getEndOffset();
2033             DefaultDocumentEvent ev = new DefaultDocumentEvent(start,
2034                                                                end - start,
2035                                                                DocumentEvent.EventType.CHANGE);
2036             fireChangedUpdate(ev);
2037             fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
2038           }
2039         else
2040           throw new AssertionError(
2041                                    "paragraph elements are expected to be"
2042                                        + "instances of AbstractDocument.AbstractElement");
2043       }
2044     finally
2045       {
2046         writeUnlock();
2047       }
2048   }
2049
2050   /**
2051    * Sets text attributes for the paragraph at the specified fragment.
2052    * 
2053    * @param offset
2054    *          the beginning of the fragment
2055    * @param length
2056    *          the length of the fragment
2057    * @param attributes
2058    *          the text attributes to set
2059    * @param replace
2060    *          if <code>true</code>, the attributes of the current selection
2061    *          are overridden, otherwise they are merged
2062    */
2063   public void setParagraphAttributes(int offset, int length,
2064                                      AttributeSet attributes, boolean replace)
2065   {
2066     try
2067       {
2068         // Must obtain a write lock for this method. writeLock() and
2069         // writeUnlock() should always be in try/finally blocks to make
2070         // sure that locking occurs in a balanced manner.
2071         writeLock();
2072
2073         // Create a DocumentEvent to use for changedUpdate().
2074         DefaultDocumentEvent ev = new DefaultDocumentEvent(offset,
2075                                                            length,
2076                                                            DocumentEvent.EventType.CHANGE);
2077
2078         // Have to iterate through all the _paragraph_ elements that are
2079         // contained or partially contained in the interval
2080         // (offset, offset + length).
2081         Element rootElement = getDefaultRootElement();
2082         int startElement = rootElement.getElementIndex(offset);
2083         int endElement = rootElement.getElementIndex(offset + length - 1);
2084         if (endElement < startElement)
2085           endElement = startElement;
2086
2087         for (int i = startElement; i <= endElement; i++)
2088           {
2089             Element par = rootElement.getElement(i);
2090             MutableAttributeSet a = (MutableAttributeSet) par.getAttributes();
2091             // Add the change to the DocumentEvent.
2092             ev.addEdit(new AttributeUndoableEdit(par, attributes, replace));
2093             // If replace is true remove the old attributes.
2094             if (replace)
2095               a.removeAttributes(a);
2096             // Add the new attributes.
2097             a.addAttributes(attributes);
2098           }
2099         fireChangedUpdate(ev);
2100         fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
2101       }
2102     finally
2103       {
2104         writeUnlock();
2105       }
2106   }
2107
2108   /**
2109    * Called in response to content insert actions. This is used to update the
2110    * element structure.
2111    * 
2112    * @param ev
2113    *          the <code>DocumentEvent</code> describing the change
2114    * @param attr
2115    *          the attributes for the change
2116    */
2117   protected void insertUpdate(DefaultDocumentEvent ev, AttributeSet attr)
2118   {
2119     int offs = ev.getOffset();
2120     int len = ev.getLength();
2121     int endOffs = offs + len;
2122     if (attr == null)
2123       attr = SimpleAttributeSet.EMPTY;
2124
2125     // Paragraph attributes are fetched from the point _after_ the insertion.
2126     Element paragraph = getParagraphElement(endOffs);
2127     AttributeSet pAttr = paragraph.getAttributes();
2128     // Character attributes are fetched from the actual insertion point.
2129     Element paragraph2 = getParagraphElement(offs);
2130     int contIndex = paragraph2.getElementIndex(offs);
2131     Element content = paragraph2.getElement(contIndex);
2132     AttributeSet cAttr = content.getAttributes();
2133
2134     boolean insertAtBoundary = content.getEndOffset() == endOffs;
2135     try
2136       {
2137         Segment s = new Segment();
2138         ArrayList buf = new ArrayList();
2139         ElementSpec lastStartTag = null;
2140         boolean insertAfterNewline = false;
2141         short lastStartDir = ElementSpec.OriginateDirection;
2142
2143         // Special handle if we are inserting after a newline.
2144         if (offs > 0)
2145           {
2146             getText(offs - 1, 1, s);
2147             if (s.array[s.offset] == '\n')
2148               {
2149                 insertAfterNewline = true;
2150                 lastStartDir = insertAfterNewline(paragraph, paragraph2,
2151                                                   pAttr, buf, offs,
2152                                                   endOffs);
2153                 // Search last start tag.
2154                 for (int i = buf.size() - 1; i >= 0 && lastStartTag == null;
2155                      i--)
2156                   {
2157                     ElementSpec tag = (ElementSpec) buf.get(i);
2158                     if (tag.getType() == ElementSpec.StartTagType)
2159                       {
2160                         lastStartTag = tag;
2161                       }
2162                   }
2163               }
2164
2165           }
2166
2167         // If we are not inserting after a newline, the paragraph attributes
2168         // come from the paragraph under the insertion point.
2169         if (! insertAfterNewline)
2170           pAttr = paragraph2.getAttributes();
2171
2172         // Scan text and build up the specs.
2173         getText(offs, len, s);
2174         int end = s.offset + s.count;
2175         int last = s.offset;
2176         for (int i = s.offset; i < end; i++)
2177           {
2178             if (s.array[i] == '\n')
2179               {
2180                 int breakOffs = i + 1;
2181                 buf.add(new ElementSpec(attr, ElementSpec.ContentType,
2182                                         breakOffs - last));
2183                 buf.add(new ElementSpec(null, ElementSpec.EndTagType));
2184                 lastStartTag = new ElementSpec(pAttr,
2185                                                ElementSpec.StartTagType);
2186                 buf.add(lastStartTag);
2187                 last = breakOffs;
2188               }
2189           }
2190
2191         // Need to add a tailing content tag if we didn't finish at a boundary.
2192         if (last < end)
2193           {
2194             buf.add(new ElementSpec(attr, ElementSpec.ContentType,
2195                                     end - last));
2196           }
2197
2198         // Now we need to fix up the directions of the specs.
2199         ElementSpec first = (ElementSpec) buf.get(0);
2200         int doclen = getLength();
2201
2202         // Maybe join-previous the first tag if it is content and has
2203         // the same attributes as the previous character run.
2204         if (first.getType() == ElementSpec.ContentType && cAttr.isEqual(attr))
2205           first.setDirection(ElementSpec.JoinPreviousDirection);
2206
2207         // Join-fracture or join-next the last start tag if necessary.
2208         if (lastStartTag != null)
2209           {
2210             if (insertAfterNewline)
2211               lastStartTag.setDirection(lastStartDir);
2212             else if (paragraph2.getEndOffset() != endOffs)
2213               lastStartTag.setDirection(ElementSpec.JoinFractureDirection);
2214             else
2215               {
2216                 Element par = paragraph2.getParentElement();
2217                 int par2Index = par.getElementIndex(offs);
2218                 if (par2Index + 1 < par.getElementCount()
2219                     && ! par.getElement(par2Index + 1).isLeaf())
2220                   lastStartTag.setDirection(ElementSpec.JoinNextDirection);
2221               }
2222           }
2223
2224         // Join-next last tag if possible.
2225         if (insertAtBoundary && endOffs < doclen)
2226           {
2227             ElementSpec lastTag = (ElementSpec) buf.get(buf.size() - 1);
2228             if (lastTag.getType() == ElementSpec.ContentType
2229                 && ((lastStartTag == null
2230                      && (paragraph == paragraph2 || insertAfterNewline))
2231                     || (lastStartTag != null
2232              && lastStartTag.getDirection() != ElementSpec.OriginateDirection)))
2233               {
2234                 int nextIndex = paragraph.getElementIndex(endOffs);
2235                 Element nextRun = paragraph.getElement(nextIndex);
2236                 if (nextRun.isLeaf() && attr.isEqual(nextRun.getAttributes()))
2237                   lastTag.setDirection(ElementSpec.JoinNextDirection);
2238               }
2239           }
2240
2241         else if (! insertAtBoundary && lastStartTag != null
2242            && lastStartTag.getDirection() == ElementSpec.JoinFractureDirection)
2243           {
2244             ElementSpec lastTag = (ElementSpec) buf.get(buf.size() - 1);
2245             if (lastTag.getType() == ElementSpec.ContentType
2246                 && lastTag.getDirection() != ElementSpec.JoinPreviousDirection
2247                 && attr.isEqual(cAttr))
2248               {
2249                 lastTag.setDirection(ElementSpec.JoinNextDirection);
2250               }
2251           }
2252
2253         ElementSpec[] specs = new ElementSpec[buf.size()];
2254         specs = (ElementSpec[]) buf.toArray(specs);
2255         buffer.insert(offs, len, specs, ev);
2256       }
2257     catch (BadLocationException ex)
2258       {
2259         // Ignore this. Comment out for debugging.
2260         ex.printStackTrace();
2261       }
2262     super.insertUpdate(ev, attr);
2263   }
2264
2265   private short insertAfterNewline(Element par1, Element par2,
2266                                    AttributeSet attr, ArrayList buf,
2267                                    int offs, int endOffs)
2268   {
2269     short dir = 0;
2270     if (par1.getParentElement() == par2.getParentElement())
2271       {
2272         ElementSpec tag = new ElementSpec(attr, ElementSpec.EndTagType);
2273         buf.add(tag);
2274         tag = new ElementSpec(attr, ElementSpec.StartTagType);
2275         buf.add(tag);
2276         if (par2.getEndOffset() != endOffs)
2277           dir = ElementSpec.JoinFractureDirection;
2278         else
2279           {
2280             Element par = par2.getParentElement();
2281             if (par.getElementIndex(offs) + 1 < par.getElementCount())
2282               dir = ElementSpec.JoinNextDirection;
2283           }
2284       }
2285     else
2286       {
2287         // For text with more than 2 levels, find the common parent of
2288         // par1 and par2.
2289         ArrayList parentsLeft = new ArrayList();
2290         ArrayList parentsRight = new ArrayList();
2291         Element e = par2;
2292         while (e != null)
2293           {
2294             parentsLeft.add(e);
2295             e = e.getParentElement();
2296           }
2297         e = par1;
2298         int leftIndex = -1;
2299         while (e != null && (leftIndex = parentsLeft.indexOf(e)) == 1)
2300           {
2301             parentsRight.add(e);
2302             e = e.getParentElement();
2303           }
2304
2305         if (e != null)
2306        
2307           {
2308             // e is now the common parent.
2309             // Insert the end tags.
2310             for (int c = 0; c < leftIndex; c++)
2311               {
2312                 buf.add(new ElementSpec(null, ElementSpec.EndTagType));
2313               }
2314             // Insert the start tags.
2315             for (int c = parentsRight.size() - 1; c >= 0; c--)
2316               {
2317                 Element el = (Element) parentsRight.get(c);
2318                 ElementSpec tag = new ElementSpec(el.getAttributes(),
2319                                                   ElementSpec.StartTagType);
2320                 if (c > 0)
2321                   tag.setDirection(ElementSpec.JoinNextDirection);
2322                 buf.add(tag);
2323               }
2324             if (parentsRight.size() > 0)
2325               dir = ElementSpec.JoinNextDirection;
2326             else
2327               dir = ElementSpec.JoinFractureDirection;
2328           }
2329         else
2330           assert false;
2331       }
2332     return dir;
2333   }
2334
2335   /**
2336    * A helper method to set up the ElementSpec buffer for the special case of an
2337    * insertion occurring immediately after a newline.
2338    * 
2339    * @param specs
2340    *          the ElementSpec buffer to initialize.
2341    */
2342   short handleInsertAfterNewline(Vector specs, int offset, int endOffset,
2343                                  Element prevParagraph, Element paragraph,
2344                                  AttributeSet a)
2345   {
2346     if (prevParagraph.getParentElement() == paragraph.getParentElement())
2347       {
2348         specs.add(new ElementSpec(a, ElementSpec.EndTagType));
2349         specs.add(new ElementSpec(a, ElementSpec.StartTagType));
2350         if (paragraph.getStartOffset() != endOffset)
2351           return ElementSpec.JoinFractureDirection;
2352         // If there is an Element after this one, use JoinNextDirection.
2353         Element parent = paragraph.getParentElement();
2354         if (parent.getElementCount() > (parent.getElementIndex(offset) + 1))
2355           return ElementSpec.JoinNextDirection;
2356       }
2357     return ElementSpec.OriginateDirection;
2358   }
2359
2360   /**
2361    * Updates the document structure in response to text removal. This is
2362    * forwarded to the {@link ElementBuffer} of this document. Any changes to the
2363    * document structure are added to the specified document event and sent to
2364    * registered listeners.
2365    * 
2366    * @param ev
2367    *          the document event that records the changes to the document
2368    */
2369   protected void removeUpdate(DefaultDocumentEvent ev)
2370   {
2371     super.removeUpdate(ev);
2372     buffer.remove(ev.getOffset(), ev.getLength(), ev);
2373   }
2374
2375   /**
2376    * Returns an enumeration of all style names.
2377    * 
2378    * @return an enumeration of all style names
2379    */
2380   public Enumeration<?> getStyleNames()
2381   {
2382     StyleContext context = (StyleContext) getAttributeContext();
2383     return context.getStyleNames();
2384   }
2385
2386   /**
2387    * Called when any of this document's styles changes.
2388    * 
2389    * @param style
2390    *          the style that changed
2391    */
2392   protected void styleChanged(Style style)
2393   {
2394     // Nothing to do here. This is intended to be overridden by subclasses.
2395   }
2396
2397   /**
2398    * Inserts a bulk of structured content at once.
2399    * 
2400    * @param offset
2401    *          the offset at which the content should be inserted
2402    * @param data
2403    *          the actual content spec to be inserted
2404    */
2405   protected void insert(int offset, ElementSpec[] data)
2406       throws BadLocationException
2407   {
2408     if (data == null || data.length == 0)
2409       return;
2410     try
2411       {
2412         // writeLock() and writeUnlock() should always be in a try/finally
2413         // block so that locking balance is guaranteed even if some
2414         // exception is thrown.
2415         writeLock();
2416
2417         // First we collect the content to be inserted.
2418         StringBuffer contentBuffer = new StringBuffer();
2419         for (int i = 0; i < data.length; i++)
2420           {
2421             // Collect all inserts into one so we can get the correct
2422             // ElementEdit
2423             ElementSpec spec = data[i];
2424             if (spec.getArray() != null && spec.getLength() > 0)
2425               contentBuffer.append(spec.getArray(), spec.getOffset(),
2426                                    spec.getLength());
2427           }
2428
2429         int length = contentBuffer.length();
2430
2431         // If there was no content inserted then exit early.
2432         if (length == 0)
2433           return;
2434
2435         Content c = getContent();
2436         UndoableEdit edit = c.insertString(offset,
2437                                            contentBuffer.toString());
2438
2439         // Create the DocumentEvent with the ElementEdit added
2440         DefaultDocumentEvent ev = new DefaultDocumentEvent(offset,
2441                                                            length,
2442                                                            DocumentEvent.EventType.INSERT);
2443
2444         ev.addEdit(edit);
2445
2446         // Finally we must update the document structure and fire the insert
2447         // update event.
2448         buffer.insert(offset, length, data, ev);
2449
2450         super.insertUpdate(ev, null);
2451
2452         ev.end();
2453         fireInsertUpdate(ev);
2454         fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
2455       }
2456     finally
2457       {
2458         writeUnlock();
2459       }
2460   }
2461
2462   /**
2463    * Initializes the <code>DefaultStyledDocument</code> with the specified
2464    * data.
2465    * 
2466    * @param data
2467    *          the specification of the content with which the document is
2468    *          initialized
2469    */
2470   protected void create(ElementSpec[] data)
2471   {
2472     try
2473       {
2474
2475         // Clear content if there is some.
2476         int len = getLength();
2477         if (len > 0)
2478           remove(0, len);
2479
2480         writeLock();
2481
2482         // Now we insert the content.
2483         StringBuilder b = new StringBuilder();
2484         for (int i = 0; i < data.length; ++i)
2485           {
2486             ElementSpec el = data[i];
2487             if (el.getArray() != null && el.getLength() > 0)
2488               b.append(el.getArray(), el.getOffset(), el.getLength());
2489           }
2490         Content content = getContent();
2491         UndoableEdit cEdit = content.insertString(0, b.toString());
2492
2493         len = b.length();
2494         DefaultDocumentEvent ev =
2495           new DefaultDocumentEvent(0, b.length(),
2496                                    DocumentEvent.EventType.INSERT);
2497         ev.addEdit(cEdit);
2498
2499         buffer.create(len, data, ev);
2500
2501         // For the bidi update.
2502         super.insertUpdate(ev, null);
2503
2504         ev.end();
2505         fireInsertUpdate(ev);
2506         fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
2507       }
2508     catch (BadLocationException ex)
2509       {
2510         AssertionError err = new AssertionError("Unexpected bad location");
2511         err.initCause(ex);
2512         throw err;
2513       }
2514     finally
2515       {
2516         writeUnlock();
2517       }
2518   }
2519 }