~ubuntu-branches/ubuntu/precise/classpath/precise

« back to all changes in this revision

Viewing changes to javax/swing/text/DefaultStyledDocument.java

  • Committer: Bazaar Package Importer
  • Author(s): Michael Koch
  • Date: 2006-05-27 16:11:15 UTC
  • mfrom: (1.1.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20060527161115-h6e39eposdt5snb6
Tags: 2:0.91-3
* Install header files to /usr/include/classpath.
* debian/control: classpath: Conflict with jamvm < 1.4.3 and
  cacao < 0.96 (Closes: #368172).

Show diffs side-by-side

added added

removed removed

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