Source for javax.swing.text.DefaultStyledDocument

   1: /* 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: }