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