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
 899:      *
 900:      * @return the cloned element with the new parent
 901:      */
 902:     public Element clone(Element parent, Element clonee)
 903:     {
 904:       Element clone = clonee;
 905:       // We can only handle AbstractElements here.
 906:       if (clonee instanceof BranchElement)
 907:         {
 908:           BranchElement branchEl = (BranchElement) clonee;
 909:           BranchElement branchClone =
 910:             new BranchElement(parent, branchEl.getAttributes());
 911:           // Also clone all of the children.
 912:           int numChildren = branchClone.getElementCount();
 913:           Element[] cloneChildren = new Element[numChildren];
 914:           for (int i = 0; i < numChildren; ++i)
 915:             {
 916:               cloneChildren[i] = clone(branchClone,
 917:                                        branchClone.getElement(i));
 918:             }
 919:           branchClone.replace(0, 0, cloneChildren);
 920:           clone = branchClone;
 921:         }
 922:       else if (clonee instanceof LeafElement)
 923:         {
 924:           clone = new LeafElement(parent, clonee.getAttributes(),
 925:                                   clonee.getStartOffset(),
 926:                                   clonee.getEndOffset());
 927:         }
 928:       return clone;
 929:     }
 930: 
 931:     private Element cloneAsNecessary(Element parent, Element clonee,
 932:                                      int rmOffs0, int rmOffs1)
 933:     {
 934:       Element cloned;
 935:       if (clonee.isLeaf())
 936:         {
 937:           cloned = createLeafElement(parent, clonee.getAttributes(),
 938:                                      clonee.getStartOffset(),
 939:                                      clonee.getEndOffset());
 940:         }
 941:       else
 942:         {
 943:           Element e = createBranchElement(parent, clonee.getAttributes());
 944:           int n = clonee.getElementCount();
 945:           ArrayList childrenList = new ArrayList(n);
 946:           for (int i = 0; i < n; i++)
 947:             {
 948:               Element elem = clonee.getElement(i);
 949:               if (elem.getStartOffset() < rmOffs0
 950:                   || elem.getEndOffset() > rmOffs1)
 951:                 {
 952:                   childrenList.add(cloneAsNecessary(e, elem, rmOffs0,
 953:                                                     rmOffs1));
 954:                 }
 955:             }
 956:           Element[] children = new Element[childrenList.size()];
 957:           children = (Element[]) childrenList.toArray(children);
 958:           ((BranchElement) e).replace(0, 0, children);
 959:           cloned = e;
 960:         }
 961:       return cloned;
 962:     }
 963: 
 964:     /**
 965:      * Inserts new <code>Element</code> in the document at the specified
 966:      * position. Most of the work is done by {@link #insertUpdate}, after some
 967:      * fields have been prepared for it.
 968:      * 
 969:      * @param offset
 970:      *          the location in the document at which the content is inserted
 971:      * @param length
 972:      *          the length of the inserted content
 973:      * @param data
 974:      *          the element specifications for the content to be inserted
 975:      * @param ev
 976:      *          the document event that is updated to reflect the structural
 977:      *          changes
 978:      */
 979:     public void insert(int offset, int length, ElementSpec[] data,
 980:                        DefaultDocumentEvent ev)
 981:     {
 982:       if (length > 0)
 983:     {
 984:       prepareEdit(offset, length);
 985:       insertUpdate(data);
 986:       finishEdit(ev);
 987:     }
 988:     }
 989: 
 990:     /**
 991:      * Prepares the state of this object for performing an insert.
 992:      *
 993:      * @param offset the offset at which is inserted
 994:      * @param length the length of the inserted region
 995:      */
 996:     private void prepareEdit(int offset, int length)
 997:     {
 998:       this.offset = offset;
 999:       this.pos = offset;
1000:       this.endOffset = offset + length;
1001:       this.length = length;
1002: 
1003:       if (edits == null)
1004:     edits = new ArrayList();
1005:       else
1006:     edits.clear();
1007: 
1008:       if (elementStack == null)
1009:     elementStack = new Stack();
1010:       else
1011:     elementStack.clear();
1012: 
1013:       fracturedParent = null;
1014:       fracturedChild = null;
1015:       offsetLastIndex = false;
1016:       offsetLastIndexReplace = false;
1017:     }
1018: 
1019:     /**
1020:      * Finishes an insert. This applies all changes and updates
1021:      * the DocumentEvent.
1022:      *
1023:      * @param ev the document event
1024:      */
1025:     private void finishEdit(DefaultDocumentEvent ev)
1026:     {
1027:       // This for loop applies all the changes that were made and updates the
1028:       // DocumentEvent.
1029:       for (Iterator i = edits.iterator(); i.hasNext();)
1030:     {
1031:           Edit edits = (Edit) i.next();
1032:           Element[] removed = new Element[edits.removed.size()];
1033:           removed = (Element[]) edits.removed.toArray(removed);
1034:           Element[] added = new Element[edits.added.size()];
1035:           added = (Element[]) edits.added.toArray(added);
1036:           int index = edits.index;
1037:           BranchElement parent = (BranchElement) edits.e;
1038:           parent.replace(index, removed.length, added);
1039:           ElementEdit ee = new ElementEdit(parent, index, removed, added);
1040:           ev.addEdit(ee);
1041:     }
1042:       edits.clear();
1043:       elementStack.clear();
1044:     }
1045: 
1046:     /**
1047:      * Inserts new content.
1048:      * 
1049:      * @param data the element specifications for the elements to be inserted
1050:      */
1051:     protected void insertUpdate(ElementSpec[] data)
1052:     {
1053:       // Push the current path to the stack.
1054:       Element current = root;
1055:       int index = current.getElementIndex(offset);
1056:       while (! current.isLeaf())
1057:         {
1058:           Element child = current.getElement(index);
1059:           int editIndex = child.isLeaf() ? index : index + 1;
1060:           Edit edit = new Edit(current, editIndex);
1061:           elementStack.push(edit);
1062:           current = child;
1063:           index = current.getElementIndex(offset);
1064:         }
1065: 
1066:       // Create a copy of the original path.
1067:       insertPath = new Edit[elementStack.size()];
1068:       insertPath = (Edit[]) elementStack.toArray(insertPath);
1069: 
1070:       // No fracture yet.
1071:       createdFracture = false;
1072: 
1073:       // Insert first content tag.
1074:       int i = 0;
1075:       recreateLeafs = false;
1076:       int type = data[0].getType();
1077:       if (type == ElementSpec.ContentType)
1078:         {
1079:           // If the first tag is content we must treat it separately to allow
1080:           // for joining properly to previous Elements and to ensure that
1081:           // no extra LeafElements are erroneously inserted.
1082:           insertFirstContentTag(data);
1083:           pos += data[0].length;
1084:           i = 1;
1085:         }
1086:       else
1087:         {
1088:           createFracture(data);
1089:           i = 0;
1090:         }
1091: 
1092:       // Handle each ElementSpec individually.
1093:       for (; i < data.length; i++)
1094:         {
1095:           insertElement(data[i]);
1096:         }
1097: 
1098:       // Fracture if we haven't done yet.
1099:       if (! createdFracture)
1100:         fracture(-1);
1101: 
1102:       // Pop the remaining stack.
1103:       while (elementStack.size() != 0)
1104:         pop();
1105: 
1106:       // Offset last index if necessary.
1107:       if (offsetLastIndex && offsetLastIndexReplace)
1108:         insertPath[insertPath.length - 1].index++;
1109: 
1110:       // Make sure we havea an Edit for each path item that has a change.
1111:       for (int p = insertPath.length - 1; p >= 0; p--)
1112:         {
1113:           Edit edit = insertPath[p];
1114:           if (edit.e == fracturedParent)
1115:             edit.added.add(fracturedChild);
1116:           if ((edit.added.size() > 0 || edit.removed.size() > 0)
1117:               && ! edits.contains(edit))
1118:             edits.add(edit);
1119:         }
1120: 
1121:       // Remove element that would be created by an insert at 0 with
1122:       // an initial end tag.
1123:       if (offset == 0 && fracturedParent != null
1124:           && data[0].getType() == ElementSpec.EndTagType)
1125:         {
1126:           int p;
1127:           for (p = 0;
1128:                p < data.length && data[p].getType() == ElementSpec.EndTagType;
1129:                p++)
1130:             ;
1131:           
1132:           Edit edit = insertPath[insertPath.length - p - 1];
1133:           edit.index--;
1134:           edit.removed.add(0, edit.e.getElement(edit.index));
1135:         }
1136:     }
1137: 
1138:     private void pop()
1139:     {
1140:       Edit edit = (Edit) elementStack.peek();
1141:       elementStack.pop();
1142:       if ((edit.added.size() > 0) || (edit.removed.size() > 0))
1143:         {
1144:           edits.add(edit);
1145:         }
1146:       else if (! elementStack.isEmpty())
1147:         {
1148:           Element e = edit.e;
1149:           if (e.getElementCount() == 0)
1150:             {
1151:               // If we pushed a branch element that didn't get
1152:               // used, make sure its not marked as having been added.
1153:               edit = (Edit) elementStack.peek();
1154:               edit.added.remove(e);
1155:           }
1156:       }
1157:     }
1158: 
1159:     private void insertElement(ElementSpec spec)
1160:     {
1161:       if (elementStack.isEmpty())
1162:         return;
1163:       
1164:       Edit edit = (Edit) elementStack.peek();
1165:       switch (spec.getType())
1166:         {
1167:         case ElementSpec.StartTagType:
1168:           switch (spec.getDirection())
1169:             {
1170:             case ElementSpec.JoinFractureDirection:
1171:               // Fracture the tree and ensure the appropriate element
1172:               // is on top of the stack.
1173:               if (! createdFracture)
1174:                 {
1175:                   fracture(elementStack.size() - 1);
1176:                 }
1177:               if (! edit.isFracture)
1178:                 {
1179:                   // If the parent isn't a fracture, then the fracture is
1180:                   // in fracturedChild.
1181:                   Edit newEdit = new Edit(fracturedChild, 0, true);
1182:                   elementStack.push(newEdit);
1183:                 }
1184:               else
1185:                 {
1186:                   // Otherwise use the parent's first child.
1187:                   Element el = edit.e.getElement(0);
1188:                   Edit newEdit = new Edit(el, 0, true);
1189:                   elementStack.push(newEdit);
1190:                 }
1191:               break;
1192:             case ElementSpec.JoinNextDirection:
1193:               // Push the next paragraph element onto the stack so
1194:               // future insertions are added to it.
1195:               Element parent = edit.e.getElement(edit.index);
1196:               if (parent.isLeaf())
1197:                 {
1198:                   if (edit.index + 1 < edit.e.getElementCount())
1199:                     parent = edit.e.getElement(edit.index + 1);
1200:                   else
1201:                     assert false; // Must not happen.
1202:                 }
1203:               elementStack.push(new Edit(parent, 0, true));
1204:               break;
1205:             default:
1206:               Element branch = createBranchElement(edit.e,
1207:                                                    spec.getAttributes());
1208:               edit.added.add(branch);
1209:               elementStack.push(new Edit(branch, 0));
1210:               break;
1211:             }
1212:           break;
1213:         case ElementSpec.EndTagType:
1214:           pop();
1215:           break;
1216:         case ElementSpec.ContentType:
1217:           insertContentTag(spec, edit);
1218:           break;
1219:         }
1220:     }
1221: 
1222:     /**
1223:      * Inserts the first tag into the document.
1224:      * 
1225:      * @param data -
1226:      *          the data to be inserted.
1227:      */
1228:     private void insertFirstContentTag(ElementSpec[] data)
1229:     {
1230:       ElementSpec first = data[0];
1231:       Edit edit = (Edit) elementStack.peek();
1232:       Element current = edit.e.getElement(edit.index);
1233:       int firstEndOffset = offset + first.length;
1234:       boolean onlyContent = data.length == 1;
1235:       switch (first.getDirection())
1236:         {
1237:         case ElementSpec.JoinPreviousDirection:
1238:           if (current.getEndOffset() != firstEndOffset && ! onlyContent)
1239:             {
1240:               Element newEl1 = createLeafElement(edit.e,
1241:                                                  current.getAttributes(),
1242:                                                  current.getStartOffset(),
1243:                                                  firstEndOffset);
1244:               edit.added.add(newEl1);
1245:               edit.removed.add(current);
1246:               if (current.getEndOffset() != endOffset)
1247:                 recreateLeafs = true;
1248:               else
1249:                 offsetLastIndex = true;
1250:             }
1251:           else
1252:             {
1253:               offsetLastIndex = true;
1254:               offsetLastIndexReplace = true;
1255:             }
1256:           break;
1257:         case ElementSpec.JoinNextDirection:
1258:           if (offset != 0)
1259:             {
1260:               Element newEl1 = createLeafElement(edit.e,
1261:                                                  current.getAttributes(),
1262:                                                  current.getStartOffset(),
1263:                                                  offset);
1264:               edit.added.add(newEl1);
1265:               Element next = edit.e.getElement(edit.index + 1);
1266:               if (onlyContent)
1267:                 newEl1 = createLeafElement(edit.e, next.getAttributes(),
1268:                                            offset, next.getEndOffset());
1269:               else
1270:                 {
1271:                   newEl1 = createLeafElement(edit.e, next.getAttributes(),
1272:                                              offset, firstEndOffset);
1273:                 }
1274:               edit.added.add(newEl1);
1275:               edit.removed.add(current);
1276:               edit.removed.add(next);
1277:             }
1278:           break;
1279:         default: // OriginateDirection.
1280:           if (current.getStartOffset() != offset)
1281:             {
1282:               Element newEl = createLeafElement(edit.e,
1283:                                                 current.getAttributes(),
1284:                                                 current.getStartOffset(),
1285:                                                 offset);
1286:               edit.added.add(newEl);
1287:             }
1288:           edit.removed.add(current);
1289:           Element newEl1 = createLeafElement(edit.e, first.getAttributes(),
1290:                                              offset, firstEndOffset);
1291:           edit.added.add(newEl1);
1292:           if (current.getEndOffset() != endOffset)
1293:             recreateLeafs = true;
1294:           else
1295:             offsetLastIndex = true;
1296:           break;
1297:         }
1298:     }
1299: 
1300:     /**
1301:      * Inserts a content element into the document structure.
1302:      * 
1303:      * @param tag -
1304:      *          the element spec
1305:      */
1306:     private void insertContentTag(ElementSpec tag, Edit edit)
1307:     {
1308:       int len = tag.getLength();
1309:       int dir = tag.getDirection();
1310:       if (dir == ElementSpec.JoinNextDirection)
1311:         {
1312:           if (! edit.isFracture)
1313:             {
1314:               Element first = null;
1315:               if (insertPath != null)
1316:                 {
1317:                   for (int p = insertPath.length - 1; p >= 0; p--)
1318:                     {
1319:                       if (insertPath[p] == edit)
1320:                         {
1321:                           if (p != insertPath.length - 1)
1322:                             first = edit.e.getElement(edit.index);
1323:                           break;
1324:                         }
1325:                     }
1326:                 }
1327:               if (first == null)
1328:                 first = edit.e.getElement(edit.index + 1);
1329:               Element leaf = createLeafElement(edit.e, first.getAttributes(),
1330:                                                pos, first.getEndOffset());
1331:               edit.added.add(leaf);
1332:               edit.removed.add(first);
1333:             }
1334:           else
1335:             {
1336:               Element first = edit.e.getElement(0);
1337:               Element leaf = createLeafElement(edit.e, first.getAttributes(),
1338:                                                pos, first.getEndOffset());
1339:               edit.added.add(leaf);
1340:               edit.removed.add(first);
1341:             }
1342:         }
1343:       else 
1344:         {
1345:           Element leaf = createLeafElement(edit.e, tag.getAttributes(), pos,
1346:                                            pos + len);
1347:           edit.added.add(leaf);
1348:         }
1349: 
1350:       pos += len;
1351:       
1352:     }
1353: 
1354:     /**
1355:      * This method fractures bottomost leaf in the elementStack. This
1356:      * happens when the first inserted tag is not content.
1357:      * 
1358:      * @param data
1359:      *          the ElementSpecs used for the entire insertion
1360:      */
1361:     private void createFracture(ElementSpec[] data)
1362:     {
1363:       Edit edit = (Edit) elementStack.peek();
1364:       Element child = edit.e.getElement(edit.index);
1365:       if (offset != 0)
1366:         {
1367:           Element newChild = createLeafElement(edit.e, child.getAttributes(),
1368:                                                child.getStartOffset(), offset);
1369:           edit.added.add(newChild);
1370:         }
1371:       edit.removed.add(child);
1372:       if (child.getEndOffset() != endOffset)
1373:         recreateLeafs = true;
1374:       else
1375:         offsetLastIndex = true;
1376:     }
1377: 
1378:     private void fracture(int depth)
1379:     {
1380:       int len = insertPath.length;
1381:       int lastIndex = -1;
1382:       boolean recreate = recreateLeafs;
1383:       Edit lastEdit = insertPath[len - 1];
1384:       boolean childChanged = lastEdit.index + 1 < lastEdit.e.getElementCount();
1385:       int deepestChangedIndex = recreate ? len : - 1;
1386:       int lastChangedIndex = len - 1;
1387:       createdFracture = true;
1388:       for (int i = len - 2; i >= 0; i--)
1389:         {
1390:           Edit edit = insertPath[i];
1391:           if (edit.added.size() > 0 || i == depth)
1392:             {
1393:               lastIndex = i;
1394:               if (! recreate && childChanged)
1395:                 {
1396:                   recreate = true;
1397:                   if (deepestChangedIndex == -1)
1398:                     deepestChangedIndex = lastChangedIndex + 1;
1399:                 }
1400:             }
1401:           if (! childChanged && edit.index < edit.e.getElementCount())
1402:             {
1403:               childChanged = true;
1404:               lastChangedIndex = i;
1405:             }
1406:         }
1407:       if (recreate)
1408:         {
1409:           if (lastIndex == -1)
1410:             lastIndex = len - 1;
1411:           recreate(lastIndex, deepestChangedIndex);
1412:         }
1413:     }
1414: 
1415:     private void recreate(int startIndex, int endIndex)
1416:     {
1417:       // Recreate the element representing the inserted index.
1418:       Edit edit = insertPath[startIndex];
1419:       Element child;
1420:       Element newChild;
1421:       int changeLength = insertPath.length;
1422: 
1423:       if (startIndex + 1 == changeLength)
1424:         child = edit.e.getElement(edit.index);
1425:       else
1426:         child = edit.e.getElement(edit.index - 1);
1427: 
1428:       if(child.isLeaf())
1429:         {
1430:           newChild = createLeafElement(edit.e, child.getAttributes(),
1431:                                    Math.max(endOffset, child.getStartOffset()),
1432:                                    child.getEndOffset());
1433:         }
1434:       else
1435:         {
1436:           newChild = createBranchElement(edit.e, child.getAttributes());
1437:         }
1438:       fracturedParent = edit.e;
1439:       fracturedChild = newChild;
1440: 
1441:       // Recreate all the elements to the right of the insertion point.
1442:       Element parent = newChild;
1443:       while (++startIndex < endIndex)
1444:         {
1445:           boolean isEnd = (startIndex + 1) == endIndex;
1446:           boolean isEndLeaf = (startIndex + 1) == changeLength;
1447: 
1448:           // Create the newChild, a duplicate of the elment at
1449:           // index. This isn't done if isEnd and offsetLastIndex are true
1450:           // indicating a join previous was done.
1451:           edit = insertPath[startIndex];
1452: 
1453:           // Determine the child to duplicate, won't have to duplicate
1454:           // if at end of fracture, or offseting index.
1455:           if(isEnd)
1456:             {
1457:               if(offsetLastIndex || ! isEndLeaf)
1458:                 child = null;
1459:               else
1460:                 child = edit.e.getElement(edit.index);
1461:             }
1462:           else
1463:             {
1464:               child = edit.e.getElement(edit.index - 1);
1465:             }
1466: 
1467:           // Duplicate it.
1468:           if(child != null)
1469:             {
1470:               if(child.isLeaf())
1471:                 {
1472:                   newChild = createLeafElement(parent, child.getAttributes(),
1473:                                    Math.max(endOffset, child.getStartOffset()),
1474:                                    child.getEndOffset());
1475:                 }
1476:               else
1477:                 {
1478:                   newChild = createBranchElement(parent,
1479:                                                  child.getAttributes());
1480:                 }
1481:             }
1482:           else
1483:             newChild = null;
1484: 
1485:         // Recreate the remaining children (there may be none).
1486:         int childrenToMove = edit.e.getElementCount() - edit.index;
1487:         Element[] children;
1488:         int moveStartIndex;
1489:         int childStartIndex = 1;
1490: 
1491:         if (newChild == null)
1492:           {
1493:             // Last part of fracture.
1494:             if (isEndLeaf)
1495:               {
1496:                 childrenToMove--;
1497:                 moveStartIndex = edit.index + 1;
1498:               }
1499:             else
1500:               {
1501:                 moveStartIndex = edit.index;
1502:               }
1503:             childStartIndex = 0;
1504:             children = new Element[childrenToMove];
1505:           }
1506:         else
1507:           {
1508:             if (! isEnd)
1509:               {
1510:                 // Branch.
1511:                 childrenToMove++;
1512:                 moveStartIndex = edit.index;
1513:             }
1514:             else
1515:               {
1516:                 // Last leaf, need to recreate part of it.
1517:                 moveStartIndex = edit.index + 1;
1518:               }
1519:             children = new Element[childrenToMove];
1520:             children[0] = newChild;
1521:         }
1522: 
1523:         for (int c = childStartIndex; c < childrenToMove; c++)
1524:           {
1525:             Element toMove = edit.e.getElement(moveStartIndex++);
1526:             children[c] = recreateFracturedElement(parent, toMove);
1527:             edit.removed.add(toMove);
1528:           }
1529:         ((BranchElement) parent).replace(0, 0, children);
1530:         parent = newChild;
1531:       }
1532: 
1533:     }
1534: 
1535:     private Element recreateFracturedElement(Element parent, Element toCopy)
1536:     {
1537:       Element recreated;
1538:       if(toCopy.isLeaf())
1539:         {
1540:           recreated = createLeafElement(parent, toCopy.getAttributes(),
1541:                                   Math.max(toCopy.getStartOffset(), endOffset),
1542:                                   toCopy.getEndOffset());
1543:         }
1544:       else
1545:         {
1546:           Element newParent = createBranchElement(parent,
1547:                                                   toCopy.getAttributes());
1548:           int childCount = toCopy.getElementCount();
1549:           Element[] newChildren = new Element[childCount];
1550:           for (int i = 0; i < childCount; i++)
1551:             {
1552:               newChildren[i] = recreateFracturedElement(newParent,
1553:                                                         toCopy.getElement(i));
1554:             }
1555:           ((BranchElement) newParent).replace(0, 0, newChildren);
1556:           recreated = newParent;
1557:         }
1558:       return recreated;
1559:     }
1560: 
1561:     private boolean split(int offs, int len)
1562:     {
1563:       boolean splitEnd = false;
1564:       // Push the path to the stack.
1565:       Element e = root;
1566:       int index = e.getElementIndex(offs);
1567:       while (! e.isLeaf())
1568:         {
1569:           elementStack.push(new Edit(e, index));
1570:           e = e.getElement(index);
1571:           index = e.getElementIndex(offs);
1572:         }
1573: 
1574:       Edit ec = (Edit) elementStack.peek();
1575:       Element child = ec.e.getElement(ec.index);
1576:       // Make sure there is something to do. If the
1577:       // offset is already at a boundary then there is
1578:       // nothing to do.
1579:       if (child.getStartOffset() < offs && offs < child.getEndOffset())
1580:         {
1581:           // We need to split, now see if the other end is within
1582:           // the same parent.
1583:           int index0 = ec.index;
1584:           int index1 = index0;
1585:           if (((offs + len) < ec.e.getEndOffset()) && (len != 0))
1586:             {
1587:               // It's a range split in the same parent.
1588:               index1 = ec.e.getElementIndex(offs+len);
1589:               if (index1 == index0)
1590:                 {
1591:                   // It's a three-way split.
1592:                   ec.removed.add(child);
1593:                   e = createLeafElement(ec.e, child.getAttributes(),
1594:                                         child.getStartOffset(), offs);
1595:                   ec.added.add(e);
1596:                   e = createLeafElement(ec.e, child.getAttributes(),
1597:                                         offs, offs + len);
1598:                   ec.added.add(e);
1599:                   e = createLeafElement(ec.e, child.getAttributes(),
1600:                                         offs + len, child.getEndOffset());
1601:                   ec.added.add(e);
1602:                   return true;
1603:                 }
1604:               else
1605:                 {
1606:                   child = ec.e.getElement(index1);
1607:                   if ((offs + len) == child.getStartOffset())
1608:                     {
1609:                       // End is already on a boundary.
1610:                       index1 = index0;
1611:                     }
1612:                 }
1613:               splitEnd = true;
1614:             }
1615: 
1616:           // Split the first location.
1617:           pos = offs;
1618:           child = ec.e.getElement(index0);
1619:           ec.removed.add(child);
1620:           e = createLeafElement(ec.e, child.getAttributes(),
1621:                                 child.getStartOffset(), pos);
1622:           ec.added.add(e);
1623:           e = createLeafElement(ec.e, child.getAttributes(),
1624:                                 pos, child.getEndOffset());
1625:           ec.added.add(e);
1626: 
1627:           // Pick up things in the middle.
1628:           for (int i = index0 + 1; i < index1; i++)
1629:             {
1630:               child = ec.e.getElement(i);
1631:               ec.removed.add(child);
1632:               ec.added.add(child);
1633:             }
1634: 
1635:           if (index1 != index0)
1636:             {
1637:               child = ec.e.getElement(index1);
1638:               pos = offs + len;
1639:               ec.removed.add(child);
1640:               e = createLeafElement(ec.e, child.getAttributes(),
1641:                                     child.getStartOffset(), pos);
1642:               ec.added.add(e);
1643:               e = createLeafElement(ec.e, child.getAttributes(),
1644:                                     pos, child.getEndOffset());
1645:               
1646:               ec.added.add(e);
1647:             }
1648:         }
1649:       return splitEnd;
1650:       
1651:     }
1652: 
1653:   }
1654: 
1655: 
1656:   /**
1657:    * An element type for sections. This is a simple BranchElement with a unique
1658:    * name.
1659:    */
1660:   protected class SectionElement extends BranchElement
1661:   {
1662:     /**
1663:      * Creates a new SectionElement.
1664:      */
1665:     public SectionElement()
1666:     {
1667:       super(null, null);
1668:     }
1669: 
1670:     /**
1671:      * Returns the name of the element. This method always returns
1672:      * &quot;section&quot;.
1673:      * 
1674:      * @return the name of the element
1675:      */
1676:     public String getName()
1677:     {
1678:       return SectionElementName;
1679:     }
1680:   }
1681: 
1682:   /**
1683:    * Receives notification when any of the document's style changes and calls
1684:    * {@link DefaultStyledDocument#styleChanged(Style)}.
1685:    * 
1686:    * @author Roman Kennke (kennke@aicas.com)
1687:    */
1688:   private class StyleChangeListener implements ChangeListener
1689:   {
1690: 
1691:     /**
1692:      * Receives notification when any of the document's style changes and calls
1693:      * {@link DefaultStyledDocument#styleChanged(Style)}.
1694:      * 
1695:      * @param event
1696:      *          the change event
1697:      */
1698:     public void stateChanged(ChangeEvent event)
1699:     {
1700:       Style style = (Style) event.getSource();
1701:       styleChanged(style);
1702:     }
1703:   }
1704: 
1705:   /** The serialization UID (compatible with JDK1.5). */
1706:   private static final long serialVersionUID = 940485415728614849L;
1707: 
1708:   /**
1709:    * The default size to use for new content buffers.
1710:    */
1711:   public static final int BUFFER_SIZE_DEFAULT = 4096;
1712: 
1713:   /**
1714:    * The <code>EditorBuffer</code> that is used to manage to
1715:    * <code>Element</code> hierarchy.
1716:    */
1717:   protected DefaultStyledDocument.ElementBuffer buffer;
1718: 
1719:   /**
1720:    * Listens for changes on this document's styles and notifies styleChanged().
1721:    */
1722:   private StyleChangeListener styleChangeListener;
1723: 
1724:   /**
1725:    * Creates a new <code>DefaultStyledDocument</code>.
1726:    */
1727:   public DefaultStyledDocument()
1728:   {
1729:     this(new GapContent(BUFFER_SIZE_DEFAULT), new StyleContext());
1730:   }
1731: 
1732:   /**
1733:    * Creates a new <code>DefaultStyledDocument</code> that uses the specified
1734:    * {@link StyleContext}.
1735:    * 
1736:    * @param context
1737:    *          the <code>StyleContext</code> to use
1738:    */
1739:   public DefaultStyledDocument(StyleContext context)
1740:   {
1741:     this(new GapContent(BUFFER_SIZE_DEFAULT), context);
1742:   }
1743: 
1744:   /**
1745:    * Creates a new <code>DefaultStyledDocument</code> that uses the specified
1746:    * {@link StyleContext} and {@link Content} buffer.
1747:    * 
1748:    * @param content
1749:    *          the <code>Content</code> buffer to use
1750:    * @param context
1751:    *          the <code>StyleContext</code> to use
1752:    */
1753:   public DefaultStyledDocument(AbstractDocument.Content content,
1754:                                StyleContext context)
1755:   {
1756:     super(content, context);
1757:     buffer = new ElementBuffer(createDefaultRoot());
1758:     setLogicalStyle(0, context.getStyle(StyleContext.DEFAULT_STYLE));
1759:   }
1760: 
1761:   /**
1762:    * Adds a style into the style hierarchy. Unspecified style attributes can be
1763:    * resolved in the <code>parent</code> style, if one is specified. While it
1764:    * is legal to add nameless styles (<code>nm == null</code),
1765:    * you must be aware that the client application is then responsible
1766:    * for managing the style hierarchy, since unnamed styles cannot be
1767:    * looked up by their name.
1768:    *
1769:    * @param nm the name of the style or <code>null</code> if the style should
1770:    *           be unnamed
1771:    * @param parent the parent in which unspecified style attributes are
1772:    *           resolved, or <code>null</code> if that is not necessary
1773:    *
1774:    * @return the newly created <code>Style</code>
1775:    */
1776:   public Style addStyle(String nm, Style parent)
1777:   {
1778:     StyleContext context = (StyleContext) getAttributeContext();
1779:     Style newStyle = context.addStyle(nm, parent);
1780: 
1781:     // Register change listener.
1782:     if (styleChangeListener == null)
1783:       styleChangeListener = new StyleChangeListener();
1784:     newStyle.addChangeListener(styleChangeListener);
1785: 
1786:     return newStyle;
1787:   }
1788: 
1789:   /**
1790:    * Create the default root element for this kind of <code>Document</code>.
1791:    * 
1792:    * @return the default root element for this kind of <code>Document</code>
1793:    */
1794:   protected AbstractDocument.AbstractElement createDefaultRoot()
1795:   {
1796:     Element[] tmp;
1797:     SectionElement section = new SectionElement();
1798: 
1799:     BranchElement paragraph = new BranchElement(section, null);
1800:     tmp = new Element[1];
1801:     tmp[0] = paragraph;
1802:     section.replace(0, 0, tmp);
1803: 
1804:     Element leaf = new LeafElement(paragraph, null, 0, 1);
1805:     tmp = new Element[1];
1806:     tmp[0] = leaf;
1807:     paragraph.replace(0, 0, tmp);
1808: 
1809:     return section;
1810:   }
1811: 
1812:   /**
1813:    * Returns the <code>Element</code> that corresponds to the character at the
1814:    * specified position.
1815:    * 
1816:    * @param position
1817:    *          the position of which we query the corresponding
1818:    *          <code>Element</code>
1819:    * @return the <code>Element</code> that corresponds to the character at the
1820:    *         specified position
1821:    */
1822:   public Element getCharacterElement(int position)
1823:   {
1824:     Element element = getDefaultRootElement();
1825: 
1826:     while (!element.isLeaf())
1827:       {
1828:         int index = element.getElementIndex(position);
1829:         element = element.getElement(index);
1830:       }
1831: 
1832:     return element;
1833:   }
1834: 
1835:   /**
1836:    * Extracts a background color from a set of attributes.
1837:    * 
1838:    * @param attributes
1839:    *          the attributes from which to get a background color
1840:    * @return the background color that correspond to the attributes
1841:    */
1842:   public Color getBackground(AttributeSet attributes)
1843:   {
1844:     StyleContext context = (StyleContext) getAttributeContext();
1845:     return context.getBackground(attributes);
1846:   }
1847: 
1848:   /**
1849:    * Returns the default root element.
1850:    * 
1851:    * @return the default root element
1852:    */
1853:   public Element getDefaultRootElement()
1854:   {
1855:     return buffer.getRootElement();
1856:   }
1857: 
1858:   /**
1859:    * Extracts a font from a set of attributes.
1860:    * 
1861:    * @param attributes
1862:    *          the attributes from which to get a font
1863:    * @return the font that correspond to the attributes
1864:    */
1865:   public Font getFont(AttributeSet attributes)
1866:   {
1867:     StyleContext context = (StyleContext) getAttributeContext();
1868:     return context.getFont(attributes);
1869:   }
1870: 
1871:   /**
1872:    * Extracts a foreground color from a set of attributes.
1873:    * 
1874:    * @param attributes
1875:    *          the attributes from which to get a foreground color
1876:    * @return the foreground color that correspond to the attributes
1877:    */
1878:   public Color getForeground(AttributeSet attributes)
1879:   {
1880:     StyleContext context = (StyleContext) getAttributeContext();
1881:     return context.getForeground(attributes);
1882:   }
1883: 
1884:   /**
1885:    * Returns the logical <code>Style</code> for the specified position.
1886:    * 
1887:    * @param position
1888:    *          the position from which to query to logical style
1889:    * @return the logical <code>Style</code> for the specified position
1890:    */
1891:   public Style getLogicalStyle(int position)
1892:   {
1893:     Element paragraph = getParagraphElement(position);
1894:     AttributeSet attributes = paragraph.getAttributes();
1895:     AttributeSet a = attributes.getResolveParent();
1896:     // If the resolve parent is not of type Style, we return null.
1897:     if (a instanceof Style)
1898:       return (Style) a;
1899:     return null;
1900:   }
1901: 
1902:   /**
1903:    * Returns the paragraph element for the specified position. If the position
1904:    * is outside the bounds of the document's root element, then the closest
1905:    * element is returned. That is the last paragraph if
1906:    * <code>position >= endIndex</code> or the first paragraph if
1907:    * <code>position < startIndex</code>.
1908:    * 
1909:    * @param position
1910:    *          the position for which to query the paragraph element
1911:    * @return the paragraph element for the specified position
1912:    */
1913:   public Element getParagraphElement(int position)
1914:   {
1915:     Element e = getDefaultRootElement();
1916:     while (!e.isLeaf())
1917:       e = e.getElement(e.getElementIndex(position));
1918: 
1919:     if (e != null)
1920:       return e.getParentElement();
1921:     return e;
1922:   }
1923: 
1924:   /**
1925:    * Looks up and returns a named <code>Style</code>.
1926:    * 
1927:    * @param nm
1928:    *          the name of the <code>Style</code>
1929:    * @return the found <code>Style</code> of <code>null</code> if no such
1930:    *         <code>Style</code> exists
1931:    */
1932:   public Style getStyle(String nm)
1933:   {
1934:     StyleContext context = (StyleContext) getAttributeContext();
1935:     return context.getStyle(nm);
1936:   }
1937: 
1938:   /**
1939:    * Removes a named <code>Style</code> from the style hierarchy.
1940:    * 
1941:    * @param nm
1942:    *          the name of the <code>Style</code> to be removed
1943:    */
1944:   public void removeStyle(String nm)
1945:   {
1946:     StyleContext context = (StyleContext) getAttributeContext();
1947:     context.removeStyle(nm);
1948:   }
1949: 
1950:   /**
1951:    * Sets text attributes for the fragment specified by <code>offset</code>
1952:    * and <code>length</code>.
1953:    * 
1954:    * @param offset
1955:    *          the start offset of the fragment
1956:    * @param length
1957:    *          the length of the fragment
1958:    * @param attributes
1959:    *          the text attributes to set
1960:    * @param replace
1961:    *          if <code>true</code>, the attributes of the current selection
1962:    *          are overridden, otherwise they are merged
1963:    */
1964:   public void setCharacterAttributes(int offset, int length,
1965:                                      AttributeSet attributes, boolean replace)
1966:   {
1967:     // Exit early if length is 0, so no DocumentEvent is created or fired.
1968:     if (length == 0)
1969:       return;
1970:     try
1971:       {
1972:         // Must obtain a write lock for this method. writeLock() and
1973:         // writeUnlock() should always be in try/finally block to make
1974:         // sure that locking happens in a balanced manner.
1975:         writeLock();
1976:         DefaultDocumentEvent ev = new DefaultDocumentEvent(offset,
1977:                                                            length,
1978:                                                            DocumentEvent.EventType.CHANGE);
1979: 
1980:         // Modify the element structure so that the interval begins at an
1981:         // element
1982:         // start and ends at an element end.
1983:         buffer.change(offset, length, ev);
1984: 
1985:         // Visit all paragraph elements within the specified interval
1986:         int end = offset + length;
1987:         Element curr;
1988:         for (int pos = offset; pos < end;)
1989:           {
1990:             // Get the CharacterElement at offset pos.
1991:             curr = getCharacterElement(pos);
1992:             if (pos == curr.getEndOffset())
1993:               break;
1994: 
1995:             MutableAttributeSet a = (MutableAttributeSet) curr.getAttributes();
1996:             ev.addEdit(new AttributeUndoableEdit(curr, attributes, replace));
1997:             // If replace is true, remove all the old attributes.
1998:             if (replace)
1999:               a.removeAttributes(a);
2000:             // Add all the new attributes.
2001:             a.addAttributes(attributes);
2002:             // Increment pos so we can check the next CharacterElement.
2003:             pos = curr.getEndOffset();
2004:           }
2005:         fireChangedUpdate(ev);
2006:         fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
2007:       }
2008:     finally
2009:       {
2010:         writeUnlock();
2011:       }
2012:   }
2013: 
2014:   /**
2015:    * Sets the logical style for the paragraph at the specified position.
2016:    * 
2017:    * @param position
2018:    *          the position at which the logical style is added
2019:    * @param style
2020:    *          the style to set for the current paragraph
2021:    */
2022:   public void setLogicalStyle(int position, Style style)
2023:   {
2024:     Element el = getParagraphElement(position);
2025:     // getParagraphElement doesn't return null but subclasses might so
2026:     // we check for null here.
2027:     if (el == null)
2028:       return;
2029:     try
2030:       {
2031:         writeLock();
2032:         if (el instanceof AbstractElement)
2033:           {
2034:             AbstractElement ael = (AbstractElement) el;
2035:             ael.setResolveParent(style);
2036:             int start = el.getStartOffset();
2037:             int end = el.getEndOffset();
2038:             DefaultDocumentEvent ev = new DefaultDocumentEvent(start,
2039:                                                                end - start,
2040:                                                                DocumentEvent.EventType.CHANGE);
2041:             fireChangedUpdate(ev);
2042:             fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
2043:           }
2044:         else
2045:           throw new AssertionError(
2046:                                    "paragraph elements are expected to be"
2047:                                        + "instances of AbstractDocument.AbstractElement");
2048:       }
2049:     finally
2050:       {
2051:         writeUnlock();
2052:       }
2053:   }
2054: 
2055:   /**
2056:    * Sets text attributes for the paragraph at the specified fragment.
2057:    * 
2058:    * @param offset
2059:    *          the beginning of the fragment
2060:    * @param length
2061:    *          the length of the fragment
2062:    * @param attributes
2063:    *          the text attributes to set
2064:    * @param replace
2065:    *          if <code>true</code>, the attributes of the current selection
2066:    *          are overridden, otherwise they are merged
2067:    */
2068:   public void setParagraphAttributes(int offset, int length,
2069:                                      AttributeSet attributes, boolean replace)
2070:   {
2071:     try
2072:       {
2073:         // Must obtain a write lock for this method. writeLock() and
2074:         // writeUnlock() should always be in try/finally blocks to make
2075:         // sure that locking occurs in a balanced manner.
2076:         writeLock();
2077: 
2078:         // Create a DocumentEvent to use for changedUpdate().
2079:         DefaultDocumentEvent ev = new DefaultDocumentEvent(offset,
2080:                                                            length,
2081:                                                            DocumentEvent.EventType.CHANGE);
2082: 
2083:         // Have to iterate through all the _paragraph_ elements that are
2084:         // contained or partially contained in the interval
2085:         // (offset, offset + length).
2086:         Element rootElement = getDefaultRootElement();
2087:         int startElement = rootElement.getElementIndex(offset);
2088:         int endElement = rootElement.getElementIndex(offset + length - 1);
2089:         if (endElement < startElement)
2090:           endElement = startElement;
2091: 
2092:         for (int i = startElement; i <= endElement; i++)
2093:           {
2094:             Element par = rootElement.getElement(i);
2095:             MutableAttributeSet a = (MutableAttributeSet) par.getAttributes();
2096:             // Add the change to the DocumentEvent.
2097:             ev.addEdit(new AttributeUndoableEdit(par, attributes, replace));
2098:             // If replace is true remove the old attributes.
2099:             if (replace)
2100:               a.removeAttributes(a);
2101:             // Add the new attributes.
2102:             a.addAttributes(attributes);
2103:           }
2104:         fireChangedUpdate(ev);
2105:         fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
2106:       }
2107:     finally
2108:       {
2109:         writeUnlock();
2110:       }
2111:   }
2112: 
2113:   /**
2114:    * Called in response to content insert actions. This is used to update the
2115:    * element structure.
2116:    * 
2117:    * @param ev
2118:    *          the <code>DocumentEvent</code> describing the change
2119:    * @param attr
2120:    *          the attributes for the change
2121:    */
2122:   protected void insertUpdate(DefaultDocumentEvent ev, AttributeSet attr)
2123:   {
2124:     int offs = ev.getOffset();
2125:     int len = ev.getLength();
2126:     int endOffs = offs + len;
2127:     if (attr == null)
2128:       attr = SimpleAttributeSet.EMPTY;
2129: 
2130:     // Paragraph attributes are fetched from the point _after_ the insertion.
2131:     Element paragraph = getParagraphElement(endOffs);
2132:     AttributeSet pAttr = paragraph.getAttributes();
2133:     // Character attributes are fetched from the actual insertion point.
2134:     Element paragraph2 = getParagraphElement(offs);
2135:     int contIndex = paragraph2.getElementIndex(offs);
2136:     Element content = paragraph2.getElement(contIndex);
2137:     AttributeSet cAttr = content.getAttributes();
2138: 
2139:     boolean insertAtBoundary = content.getEndOffset() == endOffs;
2140:     try
2141:       {
2142:         Segment s = new Segment();
2143:         ArrayList buf = new ArrayList();
2144:         ElementSpec lastStartTag = null;
2145:         boolean insertAfterNewline = false;
2146:         short lastStartDir = ElementSpec.OriginateDirection;
2147: 
2148:         // Special handle if we are inserting after a newline.
2149:         if (offs > 0)
2150:           {
2151:             getText(offs - 1, 1, s);
2152:             if (s.array[s.offset] == '\n')
2153:               {
2154:                 insertAfterNewline = true;
2155:                 lastStartDir = insertAfterNewline(paragraph, paragraph2,
2156:                                                   pAttr, buf, offs,
2157:                                                   endOffs);
2158:                 // Search last start tag.
2159:                 for (int i = buf.size() - 1; i >= 0 && lastStartTag == null;
2160:                      i--)
2161:                   {
2162:                     ElementSpec tag = (ElementSpec) buf.get(i);
2163:                     if (tag.getType() == ElementSpec.StartTagType)
2164:                       {
2165:                         lastStartTag = tag;
2166:                       }
2167:                   }
2168:               }
2169: 
2170:           }
2171: 
2172:         // If we are not inserting after a newline, the paragraph attributes
2173:         // come from the paragraph under the insertion point.
2174:         if (! insertAfterNewline)
2175:           pAttr = paragraph2.getAttributes();
2176: 
2177:         // Scan text and build up the specs.
2178:         getText(offs, len, s);
2179:         int end = s.offset + s.count;
2180:         int last = s.offset;
2181:         for (int i = s.offset; i < end; i++)
2182:           {
2183:             if (s.array[i] == '\n')
2184:               {
2185:                 int breakOffs = i + 1;
2186:                 buf.add(new ElementSpec(attr, ElementSpec.ContentType,
2187:                                         breakOffs - last));
2188:                 buf.add(new ElementSpec(null, ElementSpec.EndTagType));
2189:                 lastStartTag = new ElementSpec(pAttr,
2190:                                                ElementSpec.StartTagType);
2191:                 buf.add(lastStartTag);
2192:                 last = breakOffs;
2193:               }
2194:           }
2195: 
2196:         // Need to add a tailing content tag if we didn't finish at a boundary.
2197:         if (last < end)
2198:           {
2199:             buf.add(new ElementSpec(attr, ElementSpec.ContentType,
2200:                                     end - last));
2201:           }
2202: 
2203:         // Now we need to fix up the directions of the specs.
2204:         ElementSpec first = (ElementSpec) buf.get(0);
2205:         int doclen = getLength();
2206: 
2207:         // Maybe join-previous the first tag if it is content and has
2208:         // the same attributes as the previous character run.
2209:         if (first.getType() == ElementSpec.ContentType && cAttr.isEqual(attr))
2210:           first.setDirection(ElementSpec.JoinPreviousDirection);
2211: 
2212:         // Join-fracture or join-next the last start tag if necessary.
2213:         if (lastStartTag != null)
2214:           {
2215:             if (insertAfterNewline)
2216:               lastStartTag.setDirection(lastStartDir);
2217:             else if (paragraph2.getEndOffset() != endOffs)
2218:               lastStartTag.setDirection(ElementSpec.JoinFractureDirection);
2219:             else
2220:               {
2221:                 Element par = paragraph2.getParentElement();
2222:                 int par2Index = par.getElementIndex(offs);
2223:                 if (par2Index + 1 < par.getElementCount()
2224:                     && ! par.getElement(par2Index + 1).isLeaf())
2225:                   lastStartTag.setDirection(ElementSpec.JoinNextDirection);
2226:               }
2227:           }
2228: 
2229:         // Join-next last tag if possible.
2230:         if (insertAtBoundary && endOffs < doclen)
2231:           {
2232:             ElementSpec lastTag = (ElementSpec) buf.get(buf.size() - 1);
2233:             if (lastTag.getType() == ElementSpec.ContentType
2234:                 && ((lastStartTag == null
2235:                      && (paragraph == paragraph2 || insertAfterNewline))
2236:                     || (lastStartTag != null
2237:              && lastStartTag.getDirection() != ElementSpec.OriginateDirection)))
2238:               {
2239:                 int nextIndex = paragraph.getElementIndex(endOffs);
2240:                 Element nextRun = paragraph.getElement(nextIndex);
2241:                 if (nextRun.isLeaf() && attr.isEqual(nextRun.getAttributes()))
2242:                   lastTag.setDirection(ElementSpec.JoinNextDirection);
2243:               }
2244:           }
2245: 
2246:         else if (! insertAtBoundary && lastStartTag != null
2247:            && lastStartTag.getDirection() == ElementSpec.JoinFractureDirection)
2248:           {
2249:             ElementSpec lastTag = (ElementSpec) buf.get(buf.size() - 1);
2250:             if (lastTag.getType() == ElementSpec.ContentType
2251:                 && lastTag.getDirection() != ElementSpec.JoinPreviousDirection
2252:                 && attr.isEqual(cAttr))
2253:               {
2254:                 lastTag.setDirection(ElementSpec.JoinNextDirection);
2255:               }
2256:           }
2257: 
2258:         ElementSpec[] specs = new ElementSpec[buf.size()];
2259:         specs = (ElementSpec[]) buf.toArray(specs);
2260:         buffer.insert(offs, len, specs, ev);
2261:       }
2262:     catch (BadLocationException ex)
2263:       {
2264:         // Ignore this. Comment out for debugging.
2265:         ex.printStackTrace();
2266:       }
2267:     super.insertUpdate(ev, attr);
2268:   }
2269: 
2270:   private short insertAfterNewline(Element par1, Element par2,
2271:                                    AttributeSet attr, ArrayList buf,
2272:                                    int offs, int endOffs)
2273:   {
2274:     short dir = 0;
2275:     if (par1.getParentElement() == par2.getParentElement())
2276:       {
2277:         ElementSpec tag = new ElementSpec(attr, ElementSpec.EndTagType);
2278:         buf.add(tag);
2279:         tag = new ElementSpec(attr, ElementSpec.StartTagType);
2280:         buf.add(tag);
2281:         if (par2.getEndOffset() != endOffs)
2282:           dir = ElementSpec.JoinFractureDirection;
2283:         else
2284:           {
2285:             Element par = par2.getParentElement();
2286:             if (par.getElementIndex(offs) + 1 < par.getElementCount())
2287:               dir = ElementSpec.JoinNextDirection;
2288:           }
2289:       }
2290:     else
2291:       {
2292:         // For text with more than 2 levels, find the common parent of
2293:         // par1 and par2.
2294:         ArrayList parentsLeft = new ArrayList();
2295:         ArrayList parentsRight = new ArrayList();
2296:         Element e = par2;
2297:         while (e != null)
2298:           {
2299:             parentsLeft.add(e);
2300:             e = e.getParentElement();
2301:           }
2302:         e = par1;
2303:         int leftIndex = -1;
2304:         while (e != null && (leftIndex = parentsLeft.indexOf(e)) == 1)
2305:           {
2306:             parentsRight.add(e);
2307:             e = e.getParentElement();
2308:           }
2309: 
2310:         if (e != null)
2311:        
2312:           {
2313:             // e is now the common parent.
2314:             // Insert the end tags.
2315:             for (int c = 0; c < leftIndex; c++)
2316:               {
2317:                 buf.add(new ElementSpec(null, ElementSpec.EndTagType));
2318:               }
2319:             // Insert the start tags.
2320:             for (int c = parentsRight.size() - 1; c >= 0; c--)
2321:               {
2322:                 Element el = (Element) parentsRight.get(c);
2323:                 ElementSpec tag = new ElementSpec(el.getAttributes(),
2324:                                                   ElementSpec.StartTagType);
2325:                 if (c > 0)
2326:                   tag.setDirection(ElementSpec.JoinNextDirection);
2327:                 buf.add(tag);
2328:               }
2329:             if (parentsRight.size() > 0)
2330:               dir = ElementSpec.JoinNextDirection;
2331:             else
2332:               dir = ElementSpec.JoinFractureDirection;
2333:           }
2334:         else
2335:           assert false;
2336:       }
2337:     return dir;
2338:   }
2339: 
2340:   /**
2341:    * A helper method to set up the ElementSpec buffer for the special case of an
2342:    * insertion occurring immediately after a newline.
2343:    * 
2344:    * @param specs
2345:    *          the ElementSpec buffer to initialize.
2346:    */
2347:   short handleInsertAfterNewline(Vector specs, int offset, int endOffset,
2348:                                  Element prevParagraph, Element paragraph,
2349:                                  AttributeSet a)
2350:   {
2351:     if (prevParagraph.getParentElement() == paragraph.getParentElement())
2352:       {
2353:         specs.add(new ElementSpec(a, ElementSpec.EndTagType));
2354:         specs.add(new ElementSpec(a, ElementSpec.StartTagType));
2355:         if (paragraph.getStartOffset() != endOffset)
2356:           return ElementSpec.JoinFractureDirection;
2357:         // If there is an Element after this one, use JoinNextDirection.
2358:         Element parent = paragraph.getParentElement();
2359:         if (parent.getElementCount() > (parent.getElementIndex(offset) + 1))
2360:           return ElementSpec.JoinNextDirection;
2361:       }
2362:     return ElementSpec.OriginateDirection;
2363:   }
2364: 
2365:   /**
2366:    * Updates the document structure in response to text removal. This is
2367:    * forwarded to the {@link ElementBuffer} of this document. Any changes to the
2368:    * document structure are added to the specified document event and sent to
2369:    * registered listeners.
2370:    * 
2371:    * @param ev
2372:    *          the document event that records the changes to the document
2373:    */
2374:   protected void removeUpdate(DefaultDocumentEvent ev)
2375:   {
2376:     super.removeUpdate(ev);
2377:     buffer.remove(ev.getOffset(), ev.getLength(), ev);
2378:   }
2379: 
2380:   /**
2381:    * Returns an enumeration of all style names.
2382:    * 
2383:    * @return an enumeration of all style names
2384:    */
2385:   public Enumeration<?> getStyleNames()
2386:   {
2387:     StyleContext context = (StyleContext) getAttributeContext();
2388:     return context.getStyleNames();
2389:   }
2390: 
2391:   /**
2392:    * Called when any of this document's styles changes.
2393:    * 
2394:    * @param style
2395:    *          the style that changed
2396:    */
2397:   protected void styleChanged(Style style)
2398:   {
2399:     // Nothing to do here. This is intended to be overridden by subclasses.
2400:   }
2401: 
2402:   /**
2403:    * Inserts a bulk of structured content at once.
2404:    * 
2405:    * @param offset
2406:    *          the offset at which the content should be inserted
2407:    * @param data
2408:    *          the actual content spec to be inserted
2409:    */
2410:   protected void insert(int offset, ElementSpec[] data)
2411:       throws BadLocationException
2412:   {
2413:     if (data == null || data.length == 0)
2414:       return;
2415:     try
2416:       {
2417:         // writeLock() and writeUnlock() should always be in a try/finally
2418:         // block so that locking balance is guaranteed even if some
2419:         // exception is thrown.
2420:         writeLock();
2421: 
2422:         // First we collect the content to be inserted.
2423:         StringBuffer contentBuffer = new StringBuffer();
2424:         for (int i = 0; i < data.length; i++)
2425:           {
2426:             // Collect all inserts into one so we can get the correct
2427:             // ElementEdit
2428:             ElementSpec spec = data[i];
2429:             if (spec.getArray() != null && spec.getLength() > 0)
2430:               contentBuffer.append(spec.getArray(), spec.getOffset(),
2431:                                    spec.getLength());
2432:           }
2433: 
2434:         int length = contentBuffer.length();
2435: 
2436:         // If there was no content inserted then exit early.
2437:         if (length == 0)
2438:           return;
2439: 
2440:         Content c = getContent();
2441:         UndoableEdit edit = c.insertString(offset,
2442:                                            contentBuffer.toString());
2443: 
2444:         // Create the DocumentEvent with the ElementEdit added
2445:         DefaultDocumentEvent ev = new DefaultDocumentEvent(offset,
2446:                                                            length,
2447:                                                            DocumentEvent.EventType.INSERT);
2448: 
2449:         ev.addEdit(edit);
2450: 
2451:         // Finally we must update the document structure and fire the insert
2452:         // update event.
2453:         buffer.insert(offset, length, data, ev);
2454: 
2455:         super.insertUpdate(ev, null);
2456: 
2457:         ev.end();
2458:         fireInsertUpdate(ev);
2459:         fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
2460:       }
2461:     finally
2462:       {
2463:         writeUnlock();
2464:       }
2465:   }
2466: 
2467:   /**
2468:    * Initializes the <code>DefaultStyledDocument</code> with the specified
2469:    * data.
2470:    * 
2471:    * @param data
2472:    *          the specification of the content with which the document is
2473:    *          initialized
2474:    */
2475:   protected void create(ElementSpec[] data)
2476:   {
2477:     try
2478:       {
2479: 
2480:         // Clear content if there is some.
2481:         int len = getLength();
2482:         if (len > 0)
2483:           remove(0, len);
2484: 
2485:         writeLock();
2486: 
2487:         // Now we insert the content.
2488:         StringBuilder b = new StringBuilder();
2489:         for (int i = 0; i < data.length; ++i)
2490:           {
2491:             ElementSpec el = data[i];
2492:             if (el.getArray() != null && el.getLength() > 0)
2493:               b.append(el.getArray(), el.getOffset(), el.getLength());
2494:           }
2495:         Content content = getContent();
2496:         UndoableEdit cEdit = content.insertString(0, b.toString());
2497: 
2498:         len = b.length();
2499:         DefaultDocumentEvent ev =
2500:           new DefaultDocumentEvent(0, b.length(),
2501:                                    DocumentEvent.EventType.INSERT);
2502:         ev.addEdit(cEdit);
2503: 
2504:         buffer.create(len, data, ev);
2505: 
2506:         // For the bidi update.
2507:         super.insertUpdate(ev, null);
2508: 
2509:         ev.end();
2510:         fireInsertUpdate(ev);
2511:         fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
2512:       }
2513:     catch (BadLocationException ex)
2514:       {
2515:         AssertionError err = new AssertionError("Unexpected bad location");
2516:         err.initCause(ex);
2517:         throw err;
2518:       }
2519:     finally
2520:       {
2521:         writeUnlock();
2522:       }
2523:   }
2524: }