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