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

Subversion Repositories scarts

[/] [scarts/] [trunk/] [toolchain/] [scarts-gcc/] [gcc-4.1.1/] [libjava/] [classpath/] [javax/] [swing/] [text/] [DefaultStyledDocument.java] - Blame information for rev 14

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 14 jlechner
/* DefaultStyledDocument.java --
2
   Copyright (C) 2004, 2005 Free Software Foundation, Inc.
3
 
4
This file is part of GNU Classpath.
5
 
6
GNU Classpath is free software; you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation; either version 2, or (at your option)
9
any later version.
10
 
11
GNU Classpath is distributed in the hope that it will be useful, but
12
WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
General Public License for more details.
15
 
16
You should have received a copy of the GNU General Public License
17
along with GNU Classpath; see the file COPYING.  If not, write to the
18
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
02110-1301 USA.
20
 
21
Linking this library statically or dynamically with other modules is
22
making a combined work based on this library.  Thus, the terms and
23
conditions of the GNU General Public License cover the whole
24
combination.
25
 
26
As a special exception, the copyright holders of this library give you
27
permission to link this library with independent modules to produce an
28
executable, regardless of the license terms of these independent
29
modules, and to copy and distribute the resulting executable under
30
terms of your choice, provided that you also meet, for each linked
31
independent module, the terms and conditions of the license of that
32
module.  An independent module is a module which is not derived from
33
or based on this library.  If you modify this library, you may extend
34
this exception to your version of the library, but you are not
35
obligated to do so.  If you do not wish to do so, delete this
36
exception statement from your version. */
37
 
38
 
39
package javax.swing.text;
40
 
41
import java.awt.Color;
42
import java.awt.Font;
43
import java.io.Serializable;
44
import java.util.Enumeration;
45
import java.util.Vector;
46
 
47
import javax.swing.event.ChangeEvent;
48
import javax.swing.event.ChangeListener;
49
import javax.swing.event.DocumentEvent;
50
import javax.swing.undo.AbstractUndoableEdit;
51
import javax.swing.undo.UndoableEdit;
52
 
53
/**
54
 * The default implementation of {@link StyledDocument}.
55
 *
56
 * The document is modeled as an {@link Element} tree, which has
57
 * a {@link SectionElement} as single root, which has one or more
58
 * {@link AbstractDocument.BranchElement}s as paragraph nodes
59
 * and each paragraph node having one or more
60
 * {@link AbstractDocument.LeafElement}s as content nodes.
61
 *
62
 * @author Michael Koch (konqueror@gmx.de)
63
 * @author Roman Kennke (roman@kennke.org)
64
 */
65
public class DefaultStyledDocument extends AbstractDocument
66
  implements StyledDocument
67
{
68
  /**
69
   * An {@link UndoableEdit} that can undo attribute changes to an element.
70
   *
71
   * @author Roman Kennke (kennke@aicas.com)
72
   */
73
  public static class AttributeUndoableEdit
74
    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 the element that changes attributes
101
     * @param newAtts the new attributes
102
     * @param replacing if the new attributes replace the old or only append to
103
     *        them
104
     */
105
    public AttributeUndoableEdit(Element el, AttributeSet newAtts,
106
                                 boolean replacing)
107
    {
108
      element = el;
109
      newAttributes = newAtts;
110
      isReplacing = replacing;
111
      copy = el.getAttributes().copyAttributes();
112
    }
113
 
114
    /**
115
     * Undos the attribute change. The <code>copy</code> field is set as
116
     * attributes on <code>element</code>.
117
     */
118
    public void undo()
119
    {
120
      super.undo();
121
      AttributeSet atts = element.getAttributes();
122
      if (atts instanceof MutableAttributeSet)
123
        {
124
          MutableAttributeSet mutable = (MutableAttributeSet) atts;
125
          mutable.removeAttributes(atts);
126
          mutable.addAttributes(copy);
127
        }
128
    }
129
 
130
    /**
131
     * Redos an attribute change. This adds <code>newAttributes</code> to the
132
     * <code>element</code>'s attribute set, possibly clearing all attributes
133
     * if <code>isReplacing</code> is true.
134
     */
135
    public void redo()
136
    {
137
      super.undo();
138
      AttributeSet atts = element.getAttributes();
139
      if (atts instanceof MutableAttributeSet)
140
        {
141
          MutableAttributeSet mutable = (MutableAttributeSet) atts;
142
          if (isReplacing)
143
            mutable.removeAttributes(atts);
144
          mutable.addAttributes(newAttributes);
145
        }
146
    }
147
  }
148
 
149
  /**
150
   * Carries specification information for new {@link Element}s that should
151
   * be created in {@link ElementBuffer}. This allows the parsing process
152
   * to be decoupled from the <code>Element</code> creation process.
153
   */
154
  public static class ElementSpec
