OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libjava/] [classpath/] [javax/] [swing/] [text/] [DefaultStyledDocument.java] - Blame information for rev 772

Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.