001    /* DefaultStyledDocument.java --
002       Copyright (C) 2004, 2005 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.swing.text;
040    
041    import java.awt.Color;
042    import java.awt.Font;
043    import java.io.Serializable;
044    import java.util.ArrayList;
045    import java.util.Enumeration;
046    import java.util.Iterator;
047    import java.util.Stack;
048    import java.util.Vector;
049    
050    import javax.swing.event.ChangeEvent;
051    import javax.swing.event.ChangeListener;
052    import javax.swing.event.DocumentEvent;
053    import javax.swing.event.UndoableEditEvent;
054    import javax.swing.undo.AbstractUndoableEdit;
055    import javax.swing.undo.UndoableEdit;
056    
057    /**
058     * The default implementation of {@link StyledDocument}. The document is
059     * modeled as an {@link Element} tree, which has a {@link SectionElement} as
060     * single root, which has one or more {@link AbstractDocument.BranchElement}s
061     * as paragraph nodes and each paragraph node having one or more
062     * {@link AbstractDocument.LeafElement}s as content nodes.
063     * 
064     * @author Michael Koch (konqueror@gmx.de)
065     * @author Roman Kennke (roman@kennke.org)
066     */
067    public class DefaultStyledDocument extends AbstractDocument implements
068        StyledDocument
069    {
070    
071      /**
072       * An {@link UndoableEdit} that can undo attribute changes to an element.
073       * 
074       * @author Roman Kennke (kennke@aicas.com)
075       */
076      public static class AttributeUndoableEdit extends AbstractUndoableEdit
077      {
078        /**
079         * A copy of the old attributes.
080         */
081        protected AttributeSet copy;
082    
083        /**
084         * The new attributes.
085         */
086        protected AttributeSet newAttributes;
087    
088        /**
089         * If the new attributes replaced the old attributes or if they only were
090         * added to them.
091         */
092        protected boolean isReplacing;
093    
094        /**
095         * The element that has changed.
096         */
097        protected Element element;
098    
099        /**
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                ;
1131              
1132              Edit edit = insertPath[insertPath.length - p - 1];
1133              edit.index--;
1134              edit.removed.add(0, edit.e.getElement(edit.index));
1135            }
1136        }
1137    
1138        private void pop()
1139        {
1140          Edit edit = (Edit) elementStack.peek();
1141          elementStack.pop();
1142          if ((edit.added.size() > 0) || (edit.removed.size() > 0))
1143            {
1144              edits.add(edit);
1145            }
1146          else if (! elementStack.isEmpty())
1147            {
1148              Element e = edit.e;
1149              if (e.getElementCount() == 0)
1150                {
1151                  // If we pushed a branch element that didn't get
1152                  // used, make sure its not marked as having been added.
1153                  edit = (Edit) elementStack.peek();
1154                  edit.added.remove(e);
1155              }
1156          }
1157        }
1158    
1159        private void insertElement(ElementSpec spec)
1160        {
1161          Edit edit = (Edit) elementStack.peek();
1162          switch (spec.getType())
1163            {
1164            case ElementSpec.StartTagType:
1165              switch (spec.getDirection())
1166                {
1167                case ElementSpec.JoinFractureDirection:
1168                  // Fracture the tree and ensure the appropriate element
1169                  // is on top of the stack.
1170                  if (! createdFracture)
1171                    {
1172                      fracture(elementStack.size() - 1);
1173                    }
1174                  if (! edit.isFracture)
1175                    {
1176                      // If the parent isn't a fracture, then the fracture is
1177                      // in fracturedChild.
1178                      Edit newEdit = new Edit(fracturedChild, 0, true);
1179                      elementStack.push(newEdit);
1180                    }
1181                  else
1182                    {
1183                      // Otherwise use the parent's first child.
1184                      Element el = edit.e.getElement(0);
1185                      Edit newEdit = new Edit(el, 0, true);
1186                      elementStack.push(newEdit);
1187                    }
1188                  break;
1189                case ElementSpec.JoinNextDirection:
1190                  // Push the next paragraph element onto the stack so
1191                  // future insertions are added to it.
1192                  Element parent = edit.e.getElement(edit.index);
1193                  if (parent.isLeaf())
1194                    {
1195                      if (edit.index + 1 < edit.e.getElementCount())
1196                        parent = edit.e.getElement(edit.index + 1);
1197                      else
1198                        assert false; // Must not happen.
1199                    }
1200                  elementStack.push(new Edit(parent, 0, true));
1201                  break;
1202                default:
1203                  Element branch = createBranchElement(edit.e,
1204                                                       spec.getAttributes());
1205                  edit.added.add(branch);
1206                  elementStack.push(new Edit(branch, 0));
1207                  break;
1208                }
1209              break;
1210            case ElementSpec.EndTagType:
1211              pop();
1212              break;
1213            case ElementSpec.ContentType:
1214              insertContentTag(spec, edit);
1215              break;
1216            }
1217        }
1218    
1219        /**
1220         * Inserts the first tag into the document.
1221         * 
1222         * @param data -
1223         *          the data to be inserted.
1224         */
1225        private void insertFirstContentTag(ElementSpec[] data)
1226        {
1227          ElementSpec first = data[0];
1228          Edit edit = (Edit) elementStack.peek();
1229          Element current = edit.e.getElement(edit.index);
1230          int firstEndOffset = offset + first.length;
1231          boolean onlyContent = data.length == 1;
1232          switch (first.getDirection())
1233            {
1234            case ElementSpec.JoinPreviousDirection:
1235              if (current.getEndOffset() != firstEndOffset && ! onlyContent)
1236                {
1237                  Element newEl1 = createLeafElement(edit.e,
1238                                                     current.getAttributes(),
1239                                                     current.getStartOffset(),
1240                                                     firstEndOffset);
1241                  edit.added.add(newEl1);
1242                  edit.removed.add(current);
1243                  if (current.getEndOffset() != endOffset)
1244                    recreateLeafs = true;
1245                  else
1246                    offsetLastIndex = true;
1247                }
1248              else
1249                {
1250                  offsetLastIndex = true;
1251                  offsetLastIndexReplace = true;
1252                }
1253              break;
1254            case ElementSpec.JoinNextDirection:
1255              if (offset != 0)
1256                {
1257                  Element newEl1 = createLeafElement(edit.e,
1258                                                     current.getAttributes(),
1259                                                     current.getStartOffset(),
1260                                                     offset);
1261                  edit.added.add(newEl1);
1262                  Element next = edit.e.getElement(edit.index + 1);
1263                  if (onlyContent)
1264                    newEl1 = createLeafElement(edit.e, next.getAttributes(),
1265                                               offset, next.getEndOffset());
1266                  else
1267                    {
1268                      newEl1 = createLeafElement(edit.e, next.getAttributes(),
1269                                                 offset, firstEndOffset);
1270                    }
1271                  edit.added.add(newEl1);
1272                  edit.removed.add(current);
1273                  edit.removed.add(next);
1274                }
1275              break;
1276            default: // OriginateDirection.
1277              if (current.getStartOffset() != offset)
1278                {
1279                  Element newEl = createLeafElement(edit.e,
1280                                                    current.getAttributes(),
1281                                                    current.getStartOffset(),
1282                                                    offset);
1283                  edit.added.add(newEl);
1284                }
1285              edit.removed.add(current);
1286              Element newEl1 = createLeafElement(edit.e, first.getAttributes(),
1287                                                 offset, firstEndOffset);
1288              edit.added.add(newEl1);
1289              if (current.getEndOffset() != endOffset)
1290                recreateLeafs = true;
1291              else
1292                offsetLastIndex = true;
1293              break;
1294            }
1295        }
1296    
1297        /**
1298         * Inserts a content element into the document structure.
1299         * 
1300         * @param tag -
1301         *          the element spec
1302         */
1303        private void insertContentTag(ElementSpec tag, Edit edit)
1304        {
1305          int len = tag.getLength();
1306          int dir = tag.getDirection();
1307          if (dir == ElementSpec.JoinNextDirection)
1308            {
1309              if (! edit.isFracture)
1310                {
1311                  Element first = null;
1312                  if (insertPath != null)
1313                    {
1314                      for (int p = insertPath.length - 1; p >= 0; p--)
1315                        {
1316                          if (insertPath[p] == edit)
1317                            {
1318                              if (p != insertPath.length - 1)
1319                                first = edit.e.getElement(edit.index);
1320                              break;
1321                            }
1322                        }
1323                    }
1324                  if (first == null)
1325                    first = edit.e.getElement(edit.index + 1);
1326                  Element leaf = createLeafElement(edit.e, first.getAttributes(),
1327                                                   pos, first.getEndOffset());
1328                  edit.added.add(leaf);
1329                  edit.removed.add(first);
1330                }
1331              else
1332                {
1333                  Element first = edit.e.getElement(0);
1334                  Element leaf = createLeafElement(edit.e, first.getAttributes(),
1335                                                   pos, first.getEndOffset());
1336                  edit.added.add(leaf);
1337                  edit.removed.add(first);
1338                }
1339            }
1340          else 
1341            {
1342              Element leaf = createLeafElement(edit.e, tag.getAttributes(), pos,
1343                                               pos + len);
1344              edit.added.add(leaf);
1345            }
1346    
1347          pos += len;
1348          
1349        }
1350    
1351        /**
1352         * This method fractures bottomost leaf in the elementStack. This
1353         * happens when the first inserted tag is not content.
1354         * 
1355         * @param data
1356         *          the ElementSpecs used for the entire insertion
1357         */
1358        private void createFracture(ElementSpec[] data)
1359        {
1360          Edit edit = (Edit) elementStack.peek();
1361          Element child = edit.e.getElement(edit.index);
1362          if (offset != 0)
1363            {
1364              Element newChild = createLeafElement(edit.e, child.getAttributes(),
1365                                                   child.getStartOffset(), offset);
1366              edit.added.add(newChild);
1367            }
1368          edit.removed.add(child);
1369          if (child.getEndOffset() != endOffset)
1370            recreateLeafs = true;
1371          else
1372            offsetLastIndex = true;
1373        }
1374    
1375        private void fracture(int depth)
1376        {
1377          int len = insertPath.length;
1378          int lastIndex = -1;
1379          boolean recreate = recreateLeafs;
1380          Edit lastEdit = insertPath[len - 1];
1381          boolean childChanged = lastEdit.index + 1 < lastEdit.e.getElementCount();
1382          int deepestChangedIndex = recreate ? len : - 1;
1383          int lastChangedIndex = len - 1;
1384          createdFracture = true;
1385          for (int i = len - 2; i >= 0; i--)
1386            {
1387              Edit edit = insertPath[i];
1388              if (edit.added.size() > 0 || i == depth)
1389                {
1390                  lastIndex = i;
1391                  if (! recreate && childChanged)
1392                    {
1393                      recreate = true;
1394                      if (deepestChangedIndex == -1)
1395                        deepestChangedIndex = lastChangedIndex + 1;
1396                    }
1397                }
1398              if (! childChanged && edit.index < edit.e.getElementCount())
1399                {
1400                  childChanged = true;
1401                  lastChangedIndex = i;
1402                }
1403            }
1404          if (recreate)
1405            {
1406              if (lastIndex == -1)
1407                lastIndex = len - 1;
1408              recreate(lastIndex, deepestChangedIndex);
1409            }
1410        }
1411    
1412        private void recreate(int startIndex, int endIndex)
1413        {
1414          // Recreate the element representing the inserted index.
1415          Edit edit = insertPath[startIndex];
1416          Element child;
1417          Element newChild;
1418          int changeLength = insertPath.length;
1419    
1420          if (startIndex + 1 == changeLength)
1421            child = edit.e.getElement(edit.index);
1422          else
1423            child = edit.e.getElement(edit.index - 1);
1424    
1425          if(child.isLeaf())
1426            {
1427              newChild = createLeafElement(edit.e, child.getAttributes(),
1428                                       Math.max(endOffset, child.getStartOffset()),
1429                                       child.getEndOffset());
1430            }
1431          else
1432            {
1433              newChild = createBranchElement(edit.e, child.getAttributes());
1434            }
1435          fracturedParent = edit.e;
1436          fracturedChild = newChild;
1437    
1438          // Recreate all the elements to the right of the insertion point.
1439          Element parent = newChild;
1440          while (++startIndex < endIndex)
1441            {
1442              boolean isEnd = (startIndex + 1) == endIndex;
1443              boolean isEndLeaf = (startIndex + 1) == changeLength;
1444    
1445              // Create the newChild, a duplicate of the elment at
1446              // index. This isn't done if isEnd and offsetLastIndex are true
1447              // indicating a join previous was done.
1448              edit = insertPath[startIndex];
1449    
1450              // Determine the child to duplicate, won't have to duplicate
1451              // if at end of fracture, or offseting index.
1452              if(isEnd)
1453                {
1454                  if(offsetLastIndex || ! isEndLeaf)
1455                    child = null;
1456                  else
1457                    child = edit.e.getElement(edit.index);
1458                }
1459              else
1460                {
1461                  child = edit.e.getElement(edit.index - 1);
1462                }
1463    
1464              // Duplicate it.
1465              if(child != null)
1466                {
1467                  if(child.isLeaf())
1468                    {
1469                      newChild = createLeafElement(parent, child.getAttributes(),
1470                                       Math.max(endOffset, child.getStartOffset()),
1471                                       child.getEndOffset());
1472                    }
1473                  else
1474                    {
1475                      newChild = createBranchElement(parent,
1476                                                     child.getAttributes());
1477                    }
1478                }
1479              else
1480                newChild = null;
1481    
1482            // Recreate the remaining children (there may be none).
1483            int childrenToMove = edit.e.getElementCount() - edit.index;
1484            Element[] children;
1485            int moveStartIndex;
1486            int childStartIndex = 1;
1487    
1488            if (newChild == null)
1489              {
1490                // Last part of fracture.
1491                if (isEndLeaf)
1492                  {
1493                    childrenToMove--;
1494                    moveStartIndex = edit.index + 1;
1495                  }
1496                else
1497                  {
1498                    moveStartIndex = edit.index;
1499                  }
1500                childStartIndex = 0;
1501                children = new Element[childrenToMove];
1502              }
1503            else
1504              {
1505                if (! isEnd)
1506                  {
1507                    // Branch.
1508                    childrenToMove++;
1509                    moveStartIndex = edit.index;
1510                }
1511                else
1512                  {
1513                    // Last leaf, need to recreate part of it.
1514                    moveStartIndex = edit.index + 1;
1515                  }
1516                children = new Element[childrenToMove];
1517                children[0] = newChild;
1518            }
1519    
1520            for (int c = childStartIndex; c < childrenToMove; c++)
1521              {
1522                Element toMove = edit.e.getElement(moveStartIndex++);
1523                children[c] = recreateFracturedElement(parent, toMove);
1524                edit.removed.add(toMove);
1525              }
1526            ((BranchElement) parent).replace(0, 0, children);
1527            parent = newChild;
1528          }
1529    
1530        }
1531    
1532        private Element recreateFracturedElement(Element parent, Element toCopy)
1533        {
1534          Element recreated;
1535          if(toCopy.isLeaf())
1536            {
1537              recreated = createLeafElement(parent, toCopy.getAttributes(),
1538                                      Math.max(toCopy.getStartOffset(), endOffset),
1539                                      toCopy.getEndOffset());
1540            }
1541          else
1542            {
1543              Element newParent = createBranchElement(parent,
1544                                                      toCopy.getAttributes());
1545              int childCount = toCopy.getElementCount();
1546              Element[] newChildren = new Element[childCount];
1547              for (int i = 0; i < childCount; i++)
1548                {
1549                  newChildren[i] = recreateFracturedElement(newParent,
1550                                                            toCopy.getElement(i));
1551                }
1552              ((BranchElement) newParent).replace(0, 0, newChildren);
1553              recreated = newParent;
1554            }
1555          return recreated;
1556        }
1557    
1558        private boolean split(int offs, int len)
1559        {
1560          boolean splitEnd = false;
1561          // Push the path to the stack.
1562          Element e = root;
1563          int index = e.getElementIndex(offs);
1564          while (! e.isLeaf())
1565            {
1566              elementStack.push(new Edit(e, index));
1567              e = e.getElement(index);
1568              index = e.getElementIndex(offs);
1569            }
1570    
1571          Edit ec = (Edit) elementStack.peek();
1572          Element child = ec.e.getElement(ec.index);
1573          // Make sure there is something to do. If the
1574          // offset is already at a boundary then there is
1575          // nothing to do.
1576          if (child.getStartOffset() < offs && offs < child.getEndOffset())
1577            {
1578              // We need to split, now see if the other end is within
1579              // the same parent.
1580              int index0 = ec.index;
1581              int index1 = index0;
1582              if (((offs + len) < ec.e.getEndOffset()) && (len != 0))
1583                {
1584                  // It's a range split in the same parent.
1585                  index1 = ec.e.getElementIndex(offs+len);
1586                  if (index1 == index0)
1587                    {
1588                      // It's a three-way split.
1589                      ec.removed.add(child);
1590                      e = createLeafElement(ec.e, child.getAttributes(),
1591                                            child.getStartOffset(), offs);
1592                      ec.added.add(e);
1593                      e = createLeafElement(ec.e, child.getAttributes(),
1594                                            offs, offs + len);
1595                      ec.added.add(e);
1596                      e = createLeafElement(ec.e, child.getAttributes(),
1597                                            offs + len, child.getEndOffset());
1598                      ec.added.add(e);
1599                      return true;
1600                    }
1601                  else
1602                    {
1603                      child = ec.e.getElement(index1);
1604                      if ((offs + len) == child.getStartOffset())
1605                        {
1606                          // End is already on a boundary.
1607                          index1 = index0;
1608                        }
1609                    }
1610                  splitEnd = true;
1611                }
1612    
1613              // Split the first location.
1614              pos = offs;
1615              child = ec.e.getElement(index0);
1616              ec.removed.add(child);
1617              e = createLeafElement(ec.e, child.getAttributes(),
1618                                    child.getStartOffset(), pos);
1619              ec.added.add(e);
1620              e = createLeafElement(ec.e, child.getAttributes(),
1621                                    pos, child.getEndOffset());
1622              ec.added.add(e);
1623    
1624              // Pick up things in the middle.
1625              for (int i = index0 + 1; i < index1; i++)
1626                {
1627                  child = ec.e.getElement(i);
1628                  ec.removed.add(child);
1629                  ec.added.add(child);
1630                }
1631    
1632              if (index1 != index0)
1633                {
1634                  child = ec.e.getElement(index1);
1635                  pos = offs + len;
1636                  ec.removed.add(child);
1637                  e = createLeafElement(ec.e, child.getAttributes(),
1638                                        child.getStartOffset(), pos);
1639                  ec.added.add(e);
1640                  e = createLeafElement(ec.e, child.getAttributes(),
1641                                        pos, child.getEndOffset());
1642                  
1643                  ec.added.add(e);
1644                }
1645            }
1646          return splitEnd;
1647          
1648        }
1649    
1650      }
1651    
1652    
1653      /**
1654       * An element type for sections. This is a simple BranchElement with a unique
1655       * name.
1656       */
1657      protected class SectionElement extends BranchElement
1658      {
1659        /**
1660         * Creates a new SectionElement.
1661         */
1662        public SectionElement()
1663        {
1664          super(null, null);
1665        }
1666    
1667        /**
1668         * Returns the name of the element. This method always returns
1669         * &quot;section&quot;.
1670         * 
1671         * @return the name of the element
1672         */
1673        public String getName()
1674        {
1675          return SectionElementName;
1676        }
1677      }
1678    
1679      /**
1680       * Receives notification when any of the document's style changes and calls
1681       * {@link DefaultStyledDocument#styleChanged(Style)}.
1682       * 
1683       * @author Roman Kennke (kennke@aicas.com)
1684       */
1685      private class StyleChangeListener implements ChangeListener
1686      {
1687    
1688        /**
1689         * Receives notification when any of the document's style changes and calls
1690         * {@link DefaultStyledDocument#styleChanged(Style)}.
1691         * 
1692         * @param event
1693         *          the change event
1694         */
1695        public void stateChanged(ChangeEvent event)
1696        {
1697          Style style = (Style) event.getSource();
1698          styleChanged(style);
1699        }
1700      }
1701    
1702      /** The serialization UID (compatible with JDK1.5). */
1703      private static final long serialVersionUID = 940485415728614849L;
1704    
1705      /**
1706       * The default size to use for new content buffers.
1707       */
1708      public static final int BUFFER_SIZE_DEFAULT = 4096;
1709    
1710      /**
1711       * The <code>EditorBuffer</code> that is used to manage to
1712       * <code>Element</code> hierarchy.
1713       */
1714      protected DefaultStyledDocument.ElementBuffer buffer;
1715    
1716      /**
1717       * Listens for changes on this document's styles and notifies styleChanged().
1718       */
1719      private StyleChangeListener styleChangeListener;
1720    
1721      /**
1722       * Creates a new <code>DefaultStyledDocument</code>.
1723       */
1724      public DefaultStyledDocument()
1725      {
1726        this(new GapContent(BUFFER_SIZE_DEFAULT), new StyleContext());
1727      }
1728    
1729      /**
1730       * Creates a new <code>DefaultStyledDocument</code> that uses the specified
1731       * {@link StyleContext}.
1732       * 
1733       * @param context
1734       *          the <code>StyleContext</code> to use
1735       */
1736      public DefaultStyledDocument(StyleContext context)
1737      {
1738        this(new GapContent(BUFFER_SIZE_DEFAULT), context);
1739      }
1740    
1741      /**
1742       * Creates a new <code>DefaultStyledDocument</code> that uses the specified
1743       * {@link StyleContext} and {@link Content} buffer.
1744       * 
1745       * @param content
1746       *          the <code>Content</code> buffer to use
1747       * @param context
1748       *          the <code>StyleContext</code> to use
1749       */
1750      public DefaultStyledDocument(AbstractDocument.Content content,
1751                                   StyleContext context)
1752      {
1753        super(content, context);
1754        buffer = new ElementBuffer(createDefaultRoot());
1755        setLogicalStyle(0, context.getStyle(StyleContext.DEFAULT_STYLE));
1756      }
1757    
1758      /**
1759       * Adds a style into the style hierarchy. Unspecified style attributes can be
1760       * resolved in the <code>parent</code> style, if one is specified. While it
1761       * is legal to add nameless styles (<code>nm == null</code),
1762       * you must be aware that the client application is then responsible
1763       * for managing the style hierarchy, since unnamed styles cannot be
1764       * looked up by their name.
1765       *
1766       * @param nm the name of the style or <code>null</code> if the style should
1767       *           be unnamed
1768       * @param parent the parent in which unspecified style attributes are
1769       *           resolved, or <code>null</code> if that is not necessary
1770       *
1771       * @return the newly created <code>Style</code>
1772       */
1773      public Style addStyle(String nm, Style parent)
1774      {
1775        StyleContext context = (StyleContext) getAttributeContext();
1776        Style newStyle = context.addStyle(nm, parent);
1777    
1778        // Register change listener.
1779        if (styleChangeListener == null)
1780          styleChangeListener = new StyleChangeListener();
1781        newStyle.addChangeListener(styleChangeListener);
1782    
1783        return newStyle;
1784      }
1785    
1786      /**
1787       * Create the default root element for this kind of <code>Document</code>.
1788       * 
1789       * @return the default root element for this kind of <code>Document</code>
1790       */
1791      protected AbstractDocument.AbstractElement createDefaultRoot()
1792      {
1793        Element[] tmp;
1794        SectionElement section = new SectionElement();
1795    
1796        BranchElement paragraph = new BranchElement(section, null);
1797        tmp = new Element[1];
1798        tmp[0] = paragraph;
1799        section.replace(0, 0, tmp);
1800    
1801        Element leaf = new LeafElement(paragraph, null, 0, 1);
1802        tmp = new Element[1];
1803        tmp[0] = leaf;
1804        paragraph.replace(0, 0, tmp);
1805    
1806        return section;
1807      }
1808    
1809      /**
1810       * Returns the <code>Element</code> that corresponds to the character at the
1811       * specified position.
1812       * 
1813       * @param position
1814       *          the position of which we query the corresponding
1815       *          <code>Element</code>
1816       * @return the <code>Element</code> that corresponds to the character at the
1817       *         specified position
1818       */
1819      public Element getCharacterElement(int position)
1820      {
1821        Element element = getDefaultRootElement();
1822    
1823        while (!element.isLeaf())
1824          {
1825            int index = element.getElementIndex(position);
1826            element = element.getElement(index);
1827          }
1828    
1829        return element;
1830      }
1831    
1832      /**
1833       * Extracts a background color from a set of attributes.
1834       * 
1835       * @param attributes
1836       *          the attributes from which to get a background color
1837       * @return the background color that correspond to the attributes
1838       */
1839      public Color getBackground(AttributeSet attributes)
1840      {
1841        StyleContext context = (StyleContext) getAttributeContext();
1842        return context.getBackground(attributes);
1843      }
1844    
1845      /**
1846       * Returns the default root element.
1847       * 
1848       * @return the default root element
1849       */
1850      public Element getDefaultRootElement()
1851      {
1852        return buffer.getRootElement();
1853      }
1854    
1855      /**
1856       * Extracts a font from a set of attributes.
1857       * 
1858       * @param attributes
1859       *          the attributes from which to get a font
1860       * @return the font that correspond to the attributes
1861       */
1862      public Font getFont(AttributeSet attributes)
1863      {
1864        StyleContext context = (StyleContext) getAttributeContext();
1865        return context.getFont(attributes);
1866      }
1867    
1868      /**
1869       * Extracts a foreground color from a set of attributes.
1870       * 
1871       * @param attributes
1872       *          the attributes from which to get a foreground color
1873       * @return the foreground color that correspond to the attributes
1874       */
1875      public Color getForeground(AttributeSet attributes)
1876      {
1877        StyleContext context = (StyleContext) getAttributeContext();
1878        return context.getForeground(attributes);
1879      }
1880    
1881      /**
1882       * Returns the logical <code>Style</code> for the specified position.
1883       * 
1884       * @param position
1885       *          the position from which to query to logical style
1886       * @return the logical <code>Style</code> for the specified position
1887       */
1888      public Style getLogicalStyle(int position)
1889      {
1890        Element paragraph = getParagraphElement(position);
1891        AttributeSet attributes = paragraph.getAttributes();
1892        AttributeSet a = attributes.getResolveParent();
1893        // If the resolve parent is not of type Style, we return null.
1894        if (a instanceof Style)
1895          return (Style) a;
1896        return null;
1897      }
1898    
1899      /**
1900       * Returns the paragraph element for the specified position. If the position
1901       * is outside the bounds of the document's root element, then the closest
1902       * element is returned. That is the last paragraph if
1903       * <code>position >= endIndex</code> or the first paragraph if
1904       * <code>position < startIndex</code>.
1905       * 
1906       * @param position
1907       *          the position for which to query the paragraph element
1908       * @return the paragraph element for the specified position
1909       */
1910      public Element getParagraphElement(int position)
1911      {
1912        Element e = getDefaultRootElement();
1913        while (!e.isLeaf())
1914          e = e.getElement(e.getElementIndex(position));
1915    
1916        if (e != null)
1917          return e.getParentElement();
1918        return e;
1919      }
1920    
1921      /**
1922       * Looks up and returns a named <code>Style</code>.
1923       * 
1924       * @param nm
1925       *          the name of the <code>Style</code>
1926       * @return the found <code>Style</code> of <code>null</code> if no such
1927       *         <code>Style</code> exists
1928       */
1929      public Style getStyle(String nm)
1930      {
1931        StyleContext context = (StyleContext) getAttributeContext();
1932        return context.getStyle(nm);
1933      }
1934    
1935      /**
1936       * Removes a named <code>Style</code> from the style hierarchy.
1937       * 
1938       * @param nm
1939       *          the name of the <code>Style</code> to be removed
1940       */
1941      public void removeStyle(String nm)
1942      {
1943        StyleContext context = (StyleContext) getAttributeContext();
1944        context.removeStyle(nm);
1945      }
1946    
1947      /**
1948       * Sets text attributes for the fragment specified by <code>offset</code>
1949       * and <code>length</code>.
1950       * 
1951       * @param offset
1952       *          the start offset of the fragment
1953       * @param length
1954       *          the length of the fragment
1955       * @param attributes
1956       *          the text attributes to set
1957       * @param replace
1958       *          if <code>true</code>, the attributes of the current selection
1959       *          are overridden, otherwise they are merged
1960       */
1961      public void setCharacterAttributes(int offset, int length,
1962                                         AttributeSet attributes, boolean replace)
1963      {
1964        // Exit early if length is 0, so no DocumentEvent is created or fired.
1965        if (length == 0)
1966          return;
1967        try
1968          {
1969            // Must obtain a write lock for this method. writeLock() and
1970            // writeUnlock() should always be in try/finally block to make
1971            // sure that locking happens in a balanced manner.
1972            writeLock();
1973            DefaultDocumentEvent ev = new DefaultDocumentEvent(offset,
1974                                                               length,
1975                                                               DocumentEvent.EventType.CHANGE);
1976    
1977            // Modify the element structure so that the interval begins at an
1978            // element
1979            // start and ends at an element end.
1980            buffer.change(offset, length, ev);
1981    
1982            // Visit all paragraph elements within the specified interval
1983            int end = offset + length;
1984            Element curr;
1985            for (int pos = offset; pos < end;)
1986              {
1987                // Get the CharacterElement at offset pos.
1988                curr = getCharacterElement(pos);
1989                if (pos == curr.getEndOffset())
1990                  break;
1991    
1992                MutableAttributeSet a = (MutableAttributeSet) curr.getAttributes();
1993                ev.addEdit(new AttributeUndoableEdit(curr, attributes, replace));
1994                // If replace is true, remove all the old attributes.
1995                if (replace)
1996                  a.removeAttributes(a);
1997                // Add all the new attributes.
1998                a.addAttributes(attributes);
1999                // Increment pos so we can check the next CharacterElement.
2000                pos = curr.getEndOffset();
2001              }
2002            fireChangedUpdate(ev);
2003            fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
2004          }
2005        finally
2006          {
2007            writeUnlock();
2008          }
2009      }
2010    
2011      /**
2012       * Sets the logical style for the paragraph at the specified position.
2013       * 
2014       * @param position
2015       *          the position at which the logical style is added
2016       * @param style
2017       *          the style to set for the current paragraph
2018       */
2019      public void setLogicalStyle(int position, Style style)
2020      {
2021        Element el = getParagraphElement(position);
2022        // getParagraphElement doesn't return null but subclasses might so
2023        // we check for null here.
2024        if (el == null)
2025          return;
2026        try
2027          {
2028            writeLock();
2029            if (el instanceof AbstractElement)
2030              {
2031                AbstractElement ael = (AbstractElement) el;
2032                ael.setResolveParent(style);
2033                int start = el.getStartOffset();
2034                int end = el.getEndOffset();
2035                DefaultDocumentEvent ev = new DefaultDocumentEvent(start,
2036                                                                   end - start,
2037                                                                   DocumentEvent.EventType.CHANGE);
2038                fireChangedUpdate(ev);
2039                fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
2040              }
2041            else
2042              throw new AssertionError(
2043                                       "paragraph elements are expected to be"
2044                                           + "instances of AbstractDocument.AbstractElement");
2045          }
2046        finally
2047          {
2048            writeUnlock();
2049          }
2050      }
2051    
2052      /**
2053       * Sets text attributes for the paragraph at the specified fragment.
2054       * 
2055       * @param offset
2056       *          the beginning of the fragment
2057       * @param length
2058       *          the length of the fragment
2059       * @param attributes
2060       *          the text attributes to set
2061       * @param replace
2062       *          if <code>true</code>, the attributes of the current selection
2063       *          are overridden, otherwise they are merged
2064       */
2065      public void setParagraphAttributes(int offset, int length,
2066                                         AttributeSet attributes, boolean replace)
2067      {
2068        try
2069          {
2070            // Must obtain a write lock for this method. writeLock() and
2071            // writeUnlock() should always be in try/finally blocks to make
2072            // sure that locking occurs in a balanced manner.
2073            writeLock();
2074    
2075            // Create a DocumentEvent to use for changedUpdate().
2076            DefaultDocumentEvent ev = new DefaultDocumentEvent(offset,
2077                                                               length,
2078                                                               DocumentEvent.EventType.CHANGE);
2079    
2080            // Have to iterate through all the _paragraph_ elements that are
2081            // contained or partially contained in the interval
2082            // (offset, offset + length).
2083            Element rootElement = getDefaultRootElement();
2084            int startElement = rootElement.getElementIndex(offset);
2085            int endElement = rootElement.getElementIndex(offset + length - 1);
2086            if (endElement < startElement)
2087              endElement = startElement;
2088    
2089            for (int i = startElement; i <= endElement; i++)
2090              {
2091                Element par = rootElement.getElement(i);
2092                MutableAttributeSet a = (MutableAttributeSet) par.getAttributes();
2093                // Add the change to the DocumentEvent.
2094                ev.addEdit(new AttributeUndoableEdit(par, attributes, replace));
2095                // If replace is true remove the old attributes.
2096                if (replace)
2097                  a.removeAttributes(a);
2098                // Add the new attributes.
2099                a.addAttributes(attributes);
2100              }
2101            fireChangedUpdate(ev);
2102            fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
2103          }
2104        finally
2105          {
2106            writeUnlock();
2107          }
2108      }
2109    
2110      /**
2111       * Called in response to content insert actions. This is used to update the
2112       * element structure.
2113       * 
2114       * @param ev
2115       *          the <code>DocumentEvent</code> describing the change
2116       * @param attr
2117       *          the attributes for the change
2118       */
2119      protected void insertUpdate(DefaultDocumentEvent ev, AttributeSet attr)
2120      {
2121        int offs = ev.getOffset();
2122        int len = ev.getLength();
2123        int endOffs = offs + len;
2124        if (attr == null)
2125          attr = SimpleAttributeSet.EMPTY;
2126    
2127        // Paragraph attributes are fetched from the point _after_ the insertion.
2128        Element paragraph = getParagraphElement(endOffs);
2129        AttributeSet pAttr = paragraph.getAttributes();
2130        // Character attributes are fetched from the actual insertion point.
2131        Element paragraph2 = getParagraphElement(offs);
2132        int contIndex = paragraph2.getElementIndex(offs);
2133        Element content = paragraph2.getElement(contIndex);
2134        AttributeSet cAttr = content.getAttributes();
2135    
2136        boolean insertAtBoundary = content.getEndOffset() == endOffs;
2137        try
2138          {
2139            Segment s = new Segment();
2140            ArrayList buf = new ArrayList();
2141            ElementSpec lastStartTag = null;
2142            boolean insertAfterNewline = false;
2143            short lastStartDir = ElementSpec.OriginateDirection;
2144    
2145            // Special handle if we are inserting after a newline.
2146            if (offs > 0)
2147              {
2148                getText(offs - 1, 1, s);
2149                if (s.array[s.offset] == '\n')
2150                  {
2151                    insertAfterNewline = true;
2152                    lastStartDir = insertAfterNewline(paragraph, paragraph2,
2153                                                      pAttr, buf, offs,
2154                                                      endOffs);
2155                    // Search last start tag.
2156                    for (int i = buf.size() - 1; i >= 0 && lastStartTag == null;
2157                         i--)
2158                      {
2159                        ElementSpec tag = (ElementSpec) buf.get(i);
2160                        if (tag.getType() == ElementSpec.StartTagType)
2161                          {
2162                            lastStartTag = tag;
2163                          }
2164                      }
2165                  }
2166    
2167              }
2168    
2169            // If we are not inserting after a newline, the paragraph attributes
2170            // come from the paragraph under the insertion point.
2171            if (! insertAfterNewline)
2172              pAttr = paragraph2.getAttributes();
2173    
2174            // Scan text and build up the specs.
2175            getText(offs, len, s);
2176            int end = s.offset + s.count;
2177            int last = s.offset;
2178            for (int i = s.offset; i < end; i++)
2179              {
2180                if (s.array[i] == '\n')
2181                  {
2182                    int breakOffs = i + 1;
2183                    buf.add(new ElementSpec(attr, ElementSpec.ContentType,
2184                                            breakOffs - last));
2185                    buf.add(new ElementSpec(null, ElementSpec.EndTagType));
2186                    lastStartTag = new ElementSpec(pAttr,
2187                                                   ElementSpec.StartTagType);
2188                    buf.add(lastStartTag);
2189                    last = breakOffs;
2190                  }
2191              }
2192    
2193            // Need to add a tailing content tag if we didn't finish at a boundary.
2194            if (last < end)
2195              {
2196                buf.add(new ElementSpec(attr, ElementSpec.ContentType,
2197                                        end - last));
2198              }
2199    
2200            // Now we need to fix up the directions of the specs.
2201            ElementSpec first = (ElementSpec) buf.get(0);
2202            int doclen = getLength();
2203    
2204            // Maybe join-previous the first tag if it is content and has
2205            // the same attributes as the previous character run.
2206            if (first.getType() == ElementSpec.ContentType && cAttr.isEqual(attr))
2207              first.setDirection(ElementSpec.JoinPreviousDirection);
2208    
2209            // Join-fracture or join-next the last start tag if necessary.
2210            if (lastStartTag != null)
2211              {
2212                if (insertAfterNewline)
2213                  lastStartTag.setDirection(lastStartDir);
2214                else if (paragraph2.getEndOffset() != endOffs)
2215                  lastStartTag.setDirection(ElementSpec.JoinFractureDirection);
2216                else
2217                  {
2218                    Element par = paragraph2.getParentElement();
2219                    int par2Index = par.getElementIndex(offs);
2220                    if (par2Index + 1 < par.getElementCount()
2221                        && ! par.getElement(par2Index + 1).isLeaf())
2222                      lastStartTag.setDirection(ElementSpec.JoinNextDirection);
2223                  }
2224              }
2225    
2226            // Join-next last tag if possible.
2227            if (insertAtBoundary && endOffs < doclen)
2228              {
2229                ElementSpec lastTag = (ElementSpec) buf.get(buf.size() - 1);
2230                if (lastTag.getType() == ElementSpec.ContentType
2231                    && ((lastStartTag == null
2232                         && (paragraph == paragraph2 || insertAfterNewline))
2233                        || (lastStartTag != null
2234                 && lastStartTag.getDirection() != ElementSpec.OriginateDirection)))
2235                  {
2236                    int nextIndex = paragraph.getElementIndex(endOffs);
2237                    Element nextRun = paragraph.getElement(nextIndex);
2238                    if (nextRun.isLeaf() && attr.isEqual(nextRun.getAttributes()))
2239                      lastTag.setDirection(ElementSpec.JoinNextDirection);
2240                  }
2241              }
2242    
2243            else if (! insertAtBoundary && lastStartTag != null
2244               && lastStartTag.getDirection() == ElementSpec.JoinFractureDirection)
2245              {
2246                ElementSpec lastTag = (ElementSpec) buf.get(buf.size() - 1);
2247                if (lastTag.getType() == ElementSpec.ContentType
2248                    && lastTag.getDirection() != ElementSpec.JoinPreviousDirection
2249                    && attr.isEqual(cAttr))
2250                  {
2251                    lastTag.setDirection(ElementSpec.JoinNextDirection);
2252                  }
2253              }
2254    
2255            ElementSpec[] specs = new ElementSpec[buf.size()];
2256            specs = (ElementSpec[]) buf.toArray(specs);
2257            buffer.insert(offs, len, specs, ev);
2258          }
2259        catch (BadLocationException ex)
2260          {
2261            // Ignore this. Comment out for debugging.
2262            ex.printStackTrace();
2263          }
2264        super.insertUpdate(ev, attr);
2265      }
2266    
2267      private short insertAfterNewline(Element par1, Element par2,
2268                                       AttributeSet attr, ArrayList buf,
2269                                       int offs, int endOffs)
2270      {
2271        short dir = 0;
2272        if (par1.getParentElement() == par2.getParentElement())
2273          {
2274            ElementSpec tag = new ElementSpec(attr, ElementSpec.EndTagType);
2275            buf.add(tag);
2276            tag = new ElementSpec(attr, ElementSpec.StartTagType);
2277            buf.add(tag);
2278            if (par2.getEndOffset() != endOffs)
2279              dir = ElementSpec.JoinFractureDirection;
2280            else
2281              {
2282                Element par = par2.getParentElement();
2283                if (par.getElementIndex(offs) + 1 < par.getElementCount())
2284                  dir = ElementSpec.JoinNextDirection;
2285              }
2286          }
2287        else
2288          {
2289            // For text with more than 2 levels, find the common parent of
2290            // par1 and par2.
2291            ArrayList parentsLeft = new ArrayList();
2292            ArrayList parentsRight = new ArrayList();
2293            Element e = par2;
2294            while (e != null)
2295              {
2296                parentsLeft.add(e);
2297                e = e.getParentElement();
2298              }
2299            e = par1;
2300            int leftIndex = -1;
2301            while (e != null && (leftIndex = parentsLeft.indexOf(e)) == 1)
2302              {
2303                parentsRight.add(e);
2304                e = e.getParentElement();
2305              }
2306    
2307            if (e != null)
2308           
2309              {
2310                // e is now the common parent.
2311                // Insert the end tags.
2312                for (int c = 0; c < leftIndex; c++)
2313                  {
2314                    buf.add(new ElementSpec(null, ElementSpec.EndTagType));
2315                  }
2316                // Insert the start tags.
2317                for (int c = parentsRight.size() - 1; c >= 0; c--)
2318                  {
2319                    Element el = (Element) parentsRight.get(c);
2320                    ElementSpec tag = new ElementSpec(el.getAttributes(),
2321                                                      ElementSpec.StartTagType);
2322                    if (c > 0)
2323                      tag.setDirection(ElementSpec.JoinNextDirection);
2324                    buf.add(tag);
2325                  }
2326                if (parentsRight.size() > 0)
2327                  dir = ElementSpec.JoinNextDirection;
2328                else
2329                  dir = ElementSpec.JoinFractureDirection;
2330              }
2331            else
2332              assert false;
2333          }
2334        return dir;
2335      }
2336    
2337      /**
2338       * A helper method to set up the ElementSpec buffer for the special case of an
2339       * insertion occurring immediately after a newline.
2340       * 
2341       * @param specs
2342       *          the ElementSpec buffer to initialize.
2343       */
2344      short handleInsertAfterNewline(Vector specs, int offset, int endOffset,
2345                                     Element prevParagraph, Element paragraph,
2346                                     AttributeSet a)
2347      {
2348        if (prevParagraph.getParentElement() == paragraph.getParentElement())
2349          {
2350            specs.add(new ElementSpec(a, ElementSpec.EndTagType));
2351            specs.add(new ElementSpec(a, ElementSpec.StartTagType));
2352            if (paragraph.getStartOffset() != endOffset)
2353              return ElementSpec.JoinFractureDirection;
2354            // If there is an Element after this one, use JoinNextDirection.
2355            Element parent = paragraph.getParentElement();
2356            if (parent.getElementCount() > (parent.getElementIndex(offset) + 1))
2357              return ElementSpec.JoinNextDirection;
2358          }
2359        return ElementSpec.OriginateDirection;
2360      }
2361    
2362      /**
2363       * Updates the document structure in response to text removal. This is
2364       * forwarded to the {@link ElementBuffer} of this document. Any changes to the
2365       * document structure are added to the specified document event and sent to
2366       * registered listeners.
2367       * 
2368       * @param ev
2369       *          the document event that records the changes to the document
2370       */
2371      protected void removeUpdate(DefaultDocumentEvent ev)
2372      {
2373        super.removeUpdate(ev);
2374        buffer.remove(ev.getOffset(), ev.getLength(), ev);
2375      }
2376    
2377      /**
2378       * Returns an enumeration of all style names.
2379       * 
2380       * @return an enumeration of all style names
2381       */
2382      public Enumeration<?> getStyleNames()
2383      {
2384        StyleContext context = (StyleContext) getAttributeContext();
2385        return context.getStyleNames();
2386      }
2387    
2388      /**
2389       * Called when any of this document's styles changes.
2390       * 
2391       * @param style
2392       *          the style that changed
2393       */
2394      protected void styleChanged(Style style)
2395      {
2396        // Nothing to do here. This is intended to be overridden by subclasses.
2397      }
2398    
2399      /**
2400       * Inserts a bulk of structured content at once.
2401       * 
2402       * @param offset
2403       *          the offset at which the content should be inserted
2404       * @param data
2405       *          the actual content spec to be inserted
2406       */
2407      protected void insert(int offset, ElementSpec[] data)
2408          throws BadLocationException
2409      {
2410        if (data == null || data.length == 0)
2411          return;
2412        try
2413          {
2414            // writeLock() and writeUnlock() should always be in a try/finally
2415            // block so that locking balance is guaranteed even if some
2416            // exception is thrown.
2417            writeLock();
2418    
2419            // First we collect the content to be inserted.
2420            StringBuffer contentBuffer = new StringBuffer();
2421            for (int i = 0; i < data.length; i++)
2422              {
2423                // Collect all inserts into one so we can get the correct
2424                // ElementEdit
2425                ElementSpec spec = data[i];
2426                if (spec.getArray() != null && spec.getLength() > 0)
2427                  contentBuffer.append(spec.getArray(), spec.getOffset(),
2428                                       spec.getLength());
2429              }
2430    
2431            int length = contentBuffer.length();
2432    
2433            // If there was no content inserted then exit early.
2434            if (length == 0)
2435              return;
2436    
2437            Content c = getContent();
2438            UndoableEdit edit = c.insertString(offset,
2439                                               contentBuffer.toString());
2440    
2441            // Create the DocumentEvent with the ElementEdit added
2442            DefaultDocumentEvent ev = new DefaultDocumentEvent(offset,
2443                                                               length,
2444                                                               DocumentEvent.EventType.INSERT);
2445    
2446            ev.addEdit(edit);
2447    
2448            // Finally we must update the document structure and fire the insert
2449            // update event.
2450            buffer.insert(offset, length, data, ev);
2451    
2452            super.insertUpdate(ev, null);
2453    
2454            ev.end();
2455            fireInsertUpdate(ev);
2456            fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
2457          }
2458        finally
2459          {
2460            writeUnlock();
2461          }
2462      }
2463    
2464      /**
2465       * Initializes the <code>DefaultStyledDocument</code> with the specified
2466       * data.
2467       * 
2468       * @param data
2469       *          the specification of the content with which the document is
2470       *          initialized
2471       */
2472      protected void create(ElementSpec[] data)
2473      {
2474        try
2475          {
2476    
2477            // Clear content if there is some.
2478            int len = getLength();
2479            if (len > 0)
2480              remove(0, len);
2481    
2482            writeLock();
2483    
2484            // Now we insert the content.
2485            StringBuilder b = new StringBuilder();
2486            for (int i = 0; i < data.length; ++i)
2487              {
2488                ElementSpec el = data[i];
2489                if (el.getArray() != null && el.getLength() > 0)
2490                  b.append(el.getArray(), el.getOffset(), el.getLength());
2491              }
2492            Content content = getContent();
2493            UndoableEdit cEdit = content.insertString(0, b.toString());
2494    
2495            len = b.length();
2496            DefaultDocumentEvent ev =
2497              new DefaultDocumentEvent(0, b.length(),
2498                                       DocumentEvent.EventType.INSERT);
2499            ev.addEdit(cEdit);
2500    
2501            buffer.create(len, data, ev);
2502    
2503            // For the bidi update.
2504            super.insertUpdate(ev, null);
2505    
2506            ev.end();
2507            fireInsertUpdate(ev);
2508            fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
2509          }
2510        catch (BadLocationException ex)
2511          {
2512            AssertionError err = new AssertionError("Unexpected bad location");
2513            err.initCause(ex);
2514            throw err;
2515          }
2516        finally
2517          {
2518            writeUnlock();
2519          }
2520      }
2521    }