155
  {
156
    /**
157
     * This indicates a start tag. This is a possible value for
158
     * {@link #getType}.
159
     */
160
    public static final short StartTagType = 1;
161
 
162
    /**
163
     * This indicates an end tag. This is a possible value for
164
     * {@link #getType}.
165
     */
166
    public static final short EndTagType = 2;
167
 
168
    /**
169
     * This indicates a content element. This is a possible value for
170
     * {@link #getType}.
171
     */
172
    public static final short ContentType = 3;
173
 
174
    /**
175
     * This indicates that the data associated with this spec should be joined
176
     * with what precedes it. This is a possible value for
177
     * {@link #getDirection}.
178
     */
179
    public static final short JoinPreviousDirection = 4;
180
 
181
    /**
182
     * This indicates that the data associated with this spec should be joined
183
     * with what follows it. This is a possible value for
184
     * {@link #getDirection}.
185
     */
186
    public static final short JoinNextDirection = 5;
187
 
188
    /**
189
     * This indicates that the data associated with this spec should be used
190
     * to create a new element. This is a possible value for
191
     * {@link #getDirection}.
192
     */
193
    public static final short OriginateDirection = 6;
194
 
195
    /**
196
     * This indicates that the data associated with this spec should be joined
197
     * to the fractured element. This is a possible value for
198
     * {@link #getDirection}.
199
     */
200
    public static final short JoinFractureDirection = 7;
201
 
202
    /**
203
     * The type of the tag.
204
     */
205
    short type;
206
 
207
    /**
208
     * The direction of the tag.
209
     */
210
    short direction;
211
 
212
    /**
213
     * The offset of the content.
214
     */
215
    int offset;
216
 
217
    /**
218
     * The length of the content.
219
     */
220
    int length;
221
 
222
    /**
223
     * The actual content.
224
     */
225
    char[] content;
226
 
227
    /**
228
     * The attributes for the tag.
229
     */
230
    AttributeSet attributes;
231
 
232
    /**
233
     * Creates a new <code>ElementSpec</code> with no content, length or
234
     * offset. This is most useful for start and end tags.
235
     *
236
     * @param a the attributes for the element to be created
237
     * @param type the type of the tag
238
     */
239
    public ElementSpec(AttributeSet a, short type)
240
    {
241
      this(a, type, 0);
242
    }
243
 
244
    /**
245
     * Creates a new <code>ElementSpec</code> that specifies the length but
246
     * not the offset of an element. Such <code>ElementSpec</code>s are
247
     * processed sequentially from a known starting point.
248
     *
249
     * @param a the attributes for the element to be created
250
     * @param type the type of the tag
251
     * @param len the length of the element
252
     */
253
    public ElementSpec(AttributeSet a, short type, int len)
254
    {
255
      this(a, type, null, 0, len);
256
    }
257
 
258
    /**
259
     * Creates a new <code>ElementSpec</code> with document content.
260
     *
261
     * @param a the attributes for the element to be created
262
     * @param type the type of the tag
263
     * @param txt the actual content
264
     * @param offs the offset into the <code>txt</code> array
265
     * @param len the length of the element
266
     */
267
    public ElementSpec(AttributeSet a, short type, char[] txt, int offs,
268
                       int len)
269
    {
270
      attributes = a;
271
      this.type = type;
272
      offset = offs;
273
      length = len;
274
      content = txt;
275
      direction = OriginateDirection;
276
    }
277
 
278
    /**
279
     * Sets the type of the element.
280
     *
281
     * @param type the type of the element to be set
282
     */
283
    public void setType(short type)
284
    {
285
      this.type = type;
286
    }
287
 
288
    /**
289
     * Returns the type of the element.
290
     *
291
     * @return the type of the element
292
     */
293
    public short getType()
294
    {
295
      return type;
296
    }
297
 
298
    /**
299
     * Sets the direction of the element.
300
     *
301
     * @param dir the direction of the element to be set
302
     */
303
    public void setDirection(short dir)
304
    {
305
      direction = dir;
306
    }
307
 
308
    /**
309
     * Returns the direction of the element.
310
     *
311
     * @return the direction of the element
312
     */
313
    public short getDirection()
314
    {
315
      return direction;
316
    }
317
 
318
    /**
319
     * Returns the attributes of the element.
320
     *
321
     * @return the attributes of the element
322
     */
323
    public AttributeSet getAttributes()
324
    {
325
      return attributes;
326
    }
327
 
328
    /**
329
     * Returns the actual content of the element.
330
     *
331
     * @return the actual content of the element
332
     */
333
    public char[] getArray()
334
    {
335
      return content;
336
    }
337
 
338
    /**
339
     * Returns the offset of the content.
340
     *
341
     * @return the offset of the content
342
     */
343
    public int getOffset()
344
    {
345
      return offset;
346
    }
347
 
348
    /**
349
     * Returns the length of the content.
350
     *
351
     * @return the length of the content
352
     */
353
    public int getLength()
354
    {
355
      return length;
356
    }
357
 
358
    /**
359
     * Returns a String representation of this <code>ElementSpec</code>
360
     * describing the type, direction and length of this
361
     * <code>ElementSpec</code>.
362
     *
363
     * @return a String representation of this <code>ElementSpec</code>
364
     */
365
    public String toString()
366
    {
367
      StringBuilder b = new StringBuilder();
368
      b.append('<');
369
      switch (type)
370
        {
371
        case StartTagType:
372
          b.append("StartTag");
373
          break;
374
        case EndTagType:
375
          b.append("EndTag");
376
          break;
377
        case ContentType:
378
          b.append("Content");
379
          break;
380
        default:
381
          b.append("??");
382
          break;
383
        }
384
 
385
      b.append(':');
386
 
387
      switch (direction)
388
        {
389
        case JoinPreviousDirection:
390
          b.append("JoinPrevious");
391
          break;
392
        case JoinNextDirection:
393
          b.append("JoinNext");
394
          break;
395
        case OriginateDirection:
396
          b.append("Originate");
397
          break;
398
        case JoinFractureDirection:
399
          b.append("Fracture");
400
          break;
401
        default:
402
          b.append("??");
403
          break;
404
        }
405
 
406
      b.append(':');
407
      b.append(length);
408
 
409
      return b.toString();
410
    }
411
  }
412
 
413
  /**
414
   * Performs all <em>structural</code> changes to the <code>Element</code>
415
   * hierarchy.
416
   */
417
  public class ElementBuffer implements Serializable
