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