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