418
  {
419
    /** The serialization UID (compatible with JDK1.5). */
420
    private static final long serialVersionUID = 1688745877691146623L;
421
 
422
    /** The root element of the hierarchy. */
423
    private Element root;
424
 
425
    /** Holds the offset for structural changes. */
426
    private int offset;
427
 
428
    /** Holds the length of structural changes. */
429
    private int length;
430
 
431
    /**
432
     * Holds fractured elements during insertion of end and start tags.
433
     * Inserting an end tag may lead to fracturing of the current paragraph
434
     * element. The elements that have been cut off may be added to the
435
     * next paragraph that is created in the next start tag.
436
     */
437
    Element[] fracture;
438
 
439
    /**
440
     * The ElementChange that describes the latest changes.
441
     */
442
    DefaultDocumentEvent documentEvent;
443
 
444
    /**
445
     * Creates a new <code>ElementBuffer</code> for the specified
446
     * <code>root</code> element.
447
     *
448
     * @param root the root element for this <code>ElementBuffer</code>
449
     */
450
    public ElementBuffer(Element root)
451
    {
452
      this.root = root;
453
    }
454
 
455
    /**
456
     * Returns the root element of this <code>ElementBuffer</code>.
457
     *
458
     * @return the root element of this <code>ElementBuffer</code>
459
     */
460
    public Element getRootElement()
461
    {
462
      return root;
463
    }
464
 
465
    /**
466
     * Modifies the element structure so that the specified interval starts
467
     * and ends at an element boundary. Content and paragraph elements
468
     * are split and created as necessary.
469
     *
470
     * This also updates the <code>DefaultDocumentEvent</code> to reflect the
471
     * structural changes.
472
     *
473
     * The bulk work is delegated to {@link #changeUpdate()}.
474
     *
475
     * @param offset the start index of the interval to be changed
476
     * @param length the length of the interval to be changed
477
     * @param ev the <code>DefaultDocumentEvent</code> describing the change
478
     */
479
    public void change(int offset, int length, DefaultDocumentEvent ev)
480
    {
481
      this.offset = offset;
482
      this.length = length;
483
      documentEvent = ev;
484
      changeUpdate();
485
    }
486
 
487
    /**
488
     * Performs the actual work for {@link #change}.
489
     * The elements at the interval boundaries are split up (if necessary)
490
     * so that the interval boundaries are located at element boundaries.
491
     */
492
    protected void changeUpdate()
493
    {
494
      // Split up the element at the start offset if necessary.
495
      Element el = getCharacterElement(offset);
496
      split(el, offset);
497
 
498
      int endOffset = offset + length;
499
      el = getCharacterElement(endOffset);
500
      split(el, endOffset);
501
    }
502
 
503
    /**
504
     * Splits an element if <code>offset</code> is not alread at its boundary.
505
     *
506
     * @param el the Element to possibly split
507
     * @param offset the offset at which to possibly split
508
     */
509
    void split(Element el, int offset)
510
    {
511
      if (el instanceof AbstractElement)
512
        {
513
          AbstractElement ael = (AbstractElement) el;
514
          int startOffset = ael.getStartOffset();
515
          int endOffset = ael.getEndOffset();
516
          int len = endOffset - startOffset;
517
          if (startOffset != offset && endOffset != offset)
518
            {
519
              Element paragraph = ael.getParentElement();
520
              if (paragraph instanceof BranchElement)
521
                {
522
                  BranchElement par = (BranchElement) paragraph;
523
                  Element child1 = createLeafElement(par, ael, startOffset,
524
                                                     offset);
525
                  Element child2 = createLeafElement(par, ael, offset,
526
                                                     endOffset);
527
                  int index = par.getElementIndex(startOffset);
528
                  Element[] add = new Element[]{ child1, child2 };
529
                  par.replace(index, 1, add);
530
                  documentEvent.addEdit(new ElementEdit(par, index,
531
                                                        new Element[]{ el },
532
                                                        add));
533
                }
534
              else
535
                throw new AssertionError("paragraph elements are expected to "
536
                                         + "be instances of "
537
                          + "javax.swing.text.AbstractDocument.BranchElement");
538
            }
539
        }
540
      else
541
        throw new AssertionError("content elements are expected to be "
542
                                 + "instances of "
543
                        + "javax.swing.text.AbstractDocument.AbstractElement");
544
    }
545
 
546
    /**
547
     * Inserts new <code>Element</code> in the document at the specified
548
     * position.
549
     *
550
     * Most of the work is done by {@link #insertUpdate}, after some fields
551
     * have been prepared for it.
552
     *
553
     * @param offset the location in the document at which the content is
554
     *        inserted
555
     * @param length the length of the inserted content
556
     * @param data the element specifications for the content to be inserted
557
     * @param ev the document event that is updated to reflect the structural
558
     *        changes
559
     */
560
    public void insert(int offset, int length, ElementSpec[] data,
561
                       DefaultDocumentEvent ev)
562
    {
563
      this.offset = offset;
564
      this.length = length;
565
      documentEvent = ev;
566
      insertUpdate(data);
567
    }
568
 
569
    /**
570
     * Performs the actual structural change for {@link #insert}. This
571
     * creates a bunch of {@link Element}s as specified by <code>data</code>
572
     * and inserts it into the document as specified in the arguments to
573
     * {@link #insert}.
574
     *
575
     * @param data the element specifications for the elements to be inserte
576
     */
577
    protected void insertUpdate(ElementSpec[] data)
578
    {
579
      for (int i = 0; i < data.length; i++)
580
        {
581
          switch (data[i].getType())
582
            {
583
            case ElementSpec.StartTagType:
584
              insertStartTag(data[i]);
585
              break;
586
            case ElementSpec.EndTagType:
587
              insertEndTag(data[i]);
588
              break;
589
            default:
590
              insertContentTag(data[i]);
591
              break;
592
            }
593
        }
594
    }
595
 
596
    /**
597
     * Insert a new paragraph after the paragraph at the current position.
598
     *
599
     * @param tag the element spec that describes the element to be inserted
600
     */
601
    void insertStartTag(ElementSpec tag)
602
    {
603
      BranchElement root = (BranchElement) getDefaultRootElement();
604
      int index = root.getElementIndex(offset);
605
      if (index == -1)
606
        index = 0;
607
 
608
      BranchElement newParagraph =
609
        (BranchElement) createBranchElement(root, tag.getAttributes());
610
      newParagraph.setResolveParent(getStyle(StyleContext.DEFAULT_STYLE));
611
 
612
      // Add new paragraph into document structure.
613
      Element[] added = new Element[]{newParagraph};
614
      root.replace(index + 1, 0, added);
615
      ElementEdit edit = new ElementEdit(root, index + 1, new Element[0],
616
                                         added);
617
      documentEvent.addEdit(edit);
618
 
619
      // Maybe add fractured elements.
620
      if (tag.getDirection() == ElementSpec.JoinFractureDirection)
621
        {
622
          Element[] newFracture = new Element[fracture.length];
623
          for (int i = 0; i < fracture.length; i++)
624
            {
625
              Element oldLeaf = fracture[i];
626
              Element newLeaf = createLeafElement(newParagraph,
627
                                                  oldLeaf.getAttributes(),
628
                                                  oldLeaf.getStartOffset(),
629
                                                  oldLeaf.getEndOffset());
630
              newFracture[i] = newLeaf;
631
            }
632
          newParagraph.replace(0, 0, newFracture);
633
          edit = new ElementEdit(newParagraph, 0, new Element[0],
634
                                 fracture);
635
          documentEvent.addEdit(edit);
636
          fracture = new Element[0];
637
        }
638
    }
639
 
640
    /**
641
     * Inserts an end tag into the document structure. This cuts of the
642
     * current paragraph element, possibly fracturing it's child elements.
643
     * The fractured elements are saved so that they can be joined later
644
     * with a new paragraph element.
645
     */
646
    void insertEndTag(ElementSpec tag)
647
    {
648
      BranchElement root = (BranchElement) getDefaultRootElement();
649
      int parIndex = root.getElementIndex(offset);
650
      BranchElement paragraph = (BranchElement) root.getElement(parIndex);
651
 
652
      int index = paragraph.getElementIndex(offset);
653
      LeafElement content = (LeafElement) paragraph.getElement(index);
654
      // We might have to split the element at offset.
655
      split(content, offset);
656
      index = paragraph.getElementIndex(offset);
657
 
658
      int count = paragraph.getElementCount();
659
      // Store fractured elements.
660
      fracture = new Element[count - index];
661
      for (int i = index; i < count; ++i)
662
        fracture[i - index] = paragraph.getElement(i);
663
 
664
      // Delete fractured elements.
665
      paragraph.replace(index, count - index, new Element[0]);
666
 
667
      // Add this action to the document event.
668
      ElementEdit edit = new ElementEdit(paragraph, index, fracture,
669
                                         new Element[0]);
670
      documentEvent.addEdit(edit);
671
    }
672
 
673
    /**
674
     * Inserts a content element into the document structure.
675
     *
676
     * @param tag the element spec
677
     */
678
    void insertContentTag(ElementSpec tag)
679
    {
680
      int len = tag.getLength();
681
      int dir = tag.getDirection();
682
      if (dir == ElementSpec.JoinPreviousDirection)
683
        {
684
          Element prev = getCharacterElement(offset);
685
          BranchElement prevParent = (BranchElement) prev.getParentElement();
686
          Element join = createLeafElement(prevParent, tag.getAttributes(),
687
                                           prev.getStartOffset(),
688
                                           Math.max(prev.getEndOffset(),
689
                                                    offset + len));
690
          int ind = prevParent.getElementIndex(offset);
691
          if (ind == -1)
692
            ind = 0;
693
          Element[] add = new Element[]{join};
694
          prevParent.replace(ind, 1, add);
695
 
696
          // Add this action to the document event.
697
          ElementEdit edit = new ElementEdit(prevParent, ind,
698
                                             new Element[]{prev}, add);
699
          documentEvent.addEdit(edit);
700
        }
701
      else if (dir == ElementSpec.JoinNextDirection)
702
        {
703
          Element next = getCharacterElement(offset + len);
704
          BranchElement nextParent = (BranchElement) next.getParentElement();
705
          Element join = createLeafElement(nextParent, tag.getAttributes(),
706
                                           offset,
707
                                           next.getEndOffset());
708
          int ind = nextParent.getElementIndex(offset + len);
709
          if (ind == -1)
710
            ind = 0;
711
          Element[] add = new Element[]{join};
712
          nextParent.replace(ind, 1, add);
713
 
714
          // Add this action to the document event.
715
          ElementEdit edit = new ElementEdit(nextParent, ind,
716
                                             new Element[]{next}, add);
717
          documentEvent.addEdit(edit);
718
        }
719
      else
720
        {
721
          BranchElement par = (BranchElement) getParagraphElement(offset);
722
 
723
          int ind = par.getElementIndex(offset);
724
 
725
          // Make room for the element.
726
          // Cut previous element.
727
          Element prev = par.getElement(ind);
728
          if (prev != null && prev.getStartOffset() < offset)
729
            {
730
              Element cutPrev = createLeafElement(par, prev.getAttributes(),
731
                                                  prev.getStartOffset(),
732
                                                  offset);
733
              Element[] remove = new Element[]{prev};
734
              Element[] add = new Element[]{cutPrev};
735
              if (prev.getEndOffset() > offset + len)
736
                {
737
                  Element rem = createLeafElement(par, prev.getAttributes(),
738
                                                  offset + len,
739
                                                  prev.getEndOffset());
740
                  add = new Element[]{cutPrev, rem};
741
                }
742
 
743
              par.replace(ind, 1, add);
744
              documentEvent.addEdit(new ElementEdit(par, ind, remove, add));
745
              ind++;
746
            }
747
          // ind now points to the next element.
748
 
749
          // Cut next element if necessary.
750
          Element next = par.getElement(ind);
751
          if (next != null && next.getStartOffset() < offset + len)
752
            {
753
              Element cutNext = createLeafElement(par, next.getAttributes(),
754
                                                  offset + len,
755
                                                  next.getEndOffset());
756
              Element[] remove = new Element[]{next};
757
              Element[] add = new Element[]{cutNext};
758
              par.replace(ind, 1, add);
759
              documentEvent.addEdit(new ElementEdit(par, ind, remove,
760
                                                    add));
761
            }
762
 
763
          // Insert new element.
764
          Element newEl = createLeafElement(par, tag.getAttributes(),
765
                                            offset, offset + len);
766
          Element[] added = new Element[]{newEl};
767
          par.replace(ind, 0, added);
768
          // Add this action to the document event.
769
          ElementEdit edit = new ElementEdit(par, ind, new Element[0],
770
                                             added);
771
          documentEvent.addEdit(edit);
772
        }
773
      offset += len;
774
    }
775
 
776
    /**
777
     * Creates a copy of the element <code>clonee</code> that has the parent
778
     * <code>parent</code>.
779
     * @param parent the parent of the newly created Element
780
     * @param clonee the Element to clone
781
     * @return the cloned Element
782
     */
783
    public Element clone (Element parent, Element clonee)
784
    {
785
      // If the Element we want to clone is a leaf, then simply copy it
786
      if (clonee.isLeaf())
787
        return createLeafElement(parent, clonee.getAttributes(),
788
                                 clonee.getStartOffset(), clonee.getEndOffset());
789
 
790
      // Otherwise create a new BranchElement with the desired parent and 
791
      // the clonee's attributes
792
      BranchElement result = (BranchElement) createBranchElement(parent, clonee.getAttributes());
793
 
794
      // And clone all the of clonee's children
795
      Element[] children = new Element[clonee.getElementCount()];
796
      for (int i = 0; i < children.length; i++)
797
        children[i] = clone(result, clonee.getElement(i));
798
 
799
      // Make the cloned children the children of the BranchElement
800
      result.replace(0, 0, children);
801
      return result;
802
    }
803
  }
