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:           else
1058:             paragraph.replace(0, 0, new Element[] { leaf });
1059:         }
1060:                             
1061:       pos += len;
1062:     }
1063: 
1064:     /**
1065:      * This method fractures the child at offset.
1066:      * 
1067:      * @param data
1068:      *          the ElementSpecs used for the entire insertion
1069:      */
1070:     private void createFracture(ElementSpec[] data)
1071:     {
1072:       BranchElement paragraph = (BranchElement) elementStack.peek();
1073:       int index = paragraph.getElementIndex(offset);
1074:       Element child = paragraph.getElement(index);
1075:       Edit edit = getEditForParagraphAndIndex(paragraph, index);
1076:       AttributeSet atts = child.getAttributes();
1077:       
1078:       if (offset != 0)
1079:         {
1080:           Element newEl1 = createLeafElement(paragraph, atts,
1081:                                              child.getStartOffset(), offset);
1082:           edit.addAddedElement(newEl1);
1083:           edit.addRemovedElement(child);
1084:         }
1085:     }
1086: 
1087:     /**
1088:      * Recreates a specified part of a the tree after a new leaf
1089:      * has been inserted.
1090:      * 
1091:      * @param start - where to start recreating from
1092:      * @param paragraph - the paragraph to recreate
1093:      * @param onlyContent - true if this is the only content
1094:      */
1095:     private void recreateLeaves(int start, BranchElement paragraph, boolean onlyContent)
1096:     {
1097:       int index = paragraph.getElementIndex(start);
1098:       Element child = paragraph.getElement(index);
1099:       AttributeSet atts = child.getAttributes();
1100:       
1101:       if (!onlyContent)
1102:         {
1103:           BranchElement newBranch = (BranchElement) createBranchElement(paragraph,
1104:                                                                         atts);
1105:           Element newLeaf = createLeafElement(newBranch, atts, start, 
1106:                                               child.getEndOffset());
1107:           newBranch.replace(0, 0, new Element[] { newLeaf });
1108:           
1109:           BranchElement parent = (BranchElement) paragraph.getParentElement();
1110:           int parSize = parent.getElementCount();
1111:           Edit edit = getEditForParagraphAndIndex(parent, parSize);
1112:           edit.addAddedElement(newBranch);
1113:             
1114:           int paragraphSize = paragraph.getElementCount();
1115:           Element[] removed = new Element[paragraphSize - (index + 1)];
1116:           int s = 0;
1117:           for (int j = index + 1; j < paragraphSize; j++)
1118:             removed[s++] = paragraph.getElement(j);
1119:           
1120:           edit = getEditForParagraphAndIndex(paragraph, index);
1121:           edit.addRemovedElements(removed);
1122:           Element[] added = recreateAfterFracture(removed, newBranch, 0, child.getEndOffset());
1123:           newBranch.replace(1, 0, added);
1124:           
1125:           lastFractured = newLeaf;
1126:           pos = newBranch.getEndOffset();
1127:         }
1128:       else
1129:         {
1130:           Element newLeaf = createLeafElement(paragraph, atts, start, 
1131:                                               child.getEndOffset());
1132:           Edit edit = getEditForParagraphAndIndex(paragraph, index);
1133:           edit.addAddedElement(newLeaf);
1134:         }
1135:     }
1136:     
1137:     /**
1138:      * Splits an element if <code>offset</code> is not already at its
1139:      * boundary.
1140:      * 
1141:      * @param el
1142:      *          the Element to possibly split
1143:      * @param offset
1144:      *          the offset at which to possibly split
1145:      * @param space
1146:      *          the amount of space to create between the splitted parts
1147:      * @param editIndex 
1148:      *          the index of the edit to use
1149:      * @return An array of elements which represent the split result. This array
1150:      *         has two elements, the two parts of the split. The first element
1151:      *         might be null, which means that the element which should be
1152:      *         splitted can remain in place. The second element might also be
1153:      *         null, which means that the offset is already at an element
1154:      *         boundary and the element doesn't need to be splitted.
1155:      */
1156:     private Element[] split(Element el, int offset, int space, int editIndex)
1157:     {
1158:       // If we are at an element boundary, then return an empty array.
1159:       if ((offset == el.getStartOffset() || offset == el.getEndOffset())
1160:           && space == 0 && el.isLeaf())
1161:         return new Element[2];
1162: 
1163:       // If the element is an instance of BranchElement, then we
1164:       // recursivly
1165:       // call this method to perform the split.
1166:       Element[] res = new Element[2];
1167:       if (el instanceof BranchElement)
1168:         {
1169:           int index = el.getElementIndex(offset);
1170:           Element child = el.getElement(index);
1171:           Element[] result = split(child, offset, space, editIndex);
1172:           Element[] removed;
1173:           Element[] added;
1174:           Element[] newAdded;
1175: 
1176:           int count = el.getElementCount();
1177:           if (result[1] != null)
1178:             {
1179:               // This is the case when we can keep the first element.
1180:               if (result[0] == null)
1181:                 {
1182:                   removed = new Element[count - index - 1];
1183:                   newAdded = new Element[count - index - 1];
1184:                   added = new Element[] {};
1185: 
1186:                 }
1187:               // This is the case when we may not keep the first
1188:               // element.
1189:               else
1190:                 {
1191:                   removed = new Element[count - index];
1192:                   newAdded = new Element[count - index];
1193:                   added = new Element[] { result[0] };
1194:                 }
1195:               newAdded[0] = result[1];
1196:               for (int i = index; i < count; i++)
1197:                 {
1198:                   Element el2 = el.getElement(i);
1199:                   int ind = i - count + removed.length;
1200:                   removed[ind] = el2;
1201:                   if (ind != 0)
1202:                     newAdded[ind] = el2;
1203:                 }
1204:               
1205:               Edit edit = getEditForParagraphAndIndex((BranchElement) el, editIndex);
1206:               edit.addRemovedElements(removed);
1207:               edit.addAddedElements(added);
1208:               
1209:               BranchElement newPar =
1210:                 (BranchElement) createBranchElement(el.getParentElement(),
1211:                                                     el.getAttributes());
1212:               newPar.replace(0, 0, newAdded);
1213:               res = new Element[] { null, newPar };
1214:             }
1215:           else
1216:             {
1217:               removed = new Element[count - index];
1218:               for (int i = index; i < count; ++i)
1219:                 removed[i - index] = el.getElement(i);
1220:               
1221:               Edit edit = getEditForParagraphAndIndex((BranchElement) el, editIndex);
1222:               edit.addRemovedElements(removed);
1223:               
1224:               BranchElement newPar = (BranchElement) createBranchElement(el.getParentElement(),
1225:                                                                          el.getAttributes());
1226:               newPar.replace(0, 0, removed);
1227:               res = new Element[] { null, newPar };
1228:             }
1229:         }
1230:       else if (el instanceof LeafElement)
1231:         {
1232:           BranchElement par = (BranchElement) el.getParentElement();
1233:           Element el1 = createLeafElement(par, el.getAttributes(),
1234:                                           el.getStartOffset(), offset);
1235: 
1236:           Element el2 = createLeafElement(par, el.getAttributes(), 
1237:                                           offset + space,
1238:                                           el.getEndOffset());
1239:           res = new Element[] { el1, el2 };
1240:         }
1241:       return res;
1242:     }
1243: 
1244:     /**
1245:      * Inserts a fracture into the document structure.
1246:      * 
1247:      * @param tag -
1248:      *          the element spec.
1249:      */
1250:     private void insertFracture(ElementSpec tag)
1251:     {
1252:       // insert the fracture at offset.
1253:       BranchElement parent = (BranchElement) elementStack.peek();
1254:       int parentIndex = parent.getElementIndex(pos);
1255:       AttributeSet parentAtts = parent.getAttributes();
1256:       Element toFracture = parent.getElement(parentIndex);
1257:       int parSize = parent.getElementCount();
1258:       Edit edit = getEditForParagraphAndIndex(parent, parentIndex);
1259:       Element frac = toFracture;
1260:       int leftIns = 0;
1261:       int indexOfFrac = toFracture.getElementIndex(pos);
1262:       int size = toFracture.getElementCount();
1263: 
1264:       // gets the leaf that falls along the fracture
1265:       frac = toFracture.getElement(indexOfFrac);
1266:       while (!frac.isLeaf())
1267:         frac = frac.getElement(frac.getElementIndex(pos));
1268: 
1269:       AttributeSet atts = frac.getAttributes();
1270:       int fracStart = frac.getStartOffset();
1271:       int fracEnd = frac.getEndOffset();
1272:       if (pos >= fracStart && pos < fracEnd)
1273:         {
1274:           // recreate left-side of branch and all its children before offset
1275:           // add the fractured leaves to the right branch
1276:           BranchElement rightBranch =
1277:             (BranchElement) createBranchElement(parent, parentAtts);
1278:           
1279:           // Check if left branch has already been edited. If so, we only
1280:           // need to create the right branch.
1281:           BranchElement leftBranch = null;
1282:           Element[] added = null;
1283:           if (edit.added.size() > 0 || edit.removed.size() > 0)
1284:             {
1285:               added = new Element[] { rightBranch };
1286:               
1287:               // don't try to remove left part of tree
1288:               parentIndex++;
1289:             }
1290:           else
1291:             {
1292:               leftBranch =
1293:                 (BranchElement) createBranchElement(parent, parentAtts);
1294:               added = new Element[] { leftBranch, rightBranch };
1295: 
1296:               // add fracture to leftBranch
1297:               if (fracStart != pos)
1298:                 {
1299:                   Element leftFracturedLeaf =
1300:                     createLeafElement(leftBranch, atts, fracStart, pos);
1301:                   leftBranch.replace(leftIns, 0,
1302:                                      new Element[] { leftFracturedLeaf });
1303:                 }
1304:             }
1305: 
1306:           if (!toFracture.isLeaf())
1307:             {
1308:               // add all non-fracture elements to the branches
1309:               if (indexOfFrac > 0 && leftBranch != null)
1310:                 {
1311:                   Element[] add = new Element[indexOfFrac];
1312:                   for (int i = 0; i < indexOfFrac; i++)
1313:                     add[i] = toFracture.getElement(i);
1314:                   leftIns = add.length;
1315:                   leftBranch.replace(0, 0, add);
1316:                 }
1317: 
1318:               int count = size - indexOfFrac - 1;
1319:               if (count > 0)
1320:                 {
1321:                   Element[] add = new Element[count];
1322:                   int j = 0;
1323:                   int i = indexOfFrac + 1;
1324:                   while (j < count)
1325:                     add[j++] = toFracture.getElement(i++);
1326:                   rightBranch.replace(0, 0, add);
1327:                 }
1328:             }
1329:           
1330:           // add to fracture to rightBranch          
1331:           // Check if we can join the right frac leaf with the next leaf
1332:           int rm = 0;
1333:           int end = fracEnd;
1334:           Element next = rightBranch.getElement(0);
1335:           if (next != null && next.isLeaf()
1336:               && next.getAttributes().isEqual(atts))
1337:             {
1338:               end = next.getEndOffset();
1339:               rm = 1;
1340:             }
1341: 
1342:           Element rightFracturedLeaf = createLeafElement(rightBranch, atts,
1343:                                                          pos, end);
1344:           rightBranch.replace(0, rm, new Element[] { rightFracturedLeaf });
1345: 
1346:           // recreate those elements after parentIndex and add/remove all
1347:           // new/old elements to parent
1348:           int remove = parSize - parentIndex;
1349:           Element[] removed = new Element[0];
1350:           Element[] added2 = new Element[0];
1351:           if (remove > 0)
1352:             {
1353:               removed = new Element[remove];
1354:               int s = 0;
1355:               for (int j = parentIndex; j < parSize; j++)
1356:                 removed[s++] = parent.getElement(j);
1357:               edit.addRemovedElements(removed);
1358:               added2 = recreateAfterFracture(removed, parent, 1,
1359:                                             rightBranch.getEndOffset());
1360:             }
1361:           
1362:           edit.addAddedElements(added);
1363:           edit.addAddedElements(added2);
1364:           elementStack.push(rightBranch);
1365:           lastFractured = rightFracturedLeaf;
1366:         }
1367:       else
1368:         fracNotCreated = true;
1369:     }
1370: 
1371:     /**
1372:      * Recreates all the elements from the parent to the element on the top of
1373:      * the stack, starting from startFrom with the starting offset of
1374:      * startOffset.
1375:      * 
1376:      * @param recreate -
1377:      *          the elements to recreate
1378:      * @param parent -
1379:      *          the element to add the new elements to
1380:      * @param startFrom -
1381:      *          where to start recreating from
1382:      * @param startOffset -
1383:      *          the offset of the first element
1384:      * @return the array of added elements         
1385:      */
1386:     private Element[] recreateAfterFracture(Element[] recreate,
1387:                                        BranchElement parent, int startFrom,
1388:                                        int startOffset)
1389:     {
1390:       Element[] added = new Element[recreate.length - startFrom];
1391:       int j = 0;
1392:       for (int i = startFrom; i < recreate.length; i++)
1393:         {
1394:           Element curr = recreate[i];
1395:           int len = curr.getEndOffset() - curr.getStartOffset();
1396:           if (curr instanceof LeafElement)
1397:             added[j] = createLeafElement(parent, curr.getAttributes(),
1398:                                          startOffset, startOffset + len);
1399:           else
1400:             {
1401:               BranchElement br =
1402:                 (BranchElement) createBranchElement(parent,
1403:                                                     curr.getAttributes());
1404:               int bSize = curr.getElementCount();
1405:               for (int k = 0; k < bSize; k++)
1406:                 {
1407:                   Element bCurr = curr.getElement(k);
1408:                   Element[] add = recreateAfterFracture(new Element[] { bCurr }, br, 0,
1409:                                         startOffset);
1410:                   br.replace(0, 0, add);
1411:                   
1412:                 }
1413:               added[j] = br;
1414:             }
1415:           startOffset += len;
1416:           j++;
1417:         }
1418: 
1419:       return added;
1420:     }
1421:   }
1422: 
1423:   /**
1424:    * This method looks through the Vector of Edits to see if there is already an
1425:    * Edit object associated with the given paragraph. If there is, then we
1426:    * return it. Otherwise we create a new Edit object, add it to the vector, and
1427:    * return it. Note: this method is package private to avoid accessors.
1428:    * 
1429:    * @param index
1430:    *          the index associated with the Edit we want to create
1431:    * @param para
1432:    *          the paragraph associated with the Edit we want
1433:    * @return the found or created Edit object
1434:    */
1435:   Edit getEditForParagraphAndIndex(BranchElement para, int index)
1436:   {
1437:     Edit curr;
1438:     int size = edits.size();
1439:     for (int i = 0; i < size; i++)
1440:       {
1441:         curr = (Edit) edits.elementAt(i);
1442:         if (curr.e.equals(para))
1443:           return curr;
1444:       }
1445:     curr = new Edit(para, index, null, null);
1446:     edits.add(curr);
1447:     
1448:     return curr;
1449:   }
1450:   /**
1451:    * Instance of all editing information for an object in the Vector. This class
1452:    * is used to add information to the DocumentEvent associated with an
1453:    * insertion/removal/change as well as to store the changes that need to be
1454:    * made so they can be made all at the same (appropriate) time.
1455:    */
1456:   class Edit
1457:   {
1458:     /** The element to edit . */
1459:     Element e;
1460: 
1461:     /** The index of the change. */
1462:     int index;
1463: 
1464:     /** The removed elements. */
1465:     Vector removed = new Vector();
1466: 
1467:     /** The added elements. */
1468:     Vector added = new Vector();
1469: 
1470:     /**
1471:      * Return an array containing the Elements that have been removed from the
1472:      * paragraph associated with this Edit.
1473:      * 
1474:      * @return an array of removed Elements
1475:      */
1476:     public Element[] getRemovedElements()
1477:     {
1478:       int size = removed.size();
1479:       Element[] removedElements = new Element[size];
1480:       for (int i = 0; i < size; i++)
1481:         removedElements[i] = (Element) removed.elementAt(i);
1482:       return removedElements;
1483:     }
1484: 
1485:     /**
1486:      * Return an array containing the Elements that have been added to the
1487:      * paragraph associated with this Edit.
1488:      * 
1489:      * @return an array of added Elements
1490:      */
1491:     public Element[] getAddedElements()
1492:     {
1493:       int size = added.size();
1494:       Element[] addedElements = new Element[size];
1495:       for (int i = 0; i < size; i++)
1496:         addedElements[i] = (Element) added.elementAt(i);
1497:       return addedElements;
1498:     }
1499:     
1500:     /** 
1501:      * Checks if e is already in the vector.
1502:      * 
1503:      * @param e - the Element to look for
1504:      * @param v - the vector to search
1505:      * @return true if e is in v.
1506:      */
1507:     private boolean contains(Vector v, Element e)
1508:     {
1509:       if (e == null)
1510:         return false;
1511:       
1512:       int i = v.size();
1513:       for (int j = 0; j < i; j++)
1514:         {
1515:           Element e1 = (Element) v.get(j);
1516:           if ((e1 != null) && (e1.getAttributes().isEqual(e.getAttributes()))
1517:               && (e1.getName().equals(e.getName()))
1518:               && (e1.getStartOffset() == e.getStartOffset())
1519:               && (e1.getEndOffset() == e.getEndOffset())
1520:               && (e1.getParentElement().equals(e.getParentElement()))
1521:               && (e1.getElementCount() == e.getElementCount()))
1522:             return true;
1523:         }
1524:       return false;
1525:     }
1526: 
1527:     /**
1528:      * Adds one Element to the vector of removed Elements.
1529:      * 
1530:      * @param e
1531:      *          the Element to add
1532:      */
1533:     public void addRemovedElement(Element e)
1534:     {
1535:       if (!contains(removed, e))
1536:         removed.add(e);
1537:     }
1538: 
1539:     /**
1540:      * Adds each Element in the given array to the vector of removed Elements
1541:      * 
1542:      * @param e
1543:      *          the array containing the Elements to be added
1544:      */
1545:     public void addRemovedElements(Element[] e)
1546:     {
1547:       if (e == null || e.length == 0)
1548:         return;
1549:       for (int i = 0; i < e.length; i++)
1550:         {
1551:           if (!contains(removed, e[i]))
1552:             removed.add(e[i]);
1553:         }
1554:     }
1555: 
1556:     /**
1557:      * Adds one Element to the vector of added Elements.
1558:      * 
1559:      * @param e
1560:      *          the Element to add
1561:      */
1562:     public void addAddedElement(Element e)
1563:     {
1564:       if (!contains(added, e))
1565:         added.add(e);
1566:     }
1567: 
1568:     /**
1569:      * Adds each Element in the given array to the vector of added Elements.
1570:      * 
1571:      * @param e
1572:      *          the array containing the Elements to be added
1573:      */
1574:     public void addAddedElements(Element[] e)
1575:     {
1576:       if (e == null || e.length == 0)
1577:         return;
1578:       for (int i = 0; i < e.length; i++)
1579:         {
1580:           if (!contains(added, e[i]))
1581:             added.add(e[i]);
1582:         }
1583:     }
1584: 
1585:     /**
1586:      * Creates a new Edit object with the given parameters
1587:      * 
1588:      * @param e
1589:      *          the paragraph Element associated with this Edit
1590:      * @param i
1591:      *          the index within the paragraph where changes are started
1592:      * @param removed
1593:      *          an array containing Elements that should be removed from the
1594:      *          paragraph Element
1595:      * @param added
1596:      *          an array containing Elements that should be added to the
1597:      *          paragraph Element
1598:      */
1599:     public Edit(Element e, int i, Element[] removed, Element[] added)
1600:     {
1601:       this.e = e;
1602:       this.index = i;
1603:       addRemovedElements(removed);
1604:       addAddedElements(added);
1605:     }
1606:   }
1607: 
1608:   /**
1609:    * An element type for sections. This is a simple BranchElement with a unique
1610:    * name.
1611:    */
1612:   protected class SectionElement extends BranchElement
1613:   {
1614:     /**
1615:      * Creates a new SectionElement.
1616:      */
1617:     public SectionElement()
1618:     {
1619:       super(null, null);
1620:     }
1621: 
1622:     /**
1623:      * Returns the name of the element. This method always returns
1624:      * &quot;section&quot;.
1625:      * 
1626:      * @return the name of the element
1627:      */
1628:     public String getName()
1629:     {
1630:       return SectionElementName;
1631:     }
1632:   }
1633: 
1634:   /**
1635:    * Receives notification when any of the document's style changes and calls
1636:    * {@link DefaultStyledDocument#styleChanged(Style)}.
1637:    * 
1638:    * @author Roman Kennke (kennke@aicas.com)
1639:    */
1640:   private class StyleChangeListener implements ChangeListener
1641:   {
1642: 
1643:     /**
1644:      * Receives notification when any of the document's style changes and calls
1645:      * {@link DefaultStyledDocument#styleChanged(Style)}.
1646:      * 
1647:      * @param event
1648:      *          the change event
1649:      */
1650:     public void stateChanged(ChangeEvent event)
1651:     {
1652:       Style style = (Style) event.getSource();
1653:       styleChanged(style);
1654:     }
1655:   }
1656: 
1657:   /** The serialization UID (compatible with JDK1.5). */
1658:   private static final long serialVersionUID = 940485415728614849L;
1659: 
1660:   /**
1661:    * The default size to use for new content buffers.
1662:    */
1663:   public static final int BUFFER_SIZE_DEFAULT = 4096;
1664: 
1665:   /**
1666:    * The <code>EditorBuffer</code> that is used to manage to
1667:    * <code>Element</code> hierarchy.
1668:    */
1669:   protected DefaultStyledDocument.ElementBuffer buffer;
1670: 
1671:   /**
1672:    * Listens for changes on this document's styles and notifies styleChanged().
1673:    */
1674:   private StyleChangeListener styleChangeListener;
1675: 
1676:   /**
1677:    * Vector that contains all the edits. Maybe replace by a HashMap.
1678:    */
1679:   Vector edits = new Vector();
1680: 
1681:   /**
1682:    * Creates a new <code>DefaultStyledDocument</code>.
1683:    */
1684:   public DefaultStyledDocument()
1685:   {
1686:     this(new GapContent(BUFFER_SIZE_DEFAULT), new StyleContext());
1687:   }
1688: 
1689:   /**
1690:    * Creates a new <code>DefaultStyledDocument</code> that uses the specified
1691:    * {@link StyleContext}.
1692:    * 
1693:    * @param context
1694:    *          the <code>StyleContext</code> to use
1695:    */
1696:   public DefaultStyledDocument(StyleContext context)
1697:   {
1698:     this(new GapContent(BUFFER_SIZE_DEFAULT), context);
1699:   }
1700: 
1701:   /**
1702:    * Creates a new <code>DefaultStyledDocument</code> that uses the specified
1703:    * {@link StyleContext} and {@link Content} buffer.
1704:    * 
1705:    * @param content
1706:    *          the <code>Content</code> buffer to use
1707:    * @param context
1708:    *          the <code>StyleContext</code> to use
1709:    */
1710:   public DefaultStyledDocument(AbstractDocument.Content content,
1711:                                StyleContext context)
1712:   {
1713:     super(content, context);
1714:     buffer = new ElementBuffer(createDefaultRoot());
1715:     setLogicalStyle(0, context.getStyle(StyleContext.DEFAULT_STYLE));
1716:   }
1717: 
1718:   /**
1719:    * Adds a style into the style hierarchy. Unspecified style attributes can be
1720:    * resolved in the <code>parent</code> style, if one is specified. While it
1721:    * is legal to add nameless styles (<code>nm == null</code),
1722:    * you must be aware that the client application is then responsible
1723:    * for managing the style hierarchy, since unnamed styles cannot be
1724:    * looked up by their name.
1725:    *
1726:    * @param nm the name of the style or <code>null</code> if the style should
1727:    *           be unnamed
1728:    * @param parent the parent in which unspecified style attributes are
1729:    *           resolved, or <code>null</code> if that is not necessary
1730:    *
1731:    * @return the newly created <code>Style</code>
1732:    */
1733:   public Style addStyle(String nm, Style parent)
1734:   {
1735:     StyleContext context = (StyleContext) getAttributeContext();
1736:     Style newStyle = context.addStyle(nm, parent);
1737: 
1738:     // Register change listener.
1739:     if (styleChangeListener == null)
1740:       styleChangeListener = new StyleChangeListener();
1741:     newStyle.addChangeListener(styleChangeListener);
1742: 
1743:     return newStyle;
1744:   }
1745: 
1746:   /**
1747:    * Create the default root element for this kind of <code>Document</code>.
1748:    * 
1749:    * @return the default root element for this kind of <code>Document</code>
1750:    */
1751:   protected AbstractDocument.AbstractElement createDefaultRoot()
1752:   {
1753:     Element[] tmp;
1754:     SectionElement section = new SectionElement();
1755: 
1756:     BranchElement paragraph = new BranchElement(section, null);
1757:     tmp = new Element[1];
1758:     tmp[0] = paragraph;
1759:     section.replace(0, 0, tmp);
1760: 
1761:     Element leaf = new LeafElement(paragraph, null, 0, 1);
1762:     tmp = new Element[1];
1763:     tmp[0] = leaf;
1764:     paragraph.replace(0, 0, tmp);
1765: 
1766:     return section;
1767:   }
1768: 
1769:   /**
1770:    * Returns the <code>Element</code> that corresponds to the character at the
1771:    * specified position.
1772:    * 
1773:    * @param position
1774:    *          the position of which we query the corresponding
1775:    *          <code>Element</code>
1776:    * @return the <code>Element</code> that corresponds to the character at the
1777:    *         specified position
1778:    */
1779:   public Element getCharacterElement(int position)
1780:   {
1781:     Element element = getDefaultRootElement();
1782: 
1783:     while (!element.isLeaf())
1784:       {
1785:         int index = element.getElementIndex(position);
1786:         element = element.getElement(index);
1787:       }
1788: 
1789:     return element;
1790:   }
1791: 
1792:   /**
1793:    * Extracts a background color from a set of attributes.
1794:    * 
1795:    * @param attributes
1796:    *          the attributes from which to get a background color
1797:    * @return the background color that correspond to the attributes
1798:    */
1799:   public Color getBackground(AttributeSet attributes)
1800:   {
1801:     StyleContext context = (StyleContext) getAttributeContext();
1802:     return context.getBackground(attributes);
1803:   }
1804: 
1805:   /**
1806:    * Returns the default root element.
1807:    * 
1808:    * @return the default root element
1809:    */
1810:   public Element getDefaultRootElement()
1811:   {
1812:     return buffer.getRootElement();
1813:   }
1814: 
1815:   /**
1816:    * Extracts a font from a set of attributes.
1817:    * 
1818:    * @param attributes
1819:    *          the attributes from which to get a font
1820:    * @return the font that correspond to the attributes
1821:    */
1822:   public Font getFont(AttributeSet attributes)
1823:   {
1824:     StyleContext context = (StyleContext) getAttributeContext();
1825:     return context.getFont(attributes);
1826:   }
1827: 
1828:   /**
1829:    * Extracts a foreground color from a set of attributes.
1830:    * 
1831:    * @param attributes
1832:    *          the attributes from which to get a foreground color
1833:    * @return the foreground color that correspond to the attributes
1834:    */
1835:   public Color getForeground(AttributeSet attributes)
1836:   {
1837:     StyleContext context = (StyleContext) getAttributeContext();
1838:     return context.getForeground(attributes);
1839:   }
1840: 
1841:   /**
1842:    * Returns the logical <code>Style</code> for the specified position.
1843:    * 
1844:    * @param position
1845:    *          the position from which to query to logical style
1846:    * @return the logical <code>Style</code> for the specified position
1847:    */
1848:   public Style getLogicalStyle(int position)
1849:   {
1850:     Element paragraph = getParagraphElement(position);
1851:     AttributeSet attributes = paragraph.getAttributes();
1852:     AttributeSet a = attributes.getResolveParent();
1853:     // If the resolve parent is not of type Style, we return null.
1854:     if (a instanceof Style)
1855:       return (Style) a;
1856:     return null;
1857:   }
1858: 
1859:   /**
1860:    * Returns the paragraph element for the specified position. If the position
1861:    * is outside the bounds of the document's root element, then the closest
1862:    * element is returned. That is the last paragraph if
1863:    * <code>position >= endIndex</code> or the first paragraph if
1864:    * <code>position < startIndex</code>.
1865:    * 
1866:    * @param position
1867:    *          the position for which to query the paragraph element
1868:    * @return the paragraph element for the specified position
1869:    */
1870:   public Element getParagraphElement(int position)
1871:   {
1872:     Element e = getDefaultRootElement();
1873:     while (!e.isLeaf())
1874:       e = e.getElement(e.getElementIndex(position));
1875: 
1876:     if (e != null)
1877:       return e.getParentElement();
1878:     return e;
1879:   }
1880: 
1881:   /**
1882:    * Looks up and returns a named <code>Style</code>.
1883:    * 
1884:    * @param nm
1885:    *          the name of the <code>Style</code>
1886:    * @return the found <code>Style</code> of <code>null</code> if no such
1887:    *         <code>Style</code> exists
1888:    */
1889:   public Style getStyle(String nm)
1890:   {
1891:     StyleContext context = (StyleContext) getAttributeContext();
1892:     return context.getStyle(nm);
1893:   }
1894: 
1895:   /**
1896:    * Removes a named <code>Style</code> from the style hierarchy.
1897:    * 
1898:    * @param nm
1899:    *          the name of the <code>Style</code> to be removed
1900:    */
1901:   public void removeStyle(String nm)
1902:   {
1903:     StyleContext context = (StyleContext) getAttributeContext();
1904:     context.removeStyle(nm);
1905:   }
1906: 
1907:   /**
1908:    * Sets text attributes for the fragment specified by <code>offset</code>
1909:    * and <code>length</code>.
1910:    * 
1911:    * @param offset
1912:    *          the start offset of the fragment
1913:    * @param length
1914:    *          the length of the fragment
1915:    * @param attributes
1916:    *          the text attributes to set
1917:    * @param replace
1918:    *          if <code>true</code>, the attributes of the current selection
1919:    *          are overridden, otherwise they are merged
1920:    */
1921:   public void setCharacterAttributes(int offset, int length,
1922:                                      AttributeSet attributes, boolean replace)
1923:   {
1924:     // Exit early if length is 0, so no DocumentEvent is created or fired.
1925:     if (length == 0)
1926:       return;
1927:     try
1928:       {
1929:         // Must obtain a write lock for this method. writeLock() and
1930:         // writeUnlock() should always be in try/finally block to make
1931:         // sure that locking happens in a balanced manner.
1932:         writeLock();
1933:         DefaultDocumentEvent ev = new DefaultDocumentEvent(offset,
1934:                                                            length,
1935:                                                            DocumentEvent.EventType.CHANGE);
1936: 
1937:         // Modify the element structure so that the interval begins at an
1938:         // element
1939:         // start and ends at an element end.
1940:         buffer.change(offset, length, ev);
1941: 
1942:         Element root = getDefaultRootElement();
1943:         // Visit all paragraph elements within the specified interval
1944:         int end = offset + length;
1945:         Element curr;
1946:         for (int pos = offset; pos < end;)
1947:           {
1948:             // Get the CharacterElement at offset pos.
1949:             curr = getCharacterElement(pos);
1950:             if (pos == curr.getEndOffset())
1951:               break;
1952: 
1953:             MutableAttributeSet a = (MutableAttributeSet) curr.getAttributes();
1954:             ev.addEdit(new AttributeUndoableEdit(curr, attributes, replace));
1955:             // If replace is true, remove all the old attributes.
1956:             if (replace)
1957:               a.removeAttributes(a);
1958:             // Add all the new attributes.
1959:             a.addAttributes(attributes);
1960:             // Increment pos so we can check the next CharacterElement.
1961:             pos = curr.getEndOffset();
1962:           }
1963:         fireChangedUpdate(ev);
1964:         fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
1965:       }
1966:     finally
1967:       {
1968:         writeUnlock();
1969:       }
1970:   }
1971: 
1972:   /**
1973:    * Sets the logical style for the paragraph at the specified position.
1974:    * 
1975:    * @param position
1976:    *          the position at which the logical style is added
1977:    * @param style
1978:    *          the style to set for the current paragraph
1979:    */
1980:   public void setLogicalStyle(int position, Style style)
1981:   {
1982:     Element el = getParagraphElement(position);
1983:     // getParagraphElement doesn't return null but subclasses might so
1984:     // we check for null here.
1985:     if (el == null)
1986:       return;
1987:     try
1988:       {
1989:         writeLock();
1990:         if (el instanceof AbstractElement)
1991:           {
1992:             AbstractElement ael = (AbstractElement) el;
1993:             ael.setResolveParent(style);
1994:             int start = el.getStartOffset();
1995:             int end = el.getEndOffset();
1996:             DefaultDocumentEvent ev = new DefaultDocumentEvent(start,
1997:                                                                end - start,
1998:                                                                DocumentEvent.EventType.CHANGE);
1999:             fireChangedUpdate(ev);
2000:             fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
2001:           }
2002:         else
2003:           throw new AssertionError(
2004:                                    "paragraph elements are expected to be"
2005:                                        + "instances of AbstractDocument.AbstractElement");
2006:       }
2007:     finally
2008:       {
2009:         writeUnlock();
2010:       }
2011:   }
2012: 
2013:   /**
2014:    * Sets text attributes for the paragraph at the specified fragment.
2015:    * 
2016:    * @param offset
2017:    *          the beginning of the fragment
2018:    * @param length
2019:    *          the length of the fragment
2020:    * @param attributes
2021:    *          the text attributes to set
2022:    * @param replace
2023:    *          if <code>true</code>, the attributes of the current selection
2024:    *          are overridden, otherwise they are merged
2025:    */
2026:   public void setParagraphAttributes(int offset, int length,
2027:                                      AttributeSet attributes, boolean replace)
2028:   {
2029:     try
2030:       {
2031:         // Must obtain a write lock for this method. writeLock() and
2032:         // writeUnlock() should always be in try/finally blocks to make
2033:         // sure that locking occurs in a balanced manner.
2034:         writeLock();
2035: 
2036:         // Create a DocumentEvent to use for changedUpdate().
2037:         DefaultDocumentEvent ev = new DefaultDocumentEvent(offset,
2038:                                                            length,
2039:                                                            DocumentEvent.EventType.CHANGE);
2040: 
2041:         // Have to iterate through all the _paragraph_ elements that are
2042:         // contained or partially contained in the interval
2043:         // (offset, offset + length).
2044:         Element rootElement = getDefaultRootElement();
2045:         int startElement = rootElement.getElementIndex(offset);
2046:         int endElement = rootElement.getElementIndex(offset + length - 1);
2047:         if (endElement < startElement)
2048:           endElement = startElement;
2049: 
2050:         for (int i = startElement; i <= endElement; i++)
2051:           {
2052:             Element par = rootElement.getElement(i);
2053:             MutableAttributeSet a = (MutableAttributeSet) par.getAttributes();
2054:             // Add the change to the DocumentEvent.
2055:             ev.addEdit(new AttributeUndoableEdit(par, attributes, replace));
2056:             // If replace is true remove the old attributes.
2057:             if (replace)
2058:               a.removeAttributes(a);
2059:             // Add the new attributes.
2060:             a.addAttributes(attributes);
2061:           }
2062:         fireChangedUpdate(ev);
2063:         fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
2064:       }
2065:     finally
2066:       {
2067:         writeUnlock();
2068:       }
2069:   }
2070: 
2071:   /**
2072:    * Called in response to content insert actions. This is used to update the
2073:    * element structure.
2074:    * 
2075:    * @param ev
2076:    *          the <code>DocumentEvent</code> describing the change
2077:    * @param attr
2078:    *          the attributes for the change
2079:    */
2080:   protected void insertUpdate(DefaultDocumentEvent ev, AttributeSet attr)
2081:   {
2082:     super.insertUpdate(ev, attr);
2083:     // If the attribute set is null, use an empty attribute set.
2084:     if (attr == null)
2085:       attr = SimpleAttributeSet.EMPTY;
2086:     int offset = ev.getOffset();
2087:     int length = ev.getLength();
2088:     int endOffset = offset + length;
2089:     AttributeSet paragraphAttributes = getParagraphElement(endOffset).getAttributes();
2090:     Segment txt = new Segment();
2091:     try
2092:       {
2093:         getText(offset, length, txt);
2094:       }
2095:     catch (BadLocationException ex)
2096:       {
2097:         AssertionError ae = new AssertionError("Unexpected bad location");
2098:         ae.initCause(ex);
2099:         throw ae;
2100:       }
2101: 
2102:     int len = 0;
2103:     Vector specs = new Vector();
2104:     ElementSpec finalStartTag = null;
2105:     short finalStartDirection = ElementSpec.OriginateDirection;
2106:     boolean prevCharWasNewline = false;
2107:     Element prev = getCharacterElement(offset);
2108:     Element next = getCharacterElement(endOffset);
2109:     Element prevParagraph = getParagraphElement(offset);
2110:     Element paragraph = getParagraphElement(endOffset);
2111: 
2112:     int segmentEnd = txt.offset + txt.count;
2113: 
2114:     // Check to see if we're inserting immediately after a newline.
2115:     if (offset > 0)
2116:       {
2117:         try
2118:           {
2119:             String s = getText(offset - 1, 1);
2120:             if (s.equals("\n"))
2121:               {
2122:                 finalStartDirection = handleInsertAfterNewline(specs, offset,
2123:                                                                endOffset,
2124:                                                                prevParagraph,
2125:                                                                paragraph,
2126:                                                                paragraphAttributes);
2127: 
2128:                 prevCharWasNewline = true;
2129:                 // Find the final start tag from the ones just created.
2130:                 for (int i = 0; i < specs.size(); i++)
2131:                   if (((ElementSpec) specs.get(i)).getType() == ElementSpec.StartTagType)
2132:                     finalStartTag = (ElementSpec) specs.get(i);
2133:               }
2134:           }
2135:         catch (BadLocationException ble)
2136:           {
2137:             // This shouldn't happen.
2138:             AssertionError ae = new AssertionError();
2139:             ae.initCause(ble);
2140:             throw ae;
2141:           }
2142:       }
2143: 
2144:     for (int i = txt.offset; i < segmentEnd; ++i)
2145:       {
2146:         len++;
2147:         if (txt.array[i] == '\n')
2148:           {
2149:             // Add the ElementSpec for the content.
2150:             specs.add(new ElementSpec(attr, ElementSpec.ContentType, len));
2151: 
2152:             // Add ElementSpecs for the newline.
2153:             specs.add(new ElementSpec(null, ElementSpec.EndTagType));
2154:             finalStartTag = new ElementSpec(paragraphAttributes,
2155:                                             ElementSpec.StartTagType);
2156:             specs.add(finalStartTag);
2157:             len = 0;
2158:           }
2159:       }
2160: 
2161:     // Create last element if last character hasn't been a newline.
2162:     if (len > 0)
2163:       specs.add(new ElementSpec(attr, ElementSpec.ContentType, len));
2164: 
2165:     // Set the direction of the last spec of type StartTagType.
2166:     // If we are inserting after a newline then this value comes from
2167:     // handleInsertAfterNewline.
2168:     if (finalStartTag != null)
2169:       {
2170:         if (prevCharWasNewline)
2171:           finalStartTag.setDirection(finalStartDirection);
2172:         else if (prevParagraph.getEndOffset() != endOffset)
2173:           finalStartTag.setDirection(ElementSpec.JoinFractureDirection);
2174:         else
2175:           {
2176:             // If there is an element AFTER this one, then set the
2177:             // direction to JoinNextDirection.
2178:             Element parent = prevParagraph.getParentElement();
2179:             int index = parent.getElementIndex(offset);
2180:             if (index + 1 < parent.getElementCount()
2181:                 && !parent.getElement(index + 1).isLeaf())
2182:               finalStartTag.setDirection(ElementSpec.JoinNextDirection);
2183:           }
2184:       }
2185: 
2186:     // If we are at the last index, then check if we could probably be
2187:     // joined with the next element.
2188:     // This means:
2189:     // - we must be a ContentTag
2190:     // - if there is a next Element, we must have the same attributes
2191:     // - if there is no next Element, but one will be created,
2192:     // we must have the same attributes as the higher-level run.
2193:     ElementSpec last = (ElementSpec) specs.lastElement();
2194:     if (last.getType() == ElementSpec.ContentType)
2195:       {
2196:         Element currentRun = prevParagraph.getElement(prevParagraph.getElementIndex(offset));
2197:         if (currentRun.getEndOffset() == endOffset)
2198:           {
2199:             if (endOffset < getLength() && next.getAttributes().isEqual(attr)
2200:                 && last.getType() == ElementSpec.ContentType)
2201:               last.setDirection(ElementSpec.JoinNextDirection);
2202:           }
2203:         else
2204:           {
2205:             if (finalStartTag != null
2206:                 && finalStartTag.getDirection() == ElementSpec.JoinFractureDirection
2207:                 && currentRun.getAttributes().isEqual(attr))
2208:               {
2209:                 last.setDirection(ElementSpec.JoinNextDirection);
2210:               }
2211:           }
2212:       }
2213: 
2214:     // If we are at the first new element, then check if it could be
2215:     // joined with the previous element.
2216:     ElementSpec first = (ElementSpec) specs.firstElement();
2217:     if (prev.getAttributes().isEqual(attr)
2218:         && first.getType() == ElementSpec.ContentType)
2219:       first.setDirection(ElementSpec.JoinPreviousDirection);
2220: 
2221:     ElementSpec[] elSpecs = (ElementSpec[]) specs.toArray(new ElementSpec[specs.size()]);
2222:     buffer.insert(offset, length, elSpecs, ev);
2223:   }
2224: 
2225:   /**
2226:    * A helper method to set up the ElementSpec buffer for the special case of an
2227:    * insertion occurring immediately after a newline.
2228:    * 
2229:    * @param specs
2230:    *          the ElementSpec buffer to initialize.
2231:    */
2232:   short handleInsertAfterNewline(Vector specs, int offset, int endOffset,
2233:                                  Element prevParagraph, Element paragraph,
2234:                                  AttributeSet a)
2235:   {
2236:     if (prevParagraph.getParentElement() == paragraph.getParentElement())
2237:       {
2238:         specs.add(new ElementSpec(a, ElementSpec.EndTagType));
2239:         specs.add(new ElementSpec(a, ElementSpec.StartTagType));
2240:         if (paragraph.getStartOffset() != endOffset)
2241:           return ElementSpec.JoinFractureDirection;
2242:         // If there is an Element after this one, use JoinNextDirection.
2243:         Element parent = paragraph.getParentElement();
2244:         if (parent.getElementCount() > (parent.getElementIndex(offset) + 1))
2245:           return ElementSpec.JoinNextDirection;
2246:       }
2247:     return ElementSpec.OriginateDirection;
2248:   }
2249: 
2250:   /**
2251:    * Updates the document structure in response to text removal. This is
2252:    * forwarded to the {@link ElementBuffer} of this document. Any changes to the
2253:    * document structure are added to the specified document event and sent to
2254:    * registered listeners.
2255:    * 
2256:    * @param ev
2257:    *          the document event that records the changes to the document
2258:    */
2259:   protected void removeUpdate(DefaultDocumentEvent ev)
2260:   {
2261:     super.removeUpdate(ev);
2262:     buffer.remove(ev.getOffset(), ev.getLength(), ev);
2263:   }
2264: 
2265:   /**
2266:    * Returns an enumeration of all style names.
2267:    * 
2268:    * @return an enumeration of all style names
2269:    */
2270:   public Enumeration getStyleNames()
2271:   {
2272:     StyleContext context = (StyleContext) getAttributeContext();
2273:     return context.getStyleNames();
2274:   }
2275: 
2276:   /**
2277:    * Called when any of this document's styles changes.
2278:    * 
2279:    * @param style
2280:    *          the style that changed
2281:    */
2282:   protected void styleChanged(Style style)
2283:   {
2284:     // Nothing to do here. This is intended to be overridden by subclasses.
2285:   }
2286: 
2287:   /**
2288:    * Inserts a bulk of structured content at once.
2289:    * 
2290:    * @param offset
2291:    *          the offset at which the content should be inserted
2292:    * @param data
2293:    *          the actual content spec to be inserted
2294:    */
2295:   protected void insert(int offset, ElementSpec[] data)
2296:       throws BadLocationException
2297:   {
2298:     if (data == null || data.length == 0)
2299:       return;
2300:     try
2301:       {
2302:         // writeLock() and writeUnlock() should always be in a try/finally
2303:         // block so that locking balance is guaranteed even if some
2304:         // exception is thrown.
2305:         writeLock();
2306: 
2307:         // First we collect the content to be inserted.
2308:         StringBuffer contentBuffer = new StringBuffer();
2309:         for (int i = 0; i < data.length; i++)
2310:           {
2311:             // Collect all inserts into one so we can get the correct
2312:             // ElementEdit
2313:             ElementSpec spec = data[i];
2314:             if (spec.getArray() != null && spec.getLength() > 0)
2315:               contentBuffer.append(spec.getArray(), spec.getOffset(),
2316:                                    spec.getLength());
2317:           }
2318: 
2319:         int length = contentBuffer.length();
2320: 
2321:         // If there was no content inserted then exit early.
2322:         if (length == 0)
2323:           return;
2324: 
2325:         UndoableEdit edit = content.insertString(offset,
2326:                                                  contentBuffer.toString());
2327: 
2328:         // Create the DocumentEvent with the ElementEdit added
2329:         DefaultDocumentEvent ev = new DefaultDocumentEvent(offset,
2330:                                                            length,
2331:                                                            DocumentEvent.EventType.INSERT);
2332:         ev.addEdit(edit);
2333: 
2334:         // Finally we must update the document structure and fire the insert
2335:         // update event.
2336:         buffer.insert(offset, length, data, ev);
2337:         fireInsertUpdate(ev);
2338:         fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
2339:       }
2340:     finally
2341:       {
2342:         writeUnlock();
2343:       }
2344:   }
2345: 
2346:   /**
2347:    * Initializes the <code>DefaultStyledDocument</code> with the specified
2348:    * data.
2349:    * 
2350:    * @param data
2351:    *          the specification of the content with which the document is
2352:    *          initialized
2353:    */
2354:   protected void create(ElementSpec[] data)
2355:   {
2356:     writeLock();
2357:     try
2358:       {
2359:         // Clear content if there is some.
2360:         int len = getLength();
2361:         if (len > 0)
2362:           remove(0, len);
2363: 
2364:         // Now we insert the content.
2365:         StringBuilder b = new StringBuilder();
2366:         for (int i = 0; i < data.length; ++i)
2367:           {
2368:             ElementSpec el = data[i];
2369:             if (el.getArray() != null && el.getLength() > 0)
2370:               b.append(el.getArray(), el.getOffset(), el.getLength());
2371:           }
2372:         Content content = getContent();
2373:         UndoableEdit cEdit = content.insertString(0, b.toString());
2374: 
2375:         DefaultDocumentEvent ev =
2376:           new DefaultDocumentEvent(0, b.length(),
2377:                                    DocumentEvent.EventType.INSERT);
2378:         ev.addEdit(cEdit);
2379: 
2380:         // We do a little trick here to get the new structure: We instantiate
2381:         // a new ElementBuffer with a new root element, insert into that root
2382:         // and then reparent the newly created elements to the old root
2383:         // element.
2384:         BranchElement createRoot =
2385:           (BranchElement) createBranchElement(null, null);
2386:         Element dummyLeaf = createLeafElement(createRoot, null, 0, 1);
2387:         createRoot.replace(0, 0, new Element[]{ dummyLeaf });
2388:         ElementBuffer createBuffer = new ElementBuffer(createRoot);
2389:         createBuffer.insert(0, b.length(), data, new DefaultDocumentEvent(0, b.length(), DocumentEvent.EventType.INSERT));
2390:         // Now the new root is the first child of the createRoot.
2391:         Element newRoot = createRoot.getElement(0);
2392:         BranchElement root = (BranchElement) getDefaultRootElement();
2393:         Element[] added = new Element[newRoot.getElementCount()];
2394:         for (int i = 0; i < added.length; ++i)
2395:           {
2396:             added[i] = newRoot.getElement(i);
2397:             ((AbstractElement) added[i]).element_parent = root;
2398:           }
2399:         Element[] removed = new Element[root.getElementCount()];
2400:         for (int i = 0; i < removed.length; ++i)
2401:           removed[i] = root.getElement(i);
2402: 
2403:         // Replace the old elements in root with the new and update the event.
2404:         root.replace(0, removed.length, added);
2405:         ev.addEdit(new ElementEdit(root, 0, removed, added));
2406: 
2407:         fireInsertUpdate(ev);
2408:         fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
2409:       }
2410:     catch (BadLocationException ex)
2411:       {
2412:         AssertionError err = new AssertionError("Unexpected bad location");
2413:         err.initCause(ex);
2414:         throw err;
2415:       }
2416:     finally
2417:       {
2418:         writeUnlock();
2419:       }
2420:   }
2421: }