Source for javax.swing.text.DefaultStyledDocument

   1: /* DefaultStyledDocument.java --
   2:    Copyright (C) 2004, 2005 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package javax.swing.text;
  40: 
  41: import java.awt.Color;
  42: import java.awt.Font;
  43: import java.io.Serializable;
  44: import java.util.ArrayList;
  45: import java.util.Enumeration;
  46: import java.util.Iterator;
  47: import java.util.Stack;
  48: import java.util.Vector;
  49: 
  50: import javax.swing.event.ChangeEvent;
  51: import javax.swing.event.ChangeListener;
  52: import javax.swing.event.DocumentEvent;
  53: import javax.swing.event.UndoableEditEvent;
  54: import javax.swing.undo.AbstractUndoableEdit;
  55: import javax.swing.undo.UndoableEdit;
  56: 
  57: /**
  58:  * The default implementation of {@link StyledDocument}. The document is
  59:  * modeled as an {@link Element} tree, which has a {@link SectionElement} as
  60:  * single root, which has one or more {@link AbstractDocument.BranchElement}s
  61:  * as paragraph nodes and each paragraph node having one or more
  62:  * {@link AbstractDocument.LeafElement}s as content nodes.
  63:  * 
  64:  * @author Michael Koch (konqueror@gmx.de)
  65:  * @author Roman Kennke (roman@kennke.org)
  66:  */
  67: public class DefaultStyledDocument extends AbstractDocument implements
  68:     StyledDocument
  69: {
  70: 
  71:   /**
  72:    * An {@link UndoableEdit} that can undo attribute changes to an element.
  73:    * 
  74:    * @author Roman Kennke (kennke@aicas.com)
  75:    */
  76:   public static class AttributeUndoableEdit extends AbstractUndoableEdit
  77:   {
  78:     /**
  79:      * A copy of the old attributes.
  80:      */
  81:     protected AttributeSet copy;
  82: 
  83:     /**
  84:      * The new attributes.
  85:      */
  86:     protected AttributeSet newAttributes;
  87: 
  88:     /**
  89:      * If the new attributes replaced the old attributes or if they only were
  90:      * added to them.
  91:      */
  92:     protected boolean isReplacing;
  93: 
  94:     /**
  95:      * The element that has changed.
  96:      */
  97:     protected Element element;
  98: 
  99:     /**
 100:      * Creates a new <code>AttributeUndoableEdit</code>.
 101:      * 
 102:      * @param el
 103:      *          the element that changes attributes
 104:      * @param newAtts
 105:      *          the new attributes
 106:      * @param replacing
 107:      *          if the new attributes replace the old or only append to them
 108:      */
 109:     public AttributeUndoableEdit(Element el, AttributeSet newAtts,
 110:                                  boolean replacing)
 111:     {
 112:       element = el;
 113:       newAttributes = newAtts;
 114:       isReplacing = replacing;
 115:       copy = el.getAttributes().copyAttributes();
 116:     }
 117: 
 118:     /**
 119:      * Undos the attribute change. The <code>copy</code> field is set as
 120:      * attributes on <code>element</code>.
 121:      */
 122:     public void undo()
 123:     {
 124:       super.undo();
 125:       AttributeSet atts = element.getAttributes();
 126:       if (atts instanceof MutableAttributeSet)
 127:         {
 128:           MutableAttributeSet mutable = (MutableAttributeSet) atts;
 129:           mutable.removeAttributes(atts);
 130:           mutable.addAttributes(copy);
 131:         }
 132:     }
 133: 
 134:     /**
 135:      * Redos an attribute change. This adds <code>newAttributes</code> to the
 136:      * <code>element</code>'s attribute set, possibly clearing all attributes
 137:      * if <code>isReplacing</code> is true.
 138:      */
 139:     public void redo()
 140:     {
 141:       super.undo();
 142:       AttributeSet atts = element.getAttributes();
 143:       if (atts instanceof MutableAttributeSet)
 144:         {
 145:           MutableAttributeSet mutable = (MutableAttributeSet) atts;
 146:           if (isReplacing)
 147:             mutable.removeAttributes(atts);
 148:           mutable.addAttributes(newAttributes);
 149:         }
 150:     }
 151:   }
 152: 
 153:   /**
 154:    * Carries specification information for new {@link Element}s that should be
 155:    * created in {@link ElementBuffer}. This allows the parsing process to be
 156:    * decoupled from the <code>Element</code> creation process.
 157:    */
 158:   public static class ElementSpec
 159:   {
 160:     /**
 161:      * This indicates a start tag. This is a possible value for {@link #getType}.
 162:      */
 163:     public static final short StartTagType = 1;
 164: 
 165:     /**
 166:      * This indicates an end tag. This is a possible value for {@link #getType}.
 167:      */
 168:     public static final short EndTagType = 2;
 169: 
 170:     /**
 171:      * This indicates a content element. This is a possible value for
 172:      * {@link #getType}.
 173:      */
 174:     public static final short ContentType = 3;
 175: 
 176:     /**
 177:      * This indicates that the data associated with this spec should be joined
 178:      * with what precedes it. This is a possible value for {@link #getDirection}.
 179:      */
 180:     public static final short JoinPreviousDirection = 4;
 181: 
 182:     /**
 183:      * This indicates that the data associated with this spec should be joined
 184:      * with what follows it. This is a possible value for {@link #getDirection}.
 185:      */
 186:     public static final short JoinNextDirection = 5;
 187: 
 188:     /**
 189:      * This indicates that the data associated with this spec should be used to
 190:      * create a new element. This is a possible value for {@link #getDirection}.
 191:      */
 192:     public static final short OriginateDirection = 6;
 193: 
 194:     /**
 195:      * This indicates that the data associated with this spec should be joined
 196:      * to the fractured element. This is a possible value for
 197:      * {@link #getDirection}.
 198:      */
 199:     public static final short JoinFractureDirection = 7;
 200: 
 201:     /**
 202:      * The type of the tag.
 203:      */
 204:     short type;
 205: 
 206:     /**
 207:      * The direction of the tag.
 208:      */
 209:     short direction;
 210: 
 211:     /**
 212:      * The offset of the content.
 213:      */
 214:     int offset;
 215: 
 216:     /**
 217:      * The length of the content.
 218:      */
 219:     int length;
 220: 
 221:     /**
 222:      * The actual content.
 223:      */
 224:     char[] content;
 225: 
 226:     /**
 227:      * The attributes for the tag.
 228:      */
 229:     AttributeSet attributes;
 230: 
 231:     /**
 232:      * Creates a new <code>ElementSpec</code> with no content, length or
 233:      * offset. This is most useful for start and end tags.
 234:      * 
 235:      * @param a
 236:      *          the attributes for the element to be created
 237:      * @param type
 238:      *          the type of the tag
 239:      */
 240:     public ElementSpec(AttributeSet a, short type)
 241:     {
 242:       this(a, type, 0);
 243:     }
 244: 
 245:     /**
 246:      * Creates a new <code>ElementSpec</code> that specifies the length but
 247:      * not the offset of an element. Such <code>ElementSpec</code>s are
 248:      * processed sequentially from a known starting point.
 249:      * 
 250:      * @param a
 251:      *          the attributes for the element to be created
 252:      * @param type
 253:      *          the type of the tag
 254:      * @param len
 255:      *          the length of the element
 256:      */
 257:     public ElementSpec(AttributeSet a, short type, int len)
 258:     {
 259:       this(a, type, null, 0, len);
 260:     }
 261: 
 262:     /**
 263:      * Creates a new <code>ElementSpec</code> with document content.
 264:      * 
 265:      * @param a
 266:      *          the attributes for the element to be created
 267:      * @param type
 268:      *          the type of the tag
 269:      * @param txt
 270:      *          the actual content
 271:      * @param offs
 272:      *          the offset into the <code>txt</code> array
 273:      * @param len
 274:      *          the length of the element
 275:      */
 276:     public ElementSpec(AttributeSet a, short type, char[] txt, int offs, int len)
 277:     {
 278:       attributes = a;
 279:       this.type = type;
 280:       offset = offs;
 281:       length = len;
 282:       content = txt;
 283:       direction = OriginateDirection;
 284:     }
 285: 
 286:     /**
 287:      * Sets the type of the element.
 288:      * 
 289:      * @param type
 290:      *          the type of the element to be set
 291:      */
 292:     public void setType(short type)
 293:     {
 294:       this.type = type;
 295:     }
 296: 
 297:     /**
 298:      * Returns the type of the element.
 299:      * 
 300:      * @return the type of the element
 301:      */
 302:     public short getType()
 303:     {
 304:       return type;
 305:     }
 306: 
 307:     /**
 308:      * Sets the direction of the element.
 309:      * 
 310:      * @param dir
 311:      *          the direction of the element to be set
 312:      */
 313:     public void setDirection(short dir)
 314:     {
 315:       direction = dir;
 316:     }
 317: 
 318:     /**
 319:      * Returns the direction of the element.
 320:      * 
 321:      * @return the direction of the element
 322:      */
 323:     public short getDirection()
 324:     {
 325:       return direction;
 326:     }
 327: 
 328:     /**
 329:      * Returns the attributes of the element.
 330:      * 
 331:      * @return the attributes of the element
 332:      */
 333:     public AttributeSet getAttributes()
 334:     {
 335:       return attributes;
 336:     }
 337: 
 338:     /**
 339:      * Returns the actual content of the element.
 340:      * 
 341:      * @return the actual content of the element
 342:      */
 343:     public char[] getArray()
 344:     {
 345:       return content;
 346:     }
 347: 
 348:     /**
 349:      * Returns the offset of the content.
 350:      * 
 351:      * @return the offset of the content
 352:      */
 353:     public int getOffset()
 354:     {
 355:       return offset;
 356:     }
 357: 
 358:     /**
 359:      * Returns the length of the content.
 360:      * 
 361:      * @return the length of the content
 362:      */
 363:     public int getLength()
 364:     {
 365:       return length;
 366:     }
 367: 
 368:     /**
 369:      * Returns a String representation of this <code>ElementSpec</code>
 370:      * describing the type, direction and length of this
 371:      * <code>ElementSpec</code>.
 372:      * 
 373:      * @return a String representation of this <code>ElementSpec</code>
 374:      */
 375:     public String toString()
 376:     {
 377:       StringBuilder b = new StringBuilder();
 378:       switch (type)
 379:         {
 380:         case StartTagType:
 381:           b.append("StartTag");
 382:           break;
 383:         case EndTagType:
 384:           b.append("EndTag");
 385:           break;
 386:         case ContentType:
 387:           b.append("Content");
 388:           break;
 389:         default:
 390:           b.append("??");
 391:           break;
 392:         }
 393: 
 394:       b.append(':');
 395: 
 396:       switch (direction)
 397:         {
 398:         case JoinPreviousDirection:
 399:           b.append("JoinPrevious");
 400:           break;
 401:         case JoinNextDirection:
 402:           b.append("JoinNext");
 403:           break;
 404:         case OriginateDirection:
 405:           b.append("Originate");
 406:           break;
 407:         case JoinFractureDirection:
 408:           b.append("Fracture");
 409:           break;
 410:         default:
 411:           b.append("??");
 412:           break;
 413:         }
 414: 
 415:       b.append(':');
 416:       b.append(length);
 417: 
 418:       return b.toString();
 419:     }
 420:   }
 421: 
 422:   /**
 423:    * Performs all <em>structural</code> changes to the <code>Element</code>
 424:    * hierarchy.  This class was implemented with much help from the document:
 425:    * http://java.sun.com/products/jfc/tsc/articles/text/element_buffer/index.html.
 426:    */
 427:   public class ElementBuffer implements Serializable
 428:   {
 429:     /**
 430:      * Instance of all editing information for an object in the Vector. This class
 431:      * is used to add information to the DocumentEvent associated with an
 432:      * insertion/removal/change as well as to store the changes that need to be
 433:      * made so they can be made all at the same (appropriate) time.
 434:      */
 435:     class Edit
 436:     {
 437:       /** The element to edit . */
 438:       Element e;
 439: 
 440:       /** The index of the change. */
 441:       int index;
 442: 
 443:       /** The removed elements. */
 444:       ArrayList removed = new ArrayList();
 445: 
 446:       /** The added elements. */
 447:       ArrayList added = new ArrayList();
 448: 
 449:       /**
 450:        * Indicates if this edit contains a fracture.
 451:        */
 452:       boolean isFracture;
 453: 
 454:       /**
 455:        * Creates a new Edit for the specified element at index i.
 456:        *
 457:        * @param el the element
 458:        * @param i the index
 459:        */
 460:       Edit(Element el, int i)
 461:       {
 462:         this(el, i, false);
 463:       }
 464: 
 465:       /**
 466:        * Creates a new Edit for the specified element at index i.
 467:        *
 468:        * @param el the element
 469:        * @param i the index
 470:        * @param frac if this is a fracture edit or not
 471:        */
 472:       Edit(Element el, int i, boolean frac)
 473:       {
 474:         e = el;
 475:         index = i;
 476:         isFracture = frac;
 477:       }
 478: 
 479:     }
 480: 
 481:     /** The serialization UID (compatible with JDK1.5). */
 482:     private static final long serialVersionUID = 1688745877691146623L;
 483: 
 484:     /** The root element of the hierarchy. */
 485:     private Element root;
 486: 
 487:     /** Holds the offset for structural changes. */
 488:     private int offset;
 489: 
 490:     /** Holds the end offset for structural changes. */
 491:     private int endOffset;
 492: 
 493:     /** Holds the length of structural changes. */
 494:     private int length;
 495: 
 496:     /** Holds the position of the change. */
 497:     private int pos;
 498: 
 499:     /**
 500:      * The parent of the fracture.
 501:      */
 502:     private Element fracturedParent;
 503: 
 504:     /**
 505:      * The fractured child.
 506:      */
 507:     private Element fracturedChild;
 508: 
 509:     /**
 510:      * Indicates if a fracture has been created.
 511:      */
 512:     private boolean createdFracture;
 513: 
 514:     /**
 515:      * The current position in the element tree. This is used for bulk inserts
 516:      * using ElementSpecs.
 517:      */
 518:     private Stack elementStack;
 519: 
 520:     private Edit[] insertPath;
 521: 
 522:     private boolean recreateLeafs;
 523: 
 524:     /**
 525:      * Vector that contains all the edits. Maybe replace by a HashMap.
 526:      */
 527:     private ArrayList edits;
 528: 
 529:     private boolean offsetLastIndex;
 530:     private boolean offsetLastIndexReplace;
 531: 
 532:     /**
 533:      * Creates a new <code>ElementBuffer</code> for the specified
 534:      * <code>root</code> element.
 535:      * 
 536:      * @param root
 537:      *          the root element for this <code>ElementBuffer</code>
 538:      */
 539:     public ElementBuffer(Element root)
 540:     {
 541:       this.root = root;
 542:     }
 543: 
 544:     /**
 545:      * Returns the root element of this <code>ElementBuffer</code>.
 546:      * 
 547:      * @return the root element of this <code>ElementBuffer</code>
 548:      */
 549:     public Element getRootElement()
 550:     {
 551:       return root;
 552:     }
 553: 
 554:     /**
 555:      * Removes the content. This method sets some internal parameters and
 556:      * delegates the work to {@link #removeUpdate}.
 557:      * 
 558:      * @param offs
 559:      *          the offset from which content is remove
 560:      * @param len
 561:      *          the length of the removed content
 562:      * @param ev
 563:      *          the document event that records the changes
 564:      */
 565:     public void remove(int offs, int len, DefaultDocumentEvent ev)
 566:     {
 567:       prepareEdit(offs, len);
 568:       removeUpdate();
 569:       finishEdit(ev);
 570:     }
 571: 
 572:     /**
 573:      * Updates the element structure of the document in response to removal of
 574:      * content. It removes the affected {@link Element}s from the document
 575:      * structure.
 576:      */
 577:     protected void removeUpdate()
 578:     {
 579:       removeElements(root, offset, endOffset);
 580:     }
 581: 
 582:     private boolean removeElements(Element elem, int rmOffs0, int rmOffs1)
 583:     {
 584:       boolean ret = false; 
 585:       if (! elem.isLeaf())
 586:         {
 587:           // Update stack for changes.
 588:           int index0 = elem.getElementIndex(rmOffs0);
 589:           int index1 = elem.getElementIndex(rmOffs1);
 590:           elementStack.push(new Edit(elem, index0));
 591:           Edit ec = (Edit) elementStack.peek();
 592: 
 593:           // If the range is contained by one element,
 594:           // we just forward the request
 595:           if (index0 == index1)
 596:             {
 597:               Element child0 = elem.getElement(index0);
 598:               if(rmOffs0 <= child0.getStartOffset()
 599:                   && rmOffs1 >= child0.getEndOffset())
 600:                 {
 601:                   // Element totally removed.
 602:                   ec.removed.add(child0);
 603:                 }
 604:               else if (removeElements(child0, rmOffs0, rmOffs1))
 605:                 {
 606:                   ec.removed.add(child0);
 607:                 }
 608:             }
 609:           else
 610:             {
 611:               // The removal range spans elements.  If we can join
 612:               // the two endpoints, do it.  Otherwise we remove the
 613:               // interior and forward to the endpoints.
 614:               Element child0 = elem.getElement(index0);
 615:               Element child1 = elem.getElement(index1);
 616:               boolean containsOffs1 = (rmOffs1 < elem.getEndOffset());
 617:           if (containsOffs1 && canJoin(child0, child1))
 618:             {
 619:               // Remove and join.
 620:               for (int i = index0; i <= index1; i++)
 621:                 {
 622:                   ec.removed.add(elem.getElement(i));
 623:                 }
 624:               Element e = join(elem, child0, child1, rmOffs0, rmOffs1);
 625:               ec.added.add(e);
 626:             }
 627:           else
 628:             {
 629:               // Remove interior and forward.
 630:               int rmIndex0 = index0 + 1;
 631:               int rmIndex1 = index1 - 1;
 632:               if (child0.getStartOffset() == rmOffs0
 633:                   || (index0 == 0 && child0.getStartOffset() > rmOffs0
 634:                       && child0.getEndOffset() <= rmOffs1))
 635:                 {
 636:                   // Start element completely consumed.
 637:                   child0 = null;
 638:                   rmIndex0 = index0;
 639:                 }
 640:               if (! containsOffs1)
 641:                 {
 642:                   child1 = null;
 643:                   rmIndex1++;
 644:               }
 645:               else if (child1.getStartOffset() == rmOffs1)
 646:                 {
 647:                   // End element not touched.
 648:                   child1 = null;
 649:                 }
 650:               if (rmIndex0 <= rmIndex1)
 651:                 {
 652:                   ec.index = rmIndex0;
 653:                 }
 654:               for (int i = rmIndex0; i <= rmIndex1; i++)
 655:                 {
 656:                   ec.removed.add(elem.getElement(i));
 657:                 }
 658:               if (child0 != null)
 659:                 {
 660:                   if(removeElements(child0, rmOffs0, rmOffs1))
 661:                     {
 662:                       ec.removed.add(0, child0);
 663:                       ec.index = index0;
 664:                     }
 665:                 }
 666:               if (child1 != null)
 667:                 {
 668:                   if(removeElements(child1, rmOffs0, rmOffs1))
 669:                     {
 670:                       ec.removed.add(child1);
 671:                     }
 672:                 }
 673:             }
 674:             }
 675: 
 676:           // Perform changes.
 677:           pop();
 678: 
 679:           // Return true if we no longer have any children.
 680:           if(elem.getElementCount() == (ec.removed.size() - ec.added.size()))
 681:             ret = true;
 682:         }
 683:       return ret;
 684:     }
 685: 
 686:     /**
 687:      * Creates a document in response to a call to
 688:      * {@link DefaultStyledDocument#create(ElementSpec[])}.
 689:      *
 690:      * @param len the length of the inserted text
 691:      * @param data the specs for the elements
 692:      * @param ev the document event
 693:      */
 694:     void create(int len, ElementSpec[] data, DefaultDocumentEvent ev)
 695:     {
 696:       prepareEdit(offset, len);
 697:       Element el = root;
 698:       int index = el.getElementIndex(0);
 699:       while (! el.isLeaf())
 700:         {
 701:           Element child = el.getElement(index);
 702:           Edit edit = new Edit(el, index, false);
 703:           elementStack.push(edit);
 704:           el = child;
 705:           index = el.getElementIndex(0);
 706:         }
 707:       Edit ed = (Edit) elementStack.peek();
 708:       Element child = ed.e.getElement(ed.index);
 709:       ed.added.add(createLeafElement(ed.e, child.getAttributes(), getLength(),
 710:                                      child.getEndOffset()));
 711:       ed.removed.add(child);
 712:       while (elementStack.size() > 1)
 713:         pop();
 714:       int n = data.length;
 715: 
 716:       // Reset root element's attributes.
 717:       AttributeSet newAtts = null;
 718:       if (n > 0 && data[0].getType() == ElementSpec.StartTagType)
 719:         newAtts = data[0].getAttributes();
 720:       if (newAtts == null)
 721:         newAtts = SimpleAttributeSet.EMPTY;
 722:       MutableAttributeSet mAtts = (MutableAttributeSet) root.getAttributes();
 723:       ev.addEdit(new AttributeUndoableEdit(root, newAtts, true));
 724:       mAtts.removeAttributes(mAtts);
 725:       mAtts.addAttributes(newAtts);
 726: 
 727:       // Insert the specified elements.
 728:       for (int i = 1; i < n; i++)
 729:         insertElement(data[i]);
 730: 
 731:       // Pop remaining stack.
 732:       while (elementStack.size() > 0)
 733:         pop();
 734: 
 735:       finishEdit(ev);
 736:     }
 737: 
 738:     private boolean canJoin(Element e0, Element e1)
 739:     {
 740:       boolean ret = false;
 741:       if ((e0 != null) && (e1 != null))
 742:         {
 743:           // Don't join a leaf to a branch.
 744:           boolean isLeaf0 = e0.isLeaf();
 745:           boolean isLeaf1 = e1.isLeaf();
 746:           if(isLeaf0 == isLeaf1)
 747:             {
 748:               if (isLeaf0)
 749:                 {
 750:                   // Only join leaves if the attributes match, otherwise
 751:                   // style information will be lost.
 752:                   ret = e0.getAttributes().isEqual(e1.getAttributes());
 753:                 }
 754:               else
 755:                 {
 756:                   // Only join non-leafs if the names are equal. This may result
 757:                   // in loss of style information, but this is typically
 758:                   // acceptable for non-leafs.
 759:                   String name0 = e0.getName();
 760:                   String name1 = e1.getName();
 761:                   if (name0 != null)
 762:                     ret = name0.equals(name1);
 763:                   else if (name1 != null)
 764:                     ret = name1.equals(name0);
 765:                   else // Both names null.
 766:                     ret = true;
 767:                 }
 768:             }
 769:         }
 770:       return ret;
 771:     }
 772: 
 773:     private Element join(Element p, Element left, Element right, int rmOffs0,
 774:                          int rmOffs1)
 775:     {
 776:       Element joined = null;
 777:       if (left.isLeaf() && right.isLeaf())
 778:         {
 779:           joined = createLeafElement(p, left.getAttributes(),
 780:                                      left.getStartOffset(),
 781:                                      right.getEndOffset());
 782:         }
 783:       else if ((! left.isLeaf()) && (! right.isLeaf()))
 784:         {
 785:           // Join two branch elements.  This copies the children before
 786:           // the removal range on the left element, and after the removal
 787:           // range on the right element.  The two elements on the edge
 788:           // are joined if possible and needed.
 789:           joined = createBranchElement(p, left.getAttributes());
 790:           int ljIndex = left.getElementIndex(rmOffs0);
 791:           int rjIndex = right.getElementIndex(rmOffs1);
 792:           Element lj = left.getElement(ljIndex);
 793:           if (lj.getStartOffset() >= rmOffs0)
 794:             {
 795:               lj = null;
 796:             }
 797:           Element rj = right.getElement(rjIndex);
 798:           if (rj.getStartOffset() == rmOffs1)
 799:             {
 800:               rj = null;
 801:             }
 802:           ArrayList children = new ArrayList();
 803:           // Transfer the left.
 804:           for (int i = 0; i < ljIndex; i++)
 805:             {
 806:               children.add(clone(joined, left.getElement(i)));
 807:             }
 808: 
 809:           // Transfer the join/middle.
 810:           if (canJoin(lj, rj))
 811:             {
 812:               Element e = join(joined, lj, rj, rmOffs0, rmOffs1);
 813:               children.add(e);
 814:             }
 815:           else
 816:             {
 817:               if (lj != null)
 818:                 {
 819:                   children.add(cloneAsNecessary(joined, lj, rmOffs0, rmOffs1));
 820:                 }
 821:               if (rj != null)
 822:                 {
 823:                   children.add(cloneAsNecessary(joined, rj, rmOffs0, rmOffs1));
 824:                 }
 825:             }
 826: 
 827:           // Transfer the right.
 828:           int n = right.getElementCount();
 829:           for (int i = (rj == null) ? rjIndex : rjIndex + 1; i < n; i++)
 830:             {
 831:               children.add(clone(joined, right.getElement(i)));
 832:             }
 833: 
 834:           // Install the children.
 835:           Element[] c = new Element[children.size()];
 836:           c = (Element[]) children.toArray(c);
 837:           ((BranchElement) joined).replace(0, 0, c);
 838:         }
 839:       else
 840:         {
 841:           assert false : "Must not happen";
 842:         }
 843:       return joined;
 844:     }
 845: 
 846:     /**
 847:      * Performs the actual work for {@link #change}. The elements at the
 848:      * interval boundaries are split up (if necessary) so that the interval
 849:      * boundaries are located at element boundaries.
 850:      */
 851:     protected void changeUpdate()
 852:     {
 853:       boolean didEnd = split(offset, length);
 854:       if (! didEnd)
 855:         {
 856:           // need to do the other end
 857:           while (elementStack.size() != 0)
 858:             {
 859:               pop();
 860:             }
 861:           split(offset + length, 0);
 862:         }
 863:       while (elementStack.size() != 0)
 864:         {
 865:           pop();
 866:         }
 867:     }
 868: 
 869:     /**
 870:      * Modifies the element structure so that the specified interval starts and
 871:      * ends at an element boundary. Content and paragraph elements are split and
 872:      * created as necessary. This also updates the
 873:      * <code>DefaultDocumentEvent</code> to reflect the structural changes.
 874:      * The bulk work is delegated to {@link #changeUpdate()}.
 875:      * 
 876:      * @param offset
 877:      *          the start index of the interval to be changed
 878:      * @param length
 879:      *          the length of the interval to be changed
 880:      * @param ev
 881:      *          the <code>DefaultDocumentEvent</code> describing the change
 882:      */
 883:     public void change(int offset, int length, DefaultDocumentEvent ev)
 884:     {
 885:       prepareEdit(offset, length);
 886:       changeUpdate();
 887:       finishEdit(ev);
 888:     }
 889: 
 890:     /**
 891:      * Creates and returns a deep clone of the specified <code>clonee</code>
 892:      * with the specified parent as new parent.
 893:      *
 894:      * This method can only clone direct instances of {@link BranchElement}
 895:      * or {@link LeafElement}.
 896:      *
 897:      * @param parent the new parent
 898:      * @param clonee the element to be cloned