804
 
805
  /**
806
   * An element type for sections. This is a simple BranchElement with
807
   * a unique name.
808
   */
809
  protected class SectionElement extends BranchElement
810
  {
811
    /**
812
     * Creates a new SectionElement.
813
     */
814
    public SectionElement()
815
    {
816
      super(null, null);
817
    }
818
 
819
    /**
820
     * Returns the name of the element. This method always returns
821
     * &quot;section&quot;.
822
     *
823
     * @return the name of the element
824
     */
825
    public String getName()
826
    {
827
      return "section";
828
    }
829
  }
830
 
831
  /**
832
   * Receives notification when any of the document's style changes and calls
833
   * {@link DefaultStyledDocument#styleChanged(Style)}.
834
   *
835
   * @author Roman Kennke (kennke@aicas.com)
836
   */
837
  private class StyleChangeListener
838
    implements ChangeListener
839
  {
840
 
841
    /**
842
     * Receives notification when any of the document's style changes and calls
843
     * {@link DefaultStyledDocument#styleChanged(Style)}.
844
     *
845
     * @param event the change event
846
     */
847
    public void stateChanged(ChangeEvent event)
848
    {
849
      Style style = (Style) event.getSource();
850
      styleChanged(style);
851
    }
852
  }
853
 
854
  /** The serialization UID (compatible with JDK1.5). */
