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