855
  private static final long serialVersionUID = 940485415728614849L;
856
 
857
  /**
858
   * The default size to use for new content buffers.
859
   */
860
  public static final int BUFFER_SIZE_DEFAULT = 4096;
861
 
862
  /**
863
   * The <code>EditorBuffer</code> that is used to manage to
864
   * <code>Element</code> hierarchy.
865
   */
866
  protected DefaultStyledDocument.ElementBuffer buffer;
867
 
868
  /**
869
   * Listens for changes on this document's styles and notifies styleChanged().
870
   */
871
  private StyleChangeListener styleChangeListener;
872
 
873
  /**
874
   * Creates a new <code>DefaultStyledDocument</code>.
875
   */
876
  public DefaultStyledDocument()
877
  {
878
    this(new GapContent(BUFFER_SIZE_DEFAULT), new StyleContext());
879
  }
880
 
881
  /**
882
   * Creates a new <code>DefaultStyledDocument</code> that uses the
883
   * specified {@link StyleContext}.
884
   *
885
   * @param context the <code>StyleContext</code> to use
886
   */
887
  public DefaultStyledDocument(StyleContext context)
888
  {
889
    this(new GapContent(BUFFER_SIZE_DEFAULT), context);
890
  }
891
 
892
  /**
893
   * Creates a new <code>DefaultStyledDocument</code> that uses the
894
   * specified {@link StyleContext} and {@link Content} buffer.
895
   *
896
   * @param content the <code>Content</code> buffer to use
897
   * @param context the <code>StyleContext</code> to use
898
   */
899
  public DefaultStyledDocument(AbstractDocument.Content content,
900
                               StyleContext context)
901
  {
902
    super(content, context);
903
    buffer = new ElementBuffer(createDefaultRoot());
904
    setLogicalStyle(0, context.getStyle(StyleContext.DEFAULT_STYLE));
905
  }
906
 
907
  /**
908
   * Adds a style into the style hierarchy. Unspecified style attributes
909
   * can be resolved in the <code>parent</code> style, if one is specified.
910
   *
911
   * While it is legal to add nameless styles (<code>nm == null</code),
912
   * you must be aware that the client application is then responsible
913
   * for managing the style hierarchy, since unnamed styles cannot be
914
   * looked up by their name.
915
   *
916
   * @param nm the name of the style or <code>null</code> if the style should
917
   *           be unnamed
918
   * @param parent the parent in which unspecified style attributes are
919
   *           resolved, or <code>null</code> if that is not necessary
920
   *
921
   * @return the newly created <code>Style</code>
922
   */
923
  public Style addStyle(String nm, Style parent)
924
  {
925
    StyleContext context = (StyleContext) getAttributeContext();
926
    Style newStyle = context.addStyle(nm, parent);
927
 
928
    // Register change listener.
929
    if (styleChangeListener == null)
930
      styleChangeListener = new StyleChangeListener();
931
    newStyle.addChangeListener(styleChangeListener);
932
 
933
    return newStyle;
934
  }
935
 
936
  /**
937
   * Create the default root element for this kind of <code>Document</code>.
938
   *
939
   * @return the default root element for this kind of <code>Document</code>
940
   */
941
  protected AbstractDocument.AbstractElement createDefaultRoot()
942
  {
943
    Element[] tmp;
944
    // FIXME: Create a SecionElement here instead of a BranchElement.
945
    // Use createBranchElement() and createLeafElement instead.
946
    SectionElement section = new SectionElement();
947
 
948
    BranchElement paragraph =
949
      (BranchElement) createBranchElement(section, null);
950
    paragraph.setResolveParent(getStyle(StyleContext.DEFAULT_STYLE));
951
    tmp = new Element[1];
952
    tmp[0] = paragraph;
953
    section.replace(0, 0, tmp);
954
 
955
    LeafElement leaf = new LeafElement(paragraph, null, 0, 1);
956
    tmp = new Element[1];
957
    tmp[0] = leaf;
958
    paragraph.replace(0, 0, tmp);
959
 
960
    return section;
961
  }
962
 
963
  /**
964
   * Returns the <code>Element</code> that corresponds to the character
965
   * at the specified position.
966
   *
967
   * @param position the position of which we query the corresponding
968
   *        <code>Element</code>
969
   *
970
   * @return the <code>Element</code> that corresponds to the character
971
   *         at the specified position
972
   */
973
  public Element getCharacterElement(int position)
974
  {
975
    Element element = getDefaultRootElement();
976
 
977
    while (!element.isLeaf())
978
      {
979
        int index = element.getElementIndex(position);
980
        element = element.getElement(index);
981
      }
982
 
983
    return element;
984
  }
985
 
986
  /**
987
   * Extracts a background color from a set of attributes.
988
   *
989
   * @param attributes the attributes from which to get a background color
990
   *
991
   * @return the background color that correspond to the attributes
992
   */
993
  public Color getBackground(AttributeSet attributes)
994
  {
995
    StyleContext context = (StyleContext) getAttributeContext();
996
    return context.getBackground(attributes);
997
  }
998
 
999
  /**
1000
   * Returns the default root element.
1001
   *
1002
   * @return the default root element
1003
   */
1004
  public Element getDefaultRootElement()
1005
  {
1006
    return buffer.getRootElement();
1007
  }
1008
 
1009
  /**
1010
   * Extracts a font from a set of attributes.
1011
   *
1012
   * @param attributes the attributes from which to get a font
1013
   *
1014
   * @return the font that correspond to the attributes
1015
   */
1016
  public Font getFont(AttributeSet attributes)
1017
  {
1018
    StyleContext context = (StyleContext) getAttributeContext();
1019
    return context.getFont(attributes);
1020
  }
1021
 
1022
  /**
1023
   * Extracts a foreground color from a set of attributes.
1024
   *
1025
   * @param attributes the attributes from which to get a foreground color
1026
   *
1027
   * @return the foreground color that correspond to the attributes
1028
   */
1029
  public Color getForeground(AttributeSet attributes)
1030
  {
1031
    StyleContext context = (StyleContext) getAttributeContext();
1032
    return context.getForeground(attributes);
1033
  }
1034
 
1035
  /**
1036
   * Returns the logical <code>Style</code> for the specified position.
1037
   *
1038
   * @param position the position from which to query to logical style
1039
   *
1040
   * @return the logical <code>Style</code> for the specified position
1041
   */
1042
  public Style getLogicalStyle(int position)
1043
  {
1044
    Element paragraph = getParagraphElement(position);
1045
    AttributeSet attributes = paragraph.getAttributes();
1046
    return (Style) attributes.getResolveParent();
1047
  }
1048
 
1049
  /**
1050
   * Returns the paragraph element for the specified position.
1051
   * If the position is outside the bounds of the document's root element,
1052
   * then the closest element is returned. That is the last paragraph if
1053
   * <code>position >= endIndex</code> or the first paragraph if
1054
   * <code>position < startIndex</code>.
1055
   *
1056
   * @param position the position for which to query the paragraph element
1057
   *
1058
   * @return the paragraph element for the specified position
1059
   */
1060
  public Element getParagraphElement(int position)
1061
  {
1062
    BranchElement root = (BranchElement) getDefaultRootElement();
1063
    int start = root.getStartOffset();
1064
    int end = root.getEndOffset();
1065
    if (position >= end)
1066
      position = end - 1;
1067
    else if (position < start)
1068
      position = start;
1069
 
1070
    Element par = root.positionToElement(position);
1071
 
1072
    assert par != null : "The paragraph element must not be null";
1073
    return par;
1074
  }
1075
 
1076
  /**
1077
   * Looks up and returns a named <code>Style</code>.
1078
   *
1079
   * @param nm the name of the <code>Style</code>
1080
   *
1081
   * @return the found <code>Style</code> of <code>null</code> if no such
1082
   *         <code>Style</code> exists
1083
   */
1084
  public Style getStyle(String nm)
1085
  {
1086
    StyleContext context = (StyleContext) getAttributeContext();
1087
    return context.getStyle(nm);
1088
  }
1089
 
1090
  /**
1091
   * Removes a named <code>Style</code> from the style hierarchy.
1092
   *
1093
   * @param nm the name of the <code>Style</code> to be removed
1094
   */
1095
  public void removeStyle(String nm)
1096
  {
1097
    StyleContext context = (StyleContext) getAttributeContext();
1098
    context.removeStyle(nm);
1099
  }
1100
 
1101
  /**
1102
   * Sets text attributes for the fragment specified by <code>offset</code>
1103
   * and <code>length</code>.
1104
   *
1105
   * @param offset the start offset of the fragment
1106
   * @param length the length of the fragment
1107
   * @param attributes the text attributes to set
1108
   * @param replace if <code>true</code>, the attributes of the current
1109
   *     selection are overridden, otherwise they are merged
1110
   */
1111
  public void setCharacterAttributes(int offset, int length,
1112
                                     AttributeSet attributes,
1113
                                     boolean replace)
1114
  {
1115
    DefaultDocumentEvent ev =
1116
      new DefaultDocumentEvent(offset, length,
1117
                               DocumentEvent.EventType.CHANGE);
1118
 
1119
    // Modify the element structure so that the interval begins at an element
1120
    // start and ends at an element end.
1121
    buffer.change(offset, length, ev);
1122
 
1123
    Element root = getDefaultRootElement();
1124
    // Visit all paragraph elements within the specified interval
1125
    int paragraphCount =  root.getElementCount();
1126
    for (int pindex = 0; pindex < paragraphCount; pindex++)
1127
      {
1128
        Element paragraph = root.getElement(pindex);
1129
        // Skip paragraphs that lie outside the interval.
1130
        if ((paragraph.getStartOffset() > offset + length)
1131
            || (paragraph.getEndOffset() < offset))
1132
          continue;
1133
 
1134
        // Visit content elements within this paragraph
1135
        int contentCount = paragraph.getElementCount();
1136
        for (int cindex = 0; cindex < contentCount; cindex++)
1137
          {
1138
            Element content = paragraph.getElement(cindex);
1139
            // Skip content that lies outside the interval.
1140
            if ((content.getStartOffset() > offset + length)
1141
                || (content.getEndOffset() < offset))
1142
              continue;
1143
 
1144
            if (content instanceof AbstractElement)
1145
              {
1146
                AbstractElement el = (AbstractElement) content;
1147
                if (replace)
1148
                  el.removeAttributes(el);
1149
                el.addAttributes(attributes);
1150
              }
1151
            else
1152
              throw new AssertionError("content elements are expected to be"
1153
                                       + "instances of "
1154
                       + "javax.swing.text.AbstractDocument.AbstractElement");
1155
          }
1156
      }
1157
 
1158
    fireChangedUpdate(ev);
1159
  }
1160
 
1161
  /**
1162
   * Sets the logical style for the paragraph at the specified position.
1163
   *
1164
   * @param position the position at which the logical style is added
1165
   * @param style the style to set for the current paragraph
1166
   */
1167
  public void setLogicalStyle(int position, Style style)
1168
  {
1169
    Element el = getParagraphElement(position);
1170
    if (el instanceof AbstractElement)
1171
      {
1172
        AbstractElement ael = (AbstractElement) el;
1173
        ael.setResolveParent(style);
1174
      }
1175
    else
1176
      throw new AssertionError("paragraph elements are expected to be"
1177
         + "instances of javax.swing.text.AbstractDocument.AbstractElement");
1178
  }
1179
 
1180
  /**
1181
   * Sets text attributes for the paragraph at the specified fragment.
1182
   *
1183
   * @param offset the beginning of the fragment
1184
   * @param length the length of the fragment
1185
   * @param attributes the text attributes to set
1186
   * @param replace if <code>true</code>, the attributes of the current
1187
   *     selection are overridden, otherwise they are merged
1188
   */
1189
  public void setParagraphAttributes(int offset, int length,
1190
                                     AttributeSet attributes,
1191
                                     boolean replace)
1192
  {
1193
    int index = offset;
1194
    while (index < offset + length)
1195
      {
1196
        AbstractElement par = (AbstractElement) getParagraphElement(index);
1197
        AttributeContext ctx = getAttributeContext();
1198
        if (replace)
1199
          par.removeAttributes(par);
1200
        par.addAttributes(attributes);
1201
        index = par.getElementCount();
1202
      }
1203
  }
1204
 
1205
  /**
1206
   * Called in response to content insert actions. This is used to
1207
   * update the element structure.
1208
   *
1209
   * @param ev the <code>DocumentEvent</code> describing the change
1210
   * @param attr the attributes for the change
1211
   */
1212
  protected void insertUpdate(DefaultDocumentEvent ev, AttributeSet attr)
1213
  {
1214
    super.insertUpdate(ev, attr);
1215
    int offset = ev.getOffset();
1216
    int length = ev.getLength();
1217
    int endOffset = offset + length;
1218
    Segment txt = new Segment();
1219
    try
1220
      {
1221
        getText(offset, length, txt);
1222
      }
1223
    catch (BadLocationException ex)
1224
      {
1225
        AssertionError ae = new AssertionError("Unexpected bad location");
1226
        ae.initCause(ex);
1227
        throw ae;
1228
      }
1229
 
1230
    int len = 0;
1231
    Vector specs = new Vector();
1232
 
1233
    Element prev = getCharacterElement(offset);
1234
    Element next = getCharacterElement(endOffset);
1235
 
1236
    for (int i = offset; i < endOffset; ++i)
1237
      {
1238
        len++;
1239
        if (txt.array[i] == '\n')
1240
          {
1241
            ElementSpec spec = new ElementSpec(attr, ElementSpec.ContentType,
1242
                                               len);
1243
 
1244
            // If we are at the last index, then check if we could probably be
1245
            // joined with the next element.
1246
            if (i == endOffset - 1)
1247
              {
1248
                if (next.getAttributes().isEqual(attr))
1249
                  spec.setDirection(ElementSpec.JoinNextDirection);
1250
              }
1251
            // If we are at the first new element, then check if it could be
1252
            // joined with the previous element.
1253
            else if (specs.size() == 0)
1254
              {
1255
                if (prev.getAttributes().isEqual(attr))
1256
                    spec.setDirection(ElementSpec.JoinPreviousDirection);
1257
              }
1258
 
1259
            specs.add(spec);
1260
 
1261
            // Add ElementSpecs for the newline.
1262
            ElementSpec endTag = new ElementSpec(null, ElementSpec.EndTagType);
1263
            specs.add(endTag);
1264
            ElementSpec startTag = new ElementSpec(null,
1265
                                                   ElementSpec.StartTagType);
1266
            startTag.setDirection(ElementSpec.JoinFractureDirection);
1267
            specs.add(startTag);
1268
 
1269
            len = 0;
1270
            offset += len;
1271
          }
1272
      }
1273
 
1274
    // Create last element if last character hasn't been a newline.
1275
    if (len > 0)
1276
      {
1277
        ElementSpec spec = new ElementSpec(attr, ElementSpec.ContentType, len);
1278
        // If we are at the first new element, then check if it could be
1279
        // joined with the previous element.
1280
        if (specs.size() == 0)
1281
          {
1282
            if (prev.getAttributes().isEqual(attr))
1283
              spec.setDirection(ElementSpec.JoinPreviousDirection);
1284
          }
1285
        // Check if we could probably be joined with the next element.
1286
        else if (next.getAttributes().isEqual(attr))
1287
          spec.setDirection(ElementSpec.JoinNextDirection);
1288
 
1289
        specs.add(spec);
1290
      }
1291
 
1292
    ElementSpec[] elSpecs =
1293
      (ElementSpec[]) specs.toArray(new ElementSpec[specs.size()]);
1294
 
1295
    buffer.insert(offset, length, elSpecs, ev);
1296
  }
1297
 
1298
  /**
1299
   * Returns an enumeration of all style names.
1300
   *
1301
   * @return an enumeration of all style names
1302
   */
1303
  public Enumeration getStyleNames()
1304
  {
1305
    StyleContext context = (StyleContext) getAttributeContext();
1306
    return context.getStyleNames();
1307
  }
1308
 
1309
  /**
1310
   * Called when any of this document's styles changes.
1311
   *
1312
   * @param style the style that changed
1313
   */
1314
  protected void styleChanged(Style style)
1315
  {
1316
    // Nothing to do here. This is intended to be overridden by subclasses.
1317
  }
1318
 
1319
  /**
1320
   * Inserts a bulk of structured content at once.
1321
   *
1322
   * @param offset the offset at which the content should be inserted
1323
   * @param data the actual content spec to be inserted
1324
   */
1325
  protected void insert(int offset, ElementSpec[] data)
1326
    throws BadLocationException
1327
  {
1328
    writeLock();
1329
    // First we insert the content.
1330
    int index = offset;
1331
    for (int i = 0; i < data.length; i++)
1332
      {
1333
        ElementSpec spec = data[i];
1334
        if (spec.getArray() != null && spec.getLength() > 0)
1335
          {
1336
            String insertString = new String(spec.getArray(), spec.getOffset(),
1337
                                             spec.getLength());
1338
            content.insertString(index, insertString);
1339
          }
1340
        index += spec.getLength();
1341
      }
1342
    // Update the view structure.
1343
    DefaultDocumentEvent ev = new DefaultDocumentEvent(offset, index - offset,
1344
                                               DocumentEvent.EventType.INSERT);
1345
    for (int i = 0; i < data.length; i++)
1346
      {
1347
        ElementSpec spec = data[i];
1348
        AttributeSet atts = spec.getAttributes();
1349
        if (atts != null)
1350
          insertUpdate(ev, atts);
1351
      }
1352
 
1353
    // Finally we must update the document structure and fire the insert update
1354
    // event.
1355
    buffer.insert(offset, index - offset, data, ev);
1356
    fireInsertUpdate(ev);
1357
    writeUnlock();
1358
  }
1359
 
1360
  /**
1361
   * Initializes the <code>DefaultStyledDocument</code> with the specified
1362
   * data.
1363
   *
1364
   * @param data the specification of the content with which the document is
1365
   *        initialized
1366
   */
1367
  protected void create(ElementSpec[] data)
1368
  {
1369
    try
1370
      {
1371
        // Clear content.
1372
        content.remove(0, content.length());
1373
        // Clear buffer and root element.
1374
        buffer = new ElementBuffer(createDefaultRoot());
1375
        // Insert the data.
1376
        insert(0, data);
1377
      }
1378
    catch (BadLocationException ex)
1379
      {
1380
        AssertionError err = new AssertionError("Unexpected bad location");
1381
        err.initCause(ex);
1382
        throw err;
1383
      }
1384
  }
1385
}

powered by: WebSVN 2.1.0

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