Source for javax.swing.text.AbstractDocument

   1: /* AbstractDocument.java --
   2:    Copyright (C) 2002, 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.io.PrintStream;
  42: import java.io.Serializable;
  43: import java.util.Dictionary;
  44: import java.util.Enumeration;
  45: import java.util.EventListener;
  46: import java.util.Hashtable;
  47: import java.util.Vector;
  48: 
  49: import javax.swing.event.DocumentEvent;
  50: import javax.swing.event.DocumentListener;
  51: import javax.swing.event.EventListenerList;
  52: import javax.swing.event.UndoableEditEvent;
  53: import javax.swing.event.UndoableEditListener;
  54: import javax.swing.tree.TreeNode;
  55: import javax.swing.undo.AbstractUndoableEdit;
  56: import javax.swing.undo.CompoundEdit;
  57: import javax.swing.undo.UndoableEdit;
  58: 
  59: /**
  60:  * An abstract base implementation for the {@link Document} interface.
  61:  * This class provides some common functionality for all <code>Element</code>s,
  62:  * most notably it implements a locking mechanism to make document modification
  63:  * thread-safe.
  64:  *
  65:  * @author original author unknown
  66:  * @author Roman Kennke (roman@kennke.org)
  67:  */
  68: public abstract class AbstractDocument implements Document, Serializable
  69: {
  70:   /** The serialization UID (compatible with JDK1.5). */
  71:   private static final long serialVersionUID = 6842927725919637215L;
  72: 
  73:   /**
  74:    * Standard error message to indicate a bad location.
  75:    */
  76:   protected static final String BAD_LOCATION = "document location failure";
  77: 
  78:   /**
  79:    * Standard name for unidirectional <code>Element</code>s.
  80:    */
  81:   public static final String BidiElementName = "bidi level";
  82: 
  83:   /**
  84:    * Standard name for content <code>Element</code>s. These are usually
  85:    * {@link LeafElement}s.
  86:    */
  87:   public static final String ContentElementName = "content";
  88: 
  89:   /**
  90:    * Standard name for paragraph <code>Element</code>s. These are usually
  91:    * {@link BranchElement}s.
  92:    */
  93:   public static final String ParagraphElementName = "paragraph";
  94: 
  95:   /**
  96:    * Standard name for section <code>Element</code>s. These are usually
  97:    * {@link DefaultStyledDocument.SectionElement}s.
  98:    */
  99:   public static final String SectionElementName = "section";
 100: 
 101:   /**
 102:    * Attribute key for storing the element name.
 103:    */
 104:   public static final String ElementNameAttribute = "$ename";
 105: 
 106:   /**
 107:    * The actual content model of this <code>Document</code>.
 108:    */
 109:   Content content;
 110: 
 111:   /**
 112:    * The AttributeContext for this <code>Document</code>.
 113:    */
 114:   AttributeContext context;
 115: 
 116:   /**
 117:    * The currently installed <code>DocumentFilter</code>.
 118:    */
 119:   DocumentFilter documentFilter;
 120: 
 121:   /**
 122:    * The documents properties.
 123:    */
 124:   Dictionary properties;
 125: 
 126:   /**
 127:    * Manages event listeners for this <code>Document</code>.
 128:    */
 129:   protected EventListenerList listenerList = new EventListenerList();
 130:   
 131:   /**
 132:    * Stores the current writer thread.  Used for locking.
 133:    */ 
 134:   private Thread currentWriter = null;
 135:   
 136:   /**
 137:    * The number of readers.  Used for locking.
 138:    */
 139:   private int numReaders = 0;
 140:   
 141:   /**
 142:    * Tells if there are one or more writers waiting.
 143:    */
 144:   private int numWritersWaiting = 0;  
 145: 
 146:   /**
 147:    * A condition variable that readers and writers wait on.
 148:    */
 149:   Object documentCV = new Object();
 150: 
 151:   
 152:   /**
 153:    * Creates a new <code>AbstractDocument</code> with the specified
 154:    * {@link Content} model.
 155:    *
 156:    * @param doc the <code>Content</code> model to be used in this
 157:    *        <code>Document<code>
 158:    *
 159:    * @see GapContent
 160:    * @see StringContent
 161:    */
 162:   protected AbstractDocument(Content doc)
 163:   {
 164:     this(doc, StyleContext.getDefaultStyleContext());
 165:   }
 166: 
 167:   /**
 168:    * Creates a new <code>AbstractDocument</code> with the specified
 169:    * {@link Content} model and {@link AttributeContext}.
 170:    *
 171:    * @param doc the <code>Content</code> model to be used in this
 172:    *        <code>Document<code>
 173:    * @param ctx the <code>AttributeContext</code> to use
 174:    *
 175:    * @see GapContent
 176:    * @see StringContent
 177:    */
 178:   protected AbstractDocument(Content doc, AttributeContext ctx)
 179:   {
 180:     content = doc;
 181:     context = ctx;
 182:   }
 183: 
 184:   /**
 185:    * Returns the paragraph {@link Element} that holds the specified position.
 186:    *
 187:    * @param pos the position for which to get the paragraph element
 188:    *
 189:    * @return the paragraph {@link Element} that holds the specified position
 190:    */
 191:   public abstract Element getParagraphElement(int pos);
 192: 
 193:   /**
 194:    * Returns the default root {@link Element} of this <code>Document</code>.
 195:    * Usual <code>Document</code>s only have one root element and return this.
 196:    * However, there may be <code>Document</code> implementations that
 197:    * support multiple root elements, they have to return a default root element
 198:    * here.
 199:    *
 200:    * @return the default root {@link Element} of this <code>Document</code>
 201:    */
 202:   public abstract Element getDefaultRootElement();
 203: 
 204:   /**
 205:    * Creates and returns a branch element with the specified
 206:    * <code>parent</code> and <code>attributes</code>. Note that the new
 207:    * <code>Element</code> is linked to the parent <code>Element</code>
 208:    * through {@link Element#getParentElement}, but it is not yet added
 209:    * to the parent <code>Element</code> as child.
 210:    *
 211:    * @param parent the parent <code>Element</code> for the new branch element
 212:    * @param attributes the text attributes to be installed in the new element
 213:    *
 214:    * @return the new branch <code>Element</code>
 215:    *
 216:    * @see BranchElement
 217:    */
 218:   protected Element createBranchElement(Element parent,
 219:                     AttributeSet attributes)
 220:   {
 221:     return new BranchElement(parent, attributes);
 222:   }
 223: 
 224:   /**
 225:    * Creates and returns a leaf element with the specified
 226:    * <code>parent</code> and <code>attributes</code>. Note that the new
 227:    * <code>Element</code> is linked to the parent <code>Element</code>
 228:    * through {@link Element#getParentElement}, but it is not yet added
 229:    * to the parent <code>Element</code> as child.
 230:    *
 231:    * @param parent the parent <code>Element</code> for the new branch element
 232:    * @param attributes the text attributes to be installed in the new element
 233:    *
 234:    * @return the new branch <code>Element</code>
 235:    *
 236:    * @see LeafElement
 237:    */
 238:   protected Element createLeafElement(Element parent, AttributeSet attributes,
 239:                       int start, int end)
 240:   {
 241:     return new LeafElement(parent, attributes, start, end);
 242:   }
 243: 
 244:   /**
 245:    * Creates a {@link Position} that keeps track of the location at the
 246:    * specified <code>offset</code>.
 247:    *
 248:    * @param offset the location in the document to keep track by the new
 249:    *        <code>Position</code>
 250:    *
 251:    * @return the newly created <code>Position</code>
 252:    *
 253:    * @throws BadLocationException if <code>offset</code> is not a valid
 254:    *         location in the documents content model
 255:    */
 256:   public Position createPosition(final int offset) throws BadLocationException
 257:   {
 258:     return content.createPosition(offset);
 259:   }
 260: 
 261:   /**
 262:    * Notifies all registered listeners when the document model changes.
 263:    *
 264:    * @param event the <code>DocumentEvent</code> to be fired
 265:    */
 266:   protected void fireChangedUpdate(DocumentEvent event)
 267:   {
 268:     DocumentListener[] listeners = getDocumentListeners();
 269: 
 270:     for (int index = 0; index < listeners.length; ++index)
 271:       listeners[index].changedUpdate(event);
 272:   }
 273: 
 274:   /**
 275:    * Notifies all registered listeners when content is inserted in the document
 276:    * model.
 277:    *
 278:    * @param event the <code>DocumentEvent</code> to be fired
 279:    */
 280:   protected void fireInsertUpdate(DocumentEvent event)
 281:   {
 282:     DocumentListener[] listeners = getDocumentListeners();
 283: 
 284:     for (int index = 0; index < listeners.length; ++index)
 285:       listeners[index].insertUpdate(event);
 286:   }
 287: 
 288:   /**
 289:    * Notifies all registered listeners when content is removed from the
 290:    * document model.
 291:    *
 292:    * @param event the <code>DocumentEvent</code> to be fired
 293:    */
 294:   protected void fireRemoveUpdate(DocumentEvent event)
 295:   {
 296:     DocumentListener[] listeners = getDocumentListeners();
 297: 
 298:     for (int index = 0; index < listeners.length; ++index)
 299:       listeners[index].removeUpdate(event);
 300:   }
 301: 
 302:   /**
 303:    * Notifies all registered listeners when an <code>UndoableEdit</code> has
 304:    * been performed on this <code>Document</code>.
 305:    *
 306:    * @param event the <code>UndoableEditEvent</code> to be fired
 307:    */
 308:   protected void fireUndoableEditUpdate(UndoableEditEvent event)
 309:   {
 310:     UndoableEditListener[] listeners = getUndoableEditListeners();
 311: 
 312:     for (int index = 0; index < listeners.length; ++index)
 313:       listeners[index].undoableEditHappened(event);
 314:   }
 315: 
 316:   /**
 317:    * Returns the asynchronous loading priority. Returns <code>-1</code> if this
 318:    * document should not be loaded asynchronously.
 319:    *
 320:    * @return the asynchronous loading priority
 321:    */
 322:   public int getAsynchronousLoadPriority()
 323:   {
 324:     return 0;
 325:   }
 326: 
 327:   /**
 328:    * Returns the {@link AttributeContext} used in this <code>Document</code>.
 329:    *
 330:    * @return the {@link AttributeContext} used in this <code>Document</code>
 331:    */
 332:   protected AttributeContext getAttributeContext()
 333:   {
 334:     return context;
 335:   }
 336: 
 337:   /**
 338:    * Returns the root element for bidirectional content.
 339:    *
 340:    * @return the root element for bidirectional content
 341:    */
 342:   public Element getBidiRootElement()
 343:   {
 344:     return null;
 345:   }
 346: 
 347:   /**
 348:    * Returns the {@link Content} model for this <code>Document</code>
 349:    *
 350:    * @return the {@link Content} model for this <code>Document</code>
 351:    *
 352:    * @see GapContent
 353:    * @see StringContent
 354:    */
 355:   protected final Content getContent()
 356:   {
 357:     return content;
 358:   }
 359: 
 360:   /**
 361:    * Returns the thread that currently modifies this <code>Document</code>
 362:    * if there is one, otherwise <code>null</code>. This can be used to
 363:    * distinguish between a method call that is part of an ongoing modification
 364:    * or if it is a separate modification for which a new lock must be aquired.
 365:    *
 366:    * @return the thread that currently modifies this <code>Document</code>
 367:    *         if there is one, otherwise <code>null</code>
 368:    */
 369:   protected Thread getCurrentWriter()
 370:   {
 371:     return currentWriter;
 372:   }
 373: 
 374:   /**
 375:    * Returns the properties of this <code>Document</code>.
 376:    *
 377:    * @return the properties of this <code>Document</code>
 378:    */
 379:   public Dictionary getDocumentProperties()
 380:   {
 381:     // FIXME: make me thread-safe
 382:     if (properties == null)
 383:       properties = new Hashtable();
 384: 
 385:     return properties;
 386:   }
 387: 
 388:   /**
 389:    * Returns a {@link Position} which will always mark the end of the
 390:    * <code>Document</code>.
 391:    *
 392:    * @return a {@link Position} which will always mark the end of the
 393:    *         <code>Document</code>
 394:    */
 395:   public Position getEndPosition()
 396:   {
 397:     // FIXME: Properly implement this by calling Content.createPosition().
 398:     return new Position() 
 399:       {        
 400:         public int getOffset() 
 401:         { 
 402:           return getLength(); 
 403:         } 
 404:       };
 405:   }
 406: 
 407:   /**
 408:    * Returns the length of this <code>Document</code>'s content.
 409:    *
 410:    * @return the length of this <code>Document</code>'s content
 411:    */
 412:   public int getLength()
 413:   {
 414:     // We return Content.getLength() -1 here because there is always an
 415:     // implicit \n at the end of the Content which does count in Content
 416:     // but not in Document.
 417:     return content.length() - 1;
 418:   }
 419: 
 420:   /**
 421:    * Returns all registered listeners of a given listener type.
 422:    *
 423:    * @param listenerType the type of the listeners to be queried
 424:    *
 425:    * @return all registered listeners of the specified type
 426:    */
 427:   public EventListener[] getListeners(Class listenerType)
 428:   {
 429:     return listenerList.getListeners(listenerType);
 430:   }
 431: 
 432:   /**
 433:    * Returns a property from this <code>Document</code>'s property list.
 434:    *
 435:    * @param key the key of the property to be fetched
 436:    *
 437:    * @return the property for <code>key</code> or <code>null</code> if there
 438:    *         is no such property stored
 439:    */
 440:   public Object getProperty(Object key)
 441:   {
 442:     // FIXME: make me thread-safe
 443:     Object value = null;
 444:     if (properties != null)
 445:       value = properties.get(key);
 446: 
 447:     return value;
 448:   }
 449: 
 450:   /**
 451:    * Returns all root elements of this <code>Document</code>. By default
 452:    * this just returns the single root element returned by
 453:    * {@link #getDefaultRootElement()}. <code>Document</code> implementations
 454:    * that support multiple roots must override this method and return all roots
 455:    * here.
 456:    *
 457:    * @return all root elements of this <code>Document</code>
 458:    */
 459:   public Element[] getRootElements()
 460:   {
 461:     Element[] elements = new Element[1];
 462:     elements[0] = getDefaultRootElement();
 463:     return elements;
 464:   }
 465: 
 466:   /**
 467:    * Returns a {@link Position} which will always mark the beginning of the
 468:    * <code>Document</code>.
 469:    *
 470:    * @return a {@link Position} which will always mark the beginning of the
 471:    *         <code>Document</code>
 472:    */
 473:   public Position getStartPosition()
 474:   {
 475:     // FIXME: Properly implement this using Content.createPosition().
 476:     return new Position() 
 477:       {        
 478:         public int getOffset() 
 479:         { 
 480:           return 0; 
 481:         } 
 482:       };
 483:   }
 484: 
 485:   /**
 486:    * Returns a piece of this <code>Document</code>'s content.
 487:    *
 488:    * @param offset the start offset of the content
 489:    * @param length the length of the content
 490:    *
 491:    * @return the piece of content specified by <code>offset</code> and
 492:    *         <code>length</code>
 493:    *
 494:    * @throws BadLocationException if <code>offset</code> or <code>offset +
 495:    *         length</code> are invalid locations with this
 496:    *         <code>Document</code>
 497:    */
 498:   public String getText(int offset, int length) throws BadLocationException
 499:   {
 500:     return content.getString(offset, length);
 501:   }
 502: 
 503:   /**
 504:    * Fetches a piece of this <code>Document</code>'s content and stores
 505:    * it in the given {@link Segment}.
 506:    *
 507:    * @param offset the start offset of the content
 508:    * @param length the length of the content
 509:    * @param segment the <code>Segment</code> to store the content in
 510:    *
 511:    * @throws BadLocationException if <code>offset</code> or <code>offset +
 512:    *         length</code> are invalid locations with this
 513:    *         <code>Document</code>
 514:    */
 515:   public void getText(int offset, int length, Segment segment)
 516:     throws BadLocationException
 517:   {
 518:     content.getChars(offset, length, segment);
 519:   }
 520: 
 521:   /**
 522:    * Inserts a String into this <code>Document</code> at the specified
 523:    * position and assigning the specified attributes to it.
 524:    *
 525:    * @param offset the location at which the string should be inserted
 526:    * @param text the content to be inserted
 527:    * @param attributes the text attributes to be assigned to that string
 528:    *
 529:    * @throws BadLocationException if <code>offset</code> is not a valid
 530:    *         location in this <code>Document</code>
 531:    */
 532:   public void insertString(int offset, String text, AttributeSet attributes)
 533:     throws BadLocationException
 534:   {
 535:     // Just return when no text to insert was given.
 536:     if (text == null || text.length() == 0)
 537:       return;
 538:     DefaultDocumentEvent event =
 539:       new DefaultDocumentEvent(offset, text.length(),
 540:                    DocumentEvent.EventType.INSERT);
 541:     
 542:     writeLock();
 543:     UndoableEdit undo = content.insertString(offset, text);
 544:     insertUpdate(event, attributes);
 545:     writeUnlock();
 546: 
 547:     fireInsertUpdate(event);
 548:     if (undo != null)
 549:       fireUndoableEditUpdate(new UndoableEditEvent(this, undo));
 550:   }
 551: 
 552:   /**
 553:    * Called to indicate that text has been inserted into this
 554:    * <code>Document</code>. The default implementation does nothing.
 555:    * This method is executed within a write lock.
 556:    *
 557:    * @param chng the <code>DefaultDocumentEvent</code> describing the change
 558:    * @param attr the attributes of the changed content
 559:    */
 560:   protected void insertUpdate(DefaultDocumentEvent chng, AttributeSet attr)
 561:   {
 562:     // Do nothing here. Subclasses may want to override this.
 563:   }
 564: 
 565:   /**
 566:    * Called after some content has been removed from this
 567:    * <code>Document</code>. The default implementation does nothing.
 568:    * This method is executed within a write lock.
 569:    *
 570:    * @param chng the <code>DefaultDocumentEvent</code> describing the change
 571:    */
 572:   protected void postRemoveUpdate(DefaultDocumentEvent chng)
 573:   {
 574:     // Do nothing here. Subclasses may want to override this.
 575:   }
 576: 
 577:   /**
 578:    * Stores a property in this <code>Document</code>'s property list.
 579:    *
 580:    * @param key the key of the property to be stored
 581:    * @param value the value of the property to be stored
 582:    */
 583:   public void putProperty(Object key, Object value)
 584:   {
 585:     // FIXME: make me thread-safe
 586:     if (properties == null)
 587:       properties = new Hashtable();
 588: 
 589:     properties.put(key, value);
 590:   }
 591: 
 592:   /**
 593:    * Blocks until a read lock can be obtained.  Must block if there is
 594:    * currently a writer modifying the <code>Document</code>.
 595:    */
 596:   public void readLock()
 597:   {
 598:     if (currentWriter != null && currentWriter.equals(Thread.currentThread()))
 599:       return;
 600:     synchronized (documentCV)
 601:       {
 602:         while (currentWriter != null || numWritersWaiting > 0)
 603:           {
 604:             try
 605:               {
 606:                 documentCV.wait();
 607:               }
 608:             catch (InterruptedException ie)
 609:               {
 610:                 throw new Error("interrupted trying to get a readLock");
 611:               }
 612:           }
 613:           numReaders++;
 614:       }
 615:   }
 616: 
 617:   /**
 618:    * Releases the read lock. If this was the only reader on this
 619:    * <code>Document</code>, writing may begin now.
 620:    */
 621:   public void readUnlock()
 622:   {
 623:     // Note we could have a problem here if readUnlock was called without a
 624:     // prior call to readLock but the specs simply warn users to ensure that
 625:     // balance by using a finally block:
 626:     // readLock()
 627:     // try
 628:     // { 
 629:     //   doSomethingHere 
 630:     // }
 631:     // finally
 632:     // {
 633:     //   readUnlock();
 634:     // }
 635:     
 636:     // All that the JDK seems to check for is that you don't call unlock
 637:     // more times than you've previously called lock, but it doesn't make
 638:     // sure that the threads calling unlock were the same ones that called lock
 639: 
 640:     // FIXME: the reference implementation throws a 
 641:     // javax.swing.text.StateInvariantError here
 642:     if (numReaders == 0)
 643:       throw new IllegalStateException("document lock failure");
 644:     
 645:     synchronized (documentCV)
 646:     {
 647:       // If currentWriter is not null, the application code probably had a 
 648:       // writeLock and then tried to obtain a readLock, in which case 
 649:       // numReaders wasn't incremented
 650:       if (currentWriter == null)
 651:         {
 652:           numReaders --;
 653:           if (numReaders == 0 && numWritersWaiting != 0)
 654:             documentCV.notify();
 655:         }
 656:     }
 657:   }
 658: 
 659:   /**
 660:    * Removes a piece of content from this <code>Document</code>.
 661:    *
 662:    * @param offset the start offset of the fragment to be removed
 663:    * @param length the length of the fragment to be removed
 664:    *
 665:    * @throws BadLocationException if <code>offset</code> or
 666:    *         <code>offset + length</code> or invalid locations within this
 667:    *         document
 668:    */
 669:   public void remove(int offset, int length) throws BadLocationException
 670:   {
 671:     DefaultDocumentEvent event =
 672:       new DefaultDocumentEvent(offset, length,
 673:                    DocumentEvent.EventType.REMOVE);
 674:     
 675:     removeUpdate(event);
 676: 
 677:     boolean shouldFire = content.getString(offset, length).length() != 0;
 678:     
 679:     writeLock();
 680:     UndoableEdit temp = content.remove(offset, length);
 681:     writeUnlock();
 682:     
 683:     postRemoveUpdate(event);
 684:     
 685:     if (shouldFire)
 686:       fireRemoveUpdate(event);
 687:   }
 688: 
 689:   /**
 690:    * Replaces a piece of content in this <code>Document</code> with
 691:    * another piece of content.
 692:    *
 693:    * @param offset the start offset of the fragment to be removed
 694:    * @param length the length of the fragment to be removed
 695:    * @param text the text to replace the content with
 696:    * @param attributes the text attributes to assign to the new content
 697:    *
 698:    * @throws BadLocationException if <code>offset</code> or
 699:    *         <code>offset + length</code> or invalid locations within this
 700:    *         document
 701:    *
 702:    * @since 1.4
 703:    */
 704:   public void replace(int offset, int length, String text,
 705:               AttributeSet attributes)
 706:     throws BadLocationException
 707:   {
 708:     remove(offset, length);
 709:     insertString(offset, text, attributes);
 710:   }
 711: 
 712:   /**
 713:    * Adds a <code>DocumentListener</code> object to this document.
 714:    *
 715:    * @param listener the listener to add
 716:    */
 717:   public void addDocumentListener(DocumentListener listener)
 718:   {
 719:     listenerList.add(DocumentListener.class, listener);
 720:   }
 721: 
 722:   /**
 723:    * Removes a <code>DocumentListener</code> object from this document.
 724:    *
 725:    * @param listener the listener to remove
 726:    */
 727:   public void removeDocumentListener(DocumentListener listener)
 728:   {
 729:     listenerList.remove(DocumentListener.class, listener);
 730:   }
 731: 
 732:   /**
 733:    * Returns all registered <code>DocumentListener</code>s.
 734:    *
 735:    * @return all registered <code>DocumentListener</code>s
 736:    */
 737:   public DocumentListener[] getDocumentListeners()
 738:   {
 739:     return (DocumentListener[]) getListeners(DocumentListener.class);
 740:   }
 741: 
 742:   /**
 743:    * Adds an {@link UndoableEditListener} to this <code>Document</code>.
 744:    *
 745:    * @param listener the listener to add
 746:    */
 747:   public void addUndoableEditListener(UndoableEditListener listener)
 748:   {
 749:     listenerList.add(UndoableEditListener.class, listener);
 750:   }
 751: 
 752:   /**
 753:    * Removes an {@link UndoableEditListener} from this <code>Document</code>.
 754:    *
 755:    * @param listener the listener to remove
 756:    */
 757:   public void removeUndoableEditListener(UndoableEditListener listener)
 758:   {
 759:     listenerList.remove(UndoableEditListener.class, listener);
 760:   }
 761: 
 762:   /**
 763:    * Returns all registered {@link UndoableEditListener}s.
 764:    *
 765:    * @return all registered {@link UndoableEditListener}s
 766:    */
 767:   public UndoableEditListener[] getUndoableEditListeners()
 768:   {
 769:     return (UndoableEditListener[]) getListeners(UndoableEditListener.class);
 770:   }
 771: 
 772:   /**
 773:    * Called before some content gets removed from this <code>Document</code>.
 774:    * The default implementation does nothing but may be overridden by
 775:    * subclasses to modify the <code>Document</code> structure in response
 776:    * to a remove request. The method is executed within a write lock.
 777:    *
 778:    * @param chng the <code>DefaultDocumentEvent</code> describing the change
 779:    */
 780:   protected void removeUpdate(DefaultDocumentEvent chng)
 781:   {
 782:     // Do nothing here. Subclasses may wish to override this.
 783:   }
 784: 
 785:   /**
 786:    * Called to render this <code>Document</code> visually. It obtains a read
 787:    * lock, ensuring that no changes will be made to the <code>document</code>
 788:    * during the rendering process. It then calls the {@link Runnable#run()}
 789:    * method on <code>runnable</code>. This method <em>must not</em> attempt
 790:    * to modifiy the <code>Document</code>, since a deadlock will occur if it
 791:    * tries to obtain a write lock. When the {@link Runnable#run()} method
 792:    * completes (either naturally or by throwing an exception), the read lock
 793:    * is released. Note that there is nothing in this method related to
 794:    * the actual rendering. It could be used to execute arbitrary code within
 795:    * a read lock.
 796:    *
 797:    * @param runnable the {@link Runnable} to execute
 798:    */
 799:   public void render(Runnable runnable)
 800:   {
 801:     readLock();
 802:     try
 803:     {
 804:       runnable.run();
 805:     }
 806:     finally
 807:     {
 808:       readUnlock();
 809:     }
 810:   }
 811: 
 812:   /**
 813:    * Sets the asynchronous loading priority for this <code>Document</code>.
 814:    * A value of <code>-1</code> indicates that this <code>Document</code>
 815:    * should be loaded synchronously.
 816:    *
 817:    * @param p the asynchronous loading priority to set
 818:    */
 819:   public void setAsynchronousLoadPriority(int p)
 820:   {
 821:     // TODO: Implement this properly.
 822:   }
 823: 
 824:   /**
 825:    * Sets the properties of this <code>Document</code>.
 826:    *
 827:    * @param p the document properties to set
 828:    */
 829:   public void setDocumentProperties(Dictionary p)
 830:   {
 831:     // FIXME: make me thread-safe
 832:     properties = p;
 833:   }
 834: 
 835:   /**
 836:    * Blocks until a write lock can be obtained.  Must wait if there are 
 837:    * readers currently reading or another thread is currently writing.
 838:    */
 839:   protected void writeLock()
 840:   {
 841:     if (currentWriter!= null && currentWriter.equals(Thread.currentThread()))
 842:       return;
 843:     synchronized (documentCV)
 844:       {
 845:         numWritersWaiting++;
 846:         while (numReaders > 0)
 847:           {
 848:             try
 849:               {
 850:                 documentCV.wait();
 851:               }
 852:             catch (InterruptedException ie)
 853:               {
 854:                 throw new Error("interruped while trying to obtain write lock");
 855:               }
 856:           }
 857:         numWritersWaiting --;
 858:         currentWriter = Thread.currentThread();
 859:       }
 860:   }
 861: 
 862:   /**
 863:    * Releases the write lock. This allows waiting readers or writers to
 864:    * obtain the lock.
 865:    */
 866:   protected void writeUnlock()
 867:   {
 868:     synchronized (documentCV)
 869:     {
 870:         if (Thread.currentThread().equals(currentWriter))
 871:           {
 872:             currentWriter = null;
 873:             documentCV.notifyAll();
 874:           }
 875:     }
 876:   }
 877: 
 878:   /**
 879:    * Returns the currently installed {@link DocumentFilter} for this
 880:    * <code>Document</code>.
 881:    *
 882:    * @return the currently installed {@link DocumentFilter} for this
 883:    *         <code>Document</code>
 884:    *
 885:    * @since 1.4
 886:    */
 887:   public DocumentFilter getDocumentFilter()
 888:   {
 889:     return documentFilter;
 890:   }
 891: 
 892:   /**
 893:    * Sets the {@link DocumentFilter} for this <code>Document</code>.
 894:    *
 895:    * @param filter the <code>DocumentFilter</code> to set
 896:    *
 897:    * @since 1.4
 898:    */
 899:   public void setDocumentFilter(DocumentFilter filter)
 900:   {
 901:     this.documentFilter = filter;
 902:   }
 903: 
 904:   /**
 905:    * Dumps diagnostic information to the specified <code>PrintStream</code>.
 906:    *
 907:    * @param out the stream to write the diagnostic information to
 908:    */
 909:   public void dump(PrintStream out)
 910:   {
 911:     ((AbstractElement) getDefaultRootElement()).dump(out, 0);
 912:   }
 913: 
 914:   /**
 915:    * Defines a set of methods for managing text attributes for one or more
 916:    * <code>Document</code>s.
 917:    *
 918:    * Replicating {@link AttributeSet}s throughout a <code>Document</code> can
 919:    * be very expensive. Implementations of this interface are intended to
 920:    * provide intelligent management of <code>AttributeSet</code>s, eliminating
 921:    * costly duplication.
 922:    *
 923:    * @see StyleContext
 924:    */
 925:   public interface AttributeContext
 926:   {
 927:     /**
 928:      * Returns an {@link AttributeSet} that contains the attributes
 929:      * of <code>old</code> plus the new attribute specified by
 930:      * <code>name</code> and <code>value</code>.
 931:      *
 932:      * @param old the attribute set to be merged with the new attribute
 933:      * @param name the name of the attribute to be added
 934:      * @param value the value of the attribute to be added
 935:      *
 936:      * @return the old attributes plus the new attribute
 937:      */
 938:     AttributeSet addAttribute(AttributeSet old, Object name, Object value);
 939: 
 940:     /**
 941:      * Returns an {@link AttributeSet} that contains the attributes
 942:      * of <code>old</code> plus the new attributes in <code>attributes</code>.
 943:      *
 944:      * @param old the set of attributes where to add the new attributes
 945:      * @param attributes the attributes to be added
 946:      *
 947:      * @return an {@link AttributeSet} that contains the attributes
 948:      *         of <code>old</code> plus the new attributes in
 949:      *         <code>attributes</code>
 950:      */
 951:     AttributeSet addAttributes(AttributeSet old, AttributeSet attributes);
 952: 
 953:     /**
 954:      * Returns an empty {@link AttributeSet}.
 955:      *
 956:      * @return  an empty {@link AttributeSet}
 957:      */
 958:     AttributeSet getEmptySet();
 959: 
 960:     /**
 961:      * Called to indicate that the attributes in <code>attributes</code> are
 962:      * no longer used.
 963:      *
 964:      * @param attributes the attributes are no longer used
 965:      */
 966:     void reclaim(AttributeSet attributes);
 967: 
 968:     /**
 969:      * Returns a {@link AttributeSet} that has the attribute with the specified
 970:      * <code>name</code> removed from <code>old</code>.
 971:      *
 972:      * @param old the attribute set from which an attribute is removed
 973:      * @param name the name of the attribute to be removed
 974:      *
 975:      * @return the attributes of <code>old</code> minus the attribute
 976:      *         specified by <code>name</code>
 977:      */
 978:     AttributeSet removeAttribute(AttributeSet old, Object name);
 979: 
 980:     /**
 981:      * Removes all attributes in <code>attributes</code> from <code>old</code>
 982:      * and returns the resulting <code>AttributeSet</code>.
 983:      *
 984:      * @param old the set of attributes from which to remove attributes
 985:      * @param attributes the attributes to be removed from <code>old</code>
 986:      *
 987:      * @return the attributes of <code>old</code> minus the attributes in
 988:      *         <code>attributes</code>
 989:      */
 990:     AttributeSet removeAttributes(AttributeSet old, AttributeSet attributes);
 991: 
 992:     /**
 993:      * Removes all attributes specified by <code>names</code> from
 994:      * <code>old</code> and returns the resulting <code>AttributeSet</code>.
 995:      *
 996:      * @param old the set of attributes from which to remove attributes
 997:      * @param names the names of the attributes to be removed from
 998:      *        <code>old</code>
 999:      *
1000:      * @return the attributes of <code>old</code> minus the attributes in
1001:      *         <code>attributes</code>
1002:      */
1003:     AttributeSet removeAttributes(AttributeSet old, Enumeration names);
1004:   }
1005: 
1006:   /**
1007:    * A sequence of data that can be edited. This is were the actual content
1008:    * in <code>AbstractDocument</code>'s is stored.
1009:    */
1010:   public interface Content
1011:   {
1012:     /**
1013:      * Creates a {@link Position} that keeps track of the location at
1014:      * <code>offset</code>.
1015:      *
1016:      * @return a {@link Position} that keeps track of the location at
1017:      *         <code>offset</code>.
1018:      *
1019:      * @throw BadLocationException if <code>offset</code> is not a valid
1020:      *        location in this <code>Content</code> model
1021:      */
1022:     Position createPosition(int offset) throws BadLocationException;
1023: 
1024:     /**
1025:      * Returns the length of the content.
1026:      *
1027:      * @return the length of the content
1028:      */
1029:     int length();
1030: 
1031:     /**
1032:      * Inserts a string into the content model.
1033:      *
1034:      * @param where the offset at which to insert the string
1035:      * @param str the string to be inserted
1036:      *
1037:      * @return an <code>UndoableEdit</code> or <code>null</code> if undo is
1038:      *         not supported by this <code>Content</code> model
1039:      *
1040:      * @throws BadLocationException if <code>where</code> is not a valid
1041:      *         location in this <code>Content</code> model
1042:      */
1043:     UndoableEdit insertString(int where, String str)
1044:       throws BadLocationException;
1045: 
1046:     /**
1047:      * Removes a piece of content from the content model.
1048:      *
1049:      * @param where the offset at which to remove content
1050:      * @param nitems the number of characters to be removed
1051:      *
1052:      * @return an <code>UndoableEdit</code> or <code>null</code> if undo is
1053:      *         not supported by this <code>Content</code> model
1054:      *
1055:      * @throws BadLocationException if <code>where</code> is not a valid
1056:      *         location in this <code>Content</code> model
1057:      */
1058:     UndoableEdit remove(int where, int nitems) throws BadLocationException;
1059: 
1060:     /**
1061:      * Returns a piece of content.
1062:      *
1063:      * @param where the start offset of the requested fragment
1064:      * @param len the length of the requested fragment
1065:      *
1066:      * @return the requested fragment
1067:      * @throws BadLocationException if <code>offset</code> or
1068:      *         <code>offset + len</code>is not a valid
1069:      *         location in this <code>Content</code> model
1070:      */
1071:     String getString(int where, int len) throws BadLocationException;
1072: 
1073:     /**
1074:      * Fetches a piece of content and stores it in <code>txt</code>.
1075:      *
1076:      * @param where the start offset of the requested fragment
1077:      * @param len the length of the requested fragment
1078:      * @param txt the <code>Segment</code> where to fragment is stored into
1079:      *
1080:      * @throws BadLocationException if <code>offset</code> or
1081:      *         <code>offset + len</code>is not a valid
1082:      *         location in this <code>Content</code> model
1083:      */
1084:     void getChars(int where, int len, Segment txt) throws BadLocationException;
1085:   }
1086: 
1087:   /**
1088:    * An abstract base implementation of the {@link Element} interface.
1089:    */
1090:   public abstract class AbstractElement
1091:     implements Element, MutableAttributeSet, TreeNode, Serializable
1092:   {
1093:     /** The serialization UID (compatible with JDK1.5). */
1094:     private static final long serialVersionUID = 1712240033321461704L;
1095: 
1096:     /** The number of characters that this Element spans. */
1097:     int count;
1098: 
1099:     /** The starting offset of this Element. */
1100:     int offset;
1101: 
1102:     /** The attributes of this Element. */
1103:     AttributeSet attributes;
1104: 
1105:     /** The parent element. */
1106:     Element element_parent;
1107: 
1108:     /** The parent in the TreeNode interface. */
1109:     TreeNode tree_parent;
1110: 
1111:     /** The children of this element. */
1112:     Vector tree_children;
1113: 
1114:     /**
1115:      * Creates a new instance of <code>AbstractElement</code> with a
1116:      * specified parent <code>Element</code> and <code>AttributeSet</code>.
1117:      *
1118:      * @param p the parent of this <code>AbstractElement</code>
1119:      * @param s the attributes to be assigned to this
1120:      *        <code>AbstractElement</code>
1121:      */
1122:     public AbstractElement(Element p, AttributeSet s)
1123:     {
1124:       element_parent = p;
1125:       AttributeContext ctx = getAttributeContext();
1126:       attributes = ctx.getEmptySet();
1127:       if (s != null)
1128:         attributes = ctx.addAttributes(attributes, s);
1129:     }
1130: 
1131:     /**
1132:      * Returns the child nodes of this <code>Element</code> as an
1133:      * <code>Enumeration</code> of {@link TreeNode}s.
1134:      *
1135:      * @return the child nodes of this <code>Element</code> as an
1136:      *         <code>Enumeration</code> of {@link TreeNode}s
1137:      */
1138:     public abstract Enumeration children();
1139: 
1140:     /**
1141:      * Returns <code>true</code> if this <code>AbstractElement</code>
1142:      * allows children.
1143:      *
1144:      * @return <code>true</code> if this <code>AbstractElement</code>
1145:      *         allows children
1146:      */
1147:     public abstract boolean getAllowsChildren();
1148: 
1149:     /**
1150:      * Returns the child of this <code>AbstractElement</code> at
1151:      * <code>index</code>.
1152:      *
1153:      * @param index the position in the child list of the child element to
1154:      *        be returned
1155:      *
1156:      * @return the child of this <code>AbstractElement</code> at
1157:      *         <code>index</code>
1158:      */
1159:     public TreeNode getChildAt(int index)
1160:     {
1161:       return (TreeNode) tree_children.get(index);
1162:     }
1163: 
1164:     /**
1165:      * Returns the number of children of this <code>AbstractElement</code>.
1166:      *
1167:      * @return the number of children of this <code>AbstractElement</code>
1168:      */
1169:     public int getChildCount()
1170:     {
1171:       return tree_children.size();
1172:     }
1173: 
1174:     /**
1175:      * Returns the index of a given child <code>TreeNode</code> or
1176:      * <code>-1</code> if <code>node</code> is not a child of this
1177:      * <code>AbstractElement</code>.
1178:      *
1179:      * @param node the node for which the index is requested
1180:      *
1181:      * @return the index of a given child <code>TreeNode</code> or
1182:      *         <code>-1</code> if <code>node</code> is not a child of this
1183:      *         <code>AbstractElement</code>
1184:      */
1185:     public int getIndex(TreeNode node)
1186:     {
1187:       return tree_children.indexOf(node);
1188:     }
1189: 
1190:     /**
1191:      * Returns the parent <code>TreeNode</code> of this
1192:      * <code>AbstractElement</code> or <code>null</code> if this element
1193:      * has no parent.
1194:      *
1195:      * @return the parent <code>TreeNode</code> of this
1196:      *         <code>AbstractElement</code> or <code>null</code> if this
1197:      *         element has no parent
1198:      */
1199:     public TreeNode getParent()
1200:     {
1201:       return tree_parent;
1202:     }
1203: 
1204:     /**
1205:      * Returns <code>true</code> if this <code>AbstractElement</code> is a
1206:      * leaf element, <code>false</code> otherwise.
1207:      *
1208:      * @return <code>true</code> if this <code>AbstractElement</code> is a
1209:      *         leaf element, <code>false</code> otherwise
1210:      */
1211:     public abstract boolean isLeaf();
1212: 
1213:     /**
1214:      * Adds an attribute to this element.
1215:      *
1216:      * @param name the name of the attribute to be added
1217:      * @param value the value of the attribute to be added
1218:      */
1219:     public void addAttribute(Object name, Object value)
1220:     {
1221:       attributes = getAttributeContext().addAttribute(attributes, name, value);
1222:     }
1223: 
1224:     /**
1225:      * Adds a set of attributes to this element.
1226:      *
1227:      * @param attrs the attributes to be added to this element
1228:      */
1229:     public void addAttributes(AttributeSet attrs)
1230:     {
1231:       attributes = getAttributeContext().addAttributes(attributes, attrs);
1232:     }
1233: 
1234:     /**
1235:      * Removes an attribute from this element.
1236:      *
1237:      * @param name the name of the attribute to be removed
1238:      */
1239:     public void removeAttribute(Object name)
1240:     {
1241:       attributes = getAttributeContext().removeAttribute(attributes, name);
1242:     }
1243: 
1244:     /**
1245:      * Removes a set of attributes from this element.
1246:      *
1247:      * @param attrs the attributes to be removed
1248:      */
1249:     public void removeAttributes(AttributeSet attrs)
1250:     {
1251:       attributes = getAttributeContext().removeAttributes(attributes, attrs);
1252:     }
1253: 
1254:     /**
1255:      * Removes a set of attribute from this element.
1256:      *
1257:      * @param names the names of the attributes to be removed
1258:      */
1259:     public void removeAttributes(Enumeration names)
1260:     {
1261:       attributes = getAttributeContext().removeAttributes(attributes, names);
1262:     }
1263: 
1264:     /**
1265:      * Sets the parent attribute set against which the element can resolve
1266:      * attributes that are not defined in itself.
1267:      *
1268:      * @param parent the resolve parent to set
1269:      */
1270:     public void setResolveParent(AttributeSet parent)
1271:     {
1272:       attributes = getAttributeContext().addAttribute(attributes,
1273:                                                       ResolveAttribute,
1274:                                                       parent);
1275:     }
1276: 
1277:     /**
1278:      * Returns <code>true</code> if this element contains the specified
1279:      * attribute.
1280:      *
1281:      * @param name the name of the attribute to check
1282:      * @param value the value of the attribute to check
1283:      *
1284:      * @return <code>true</code> if this element contains the specified
1285:      *         attribute
1286:      */
1287:     public boolean containsAttribute(Object name, Object value)
1288:     {
1289:       return attributes.containsAttribute(name, value);
1290:     }
1291: 
1292:     /**
1293:      * Returns <code>true</code> if this element contains all of the
1294:      * specified attributes.
1295:      *
1296:      * @param attrs the attributes to check
1297:      *
1298:      * @return <code>true</code> if this element contains all of the
1299:      *         specified attributes
1300:      */
1301:     public boolean containsAttributes(AttributeSet attrs)
1302:     {
1303:       return attributes.containsAttributes(attrs);
1304:     }
1305: 
1306:     /**
1307:      * Returns a copy of the attributes of this element.
1308:      *
1309:      * @return a copy of the attributes of this element
1310:      */
1311:     public AttributeSet copyAttributes()
1312:     {
1313:       return attributes.copyAttributes();
1314:     }
1315: 
1316:     /**
1317:      * Returns the attribute value with the specified key. If this attribute
1318:      * is not defined in this element and this element has a resolving
1319:      * parent, the search goes upward to the resolve parent chain.
1320:      *
1321:      * @param key the key of the requested attribute
1322:      *
1323:      * @return the attribute value for <code>key</code> of <code>null</code>
1324:      *         if <code>key</code> is not found locally and cannot be resolved
1325:      *         in this element's resolve parents
1326:      */
1327:     public Object getAttribute(Object key)
1328:     {
1329:       return attributes.getAttribute(key);
1330:     }
1331: 
1332:     /**
1333:      * Returns the number of defined attributes in this element.
1334:      *
1335:      * @return the number of defined attributes in this element
1336:      */
1337:     public int getAttributeCount()
1338:     {
1339:       return attributes.getAttributeCount();
1340:     }
1341: 
1342:     /**
1343:      * Returns the names of the attributes of this element.
1344:      *
1345:      * @return the names of the attributes of this element
1346:      */
1347:     public Enumeration getAttributeNames()
1348:     {
1349:       return attributes.getAttributeNames();
1350:     }
1351: 
1352:     /**
1353:      * Returns the resolve parent of this element.
1354:      * This is taken from the AttributeSet, but if this is null,
1355:      * this method instead returns the Element's parent's 
1356:      * AttributeSet
1357:      *
1358:      * @return the resolve parent of this element
1359:      *
1360:      * @see #setResolveParent(AttributeSet)
1361:      */
1362:     public AttributeSet getResolveParent()
1363:     {
1364:       if (attributes.getResolveParent() != null)
1365:         return attributes.getResolveParent();
1366:       return element_parent.getAttributes();
1367:     }
1368: 
1369:     /**
1370:      * Returns <code>true</code> if an attribute with the specified name
1371:      * is defined in this element, <code>false</code> otherwise.
1372:      *
1373:      * @param attrName the name of the requested attributes
1374:      *
1375:      * @return <code>true</code> if an attribute with the specified name
1376:      *         is defined in this element, <code>false</code> otherwise
1377:      */
1378:     public boolean isDefined(Object attrName)
1379:     {
1380:       return attributes.isDefined(attrName);
1381:     }
1382: 
1383:     /**
1384:      * Returns <code>true</code> if the specified <code>AttributeSet</code>
1385:      * is equal to this element's <code>AttributeSet</code>, <code>false</code>
1386:      * otherwise.
1387:      *
1388:      * @param attrs the attributes to compare this element to
1389:      *
1390:      * @return <code>true</code> if the specified <code>AttributeSet</code>
1391:      *         is equal to this element's <code>AttributeSet</code>,
1392:      *         <code>false</code> otherwise
1393:      */
1394:     public boolean isEqual(AttributeSet attrs) 
1395:     {
1396:       return attributes.isEqual(attrs);
1397:     }
1398: 
1399:     /**
1400:      * Returns the attributes of this element.
1401:      *
1402:      * @return the attributes of this element
1403:      */
1404:     public AttributeSet getAttributes()
1405:     {
1406:       return this;
1407:     }
1408: 
1409:     /**
1410:      * Returns the {@link Document} to which this element belongs.
1411:      *
1412:      * @return the {@link Document} to which this element belongs
1413:      */
1414:     public Document getDocument()
1415:     {
1416:       return AbstractDocument.this;
1417:     }
1418: 
1419:     /**
1420:      * Returns the child element at the specified <code>index</code>.
1421:      *
1422:      * @param index the index of the requested child element
1423:      *
1424:      * @return the requested element
1425:      */
1426:     public abstract Element getElement(int index);
1427: 
1428:     /**
1429:      * Returns the name of this element.
1430:      *
1431:      * @return the name of this element
1432:      */
1433:     public String getName()
1434:     {
1435:       return (String) getAttribute(NameAttribute);
1436:     }
1437: 
1438:     /**
1439:      * Returns the parent element of this element.
1440:      *
1441:      * @return the parent element of this element
1442:      */
1443:     public Element getParentElement()
1444:     {
1445:       return element_parent;
1446:     }
1447: 
1448:     /**
1449:      * Returns the offset inside the document model that is after the last
1450:      * character of this element.
1451:      *
1452:      * @return the offset inside the document model that is after the last
1453:      *         character of this element
1454:      */
1455:     public abstract int getEndOffset();
1456: 
1457:     /**
1458:      * Returns the number of child elements of this element.
1459:      *
1460:      * @return the number of child elements of this element
1461:      */
1462:     public abstract int getElementCount();
1463: 
1464:     /**
1465:      * Returns the index of the child element that spans the specified
1466:      * offset in the document model.
1467:      *
1468:      * @param offset the offset for which the responsible element is searched
1469:      *
1470:      * @return the index of the child element that spans the specified
1471:      *         offset in the document model
1472:      */
1473:     public abstract int getElementIndex(int offset);
1474: 
1475:     /**
1476:      * Returns the start offset if this element inside the document model.
1477:      *
1478:      * @return the start offset if this element inside the document model
1479:      */
1480:     public abstract int getStartOffset();
1481: 
1482:     /**
1483:      * Prints diagnostic output to the specified stream.
1484:      *
1485:      * @param stream the stream to write to
1486:      * @param indent the indentation level
1487:      */
1488:     public void dump(PrintStream stream, int indent)
1489:     {
1490:       StringBuffer b = new StringBuffer();
1491:       for (int i = 0; i < indent; ++i)
1492:         b.append(' ');
1493:       b.append('<');
1494:       b.append(getName());
1495:       // Dump attributes if there are any.
1496:       if (getAttributeCount() > 0)
1497:         {
1498:           b.append('\n');
1499:           Enumeration attNames = getAttributeNames();
1500:           while (attNames.hasMoreElements())
1501:             {
1502:               for (int i = 0; i < indent + 2; ++i)
1503:                 b.append(' ');
1504:               Object attName = attNames.nextElement();
1505:               b.append(attName);
1506:               b.append('=');
1507:               Object attribute = getAttribute(attName);
1508:               b.append(attribute);
1509:               b.append('\n');
1510:             }
1511:         }
1512:       b.append(">\n");
1513: 
1514:       // Dump element content for leaf elements.
1515:       if (isLeaf())
1516:         {
1517:           for (int i = 0; i < indent + 2; ++i)
1518:             b.append(' ');
1519:           int start = getStartOffset();
1520:           int end = getEndOffset();
1521:           b.append('[');
1522:           b.append(start);
1523:           b.append(',');
1524:           b.append(end);
1525:           b.append("][");
1526:           try
1527:             {
1528:               b.append(getDocument().getText(start, end - start));
1529:             }
1530:           catch (BadLocationException ex)
1531:             {
1532:               AssertionError err = new AssertionError("BadLocationException "
1533:                                                       + "must not be thrown "
1534:                                                       + "here.");
1535:               err.initCause(ex);
1536:           throw err;
1537:             }
1538:           b.append("]\n");
1539:         }
1540:       stream.print(b.toString());
1541: 
1542:       // Dump child elements if any.
1543:       int count = getElementCount();
1544:       for (int i = 0; i < count; ++i)
1545:         {
1546:           Element el = getElement(i);
1547:           if (el instanceof AbstractElement)
1548:             ((AbstractElement) el).dump(stream, indent + 2);
1549:         }
1550:     }
1551:   }
1552: 
1553:   /**
1554:    * An implementation of {@link Element} to represent composite
1555:    * <code>Element</code>s that contain other <code>Element</code>s.
1556:    */
1557:   public class BranchElement extends AbstractElement
1558:   {
1559:     /** The serialization UID (compatible with JDK1.5). */
1560:     private static final long serialVersionUID = -6037216547466333183L;
1561: 
1562:     /** The child elements of this BranchElement. */
1563:     private Element[] children = new Element[0];
1564: 
1565:     /**
1566:      * Creates a new <code>BranchElement</code> with the specified
1567:      * parent and attributes.
1568:      *
1569:      * @param parent the parent element of this <code>BranchElement</code>
1570:      * @param attributes the attributes to set on this
1571:      *        <code>BranchElement</code>
1572:      */
1573:     public BranchElement(Element parent, AttributeSet attributes)
1574:     {
1575:       super(parent, attributes);
1576:     }
1577: 
1578:     /**
1579:      * Returns the children of this <code>BranchElement</code>.
1580:      *
1581:      * @return the children of this <code>BranchElement</code>
1582:      */
1583:     public Enumeration children()
1584:     {
1585:       if (children.length == 0)
1586:         return null;
1587: 
1588:       Vector tmp = new Vector();
1589: 
1590:       for (int index = 0; index < children.length; ++index)
1591:     tmp.add(children[index]);
1592:       
1593:       return tmp.elements();
1594:     }
1595: 
1596:     /**
1597:      * Returns <code>true</code> since <code>BranchElements</code> allow
1598:      * child elements.
1599:      *
1600:      * @return <code>true</code> since <code>BranchElements</code> allow
1601:      *         child elements
1602:      */
1603:     public boolean getAllowsChildren()
1604:     {
1605:       return true;
1606:     }
1607: 
1608:     /**
1609:      * Returns the child element at the specified <code>index</code>.
1610:      *
1611:      * @param index the index of the requested child element
1612:      *
1613:      * @return the requested element
1614:      */
1615:     public Element getElement(int index)
1616:     {
1617:       if (index < 0 || index >= children.length)
1618:     return null;
1619: 
1620:       return children[index];
1621:     }
1622: 
1623:     /**
1624:      * Returns the number of child elements of this element.
1625:      *
1626:      * @return the number of child elements of this element
1627:      */
1628:     public int getElementCount()
1629:     {
1630:       return children.length;
1631:     }
1632: 
1633:     /**
1634:      * Returns the index of the child element that spans the specified
1635:      * offset in the document model.
1636:      *
1637:      * @param offset the offset for which the responsible element is searched
1638:      *
1639:      * @return the index of the child element that spans the specified
1640:      *         offset in the document model
1641:      */
1642:     public int getElementIndex(int offset)
1643:     {
1644:       // If offset is less than the start offset of our first child,
1645:       // return 0
1646:       if (offset < getStartOffset())
1647:         return 0;
1648:       
1649:       // XXX: There is surely a better algorithm
1650:       // as beginning from first element each time.
1651:       for (int index = 0; index < children.length - 1; ++index)
1652:         {
1653:           Element elem = children[index];
1654: 
1655:           if ((elem.getStartOffset() <= offset)
1656:                && (offset < elem.getEndOffset()))
1657:             return index;
1658:           // If the next element's start offset is greater than offset
1659:           // then we have to return the closest Element, since no Elements
1660:           // will contain the offset
1661:           if (children[index + 1].getStartOffset() > offset)
1662:             {
1663:               if ((offset - elem.getEndOffset()) > (children[index + 1].getStartOffset() - offset))
1664:                 return index + 1;
1665:               else
1666:                 return index;
1667:             }
1668:         }
1669: 
1670:       // If offset is greater than the index of the last element, return
1671:       // the index of the last element.
1672:       return getElementCount() - 1;
1673:     }
1674: 
1675:     /**
1676:      * Returns the offset inside the document model that is after the last
1677:      * character of this element.
1678:      * This is the end offset of the last child element. If this element
1679:      * has no children, this method throws a <code>NullPointerException</code>.
1680:      *
1681:      * @return the offset inside the document model that is after the last
1682:      *         character of this element
1683:      *
1684:      * @throws NullPointerException if this branch element has no children
1685:      */
1686:     public int getEndOffset()
1687:     {
1688:       if (getElementCount() == 0)
1689:         throw new NullPointerException("This BranchElement has no children.");
1690:       return children[children.length - 1].getEndOffset();
1691:     }
1692: 
1693:     /**
1694:      * Returns the name of this element. This is {@link #ParagraphElementName}
1695:      * in this case.
1696:      *
1697:      * @return the name of this element
1698:      */
1699:     public String getName()
1700:     {
1701:       return ParagraphElementName;
1702:     }
1703: 
1704:     /**
1705:      * Returns the start offset of this element inside the document model.
1706:      * This is the start offset of the first child element. If this element
1707:      * has no children, this method throws a <code>NullPointerException</code>.
1708:      *
1709:      * @return the start offset of this element inside the document model
1710:      *
1711:      * @throws NullPointerException if this branch element has no children
1712:      */
1713:     public int getStartOffset()
1714:     {
1715:       if (getElementCount() == 0)
1716:         throw new NullPointerException("This BranchElement has no children.");
1717:       return children[0].getStartOffset();
1718:     }
1719: 
1720:     /**
1721:      * Returns <code>false</code> since <code>BranchElement</code> are no
1722:      * leafes.
1723:      *
1724:      * @return <code>false</code> since <code>BranchElement</code> are no
1725:      *         leafes
1726:      */
1727:     public boolean isLeaf()
1728:     {
1729:       return false;
1730:     }
1731: 
1732:     /**
1733:      * Returns the <code>Element</code> at the specified <code>Document</code>
1734:      * offset.
1735:      *
1736:      * @return the <code>Element</code> at the specified <code>Document</code>
1737:      *         offset
1738:      *
1739:      * @see #getElementIndex(int)
1740:      */
1741:     public Element positionToElement(int position)
1742:     {
1743:       // XXX: There is surely a better algorithm
1744:       // as beginning from first element each time.
1745:       for (int index = 0; index < children.length; ++index)
1746:         {
1747:       Element elem = children[index];
1748: 
1749:       if ((elem.getStartOffset() <= position)
1750:           && (position < elem.getEndOffset()))
1751:         return elem;
1752:         }
1753: 
1754:       return null;
1755:     }
1756: 
1757:     /**
1758:      * Replaces a set of child elements with a new set of child elemens.
1759:      *
1760:      * @param offset the start index of the elements to be removed
1761:      * @param length the number of elements to be removed
1762:      * @param elements the new elements to be inserted
1763:      */
1764:     public void replace(int offset, int length, Element[] elements)
1765:     {
1766:       Element[] target = new Element[children.length - length
1767:                      + elements.length];
1768:       System.arraycopy(children, 0, target, 0, offset);
1769:       System.arraycopy(elements, 0, target, offset, elements.length);
1770:       System.arraycopy(children, offset + length, target,
1771:                offset + elements.length,
1772:                children.length - offset - length);
1773:       children = target;
1774:     }
1775: 
1776:     /**
1777:      * Returns a string representation of this element.
1778:      *
1779:      * @return a string representation of this element
1780:      */
1781:     public String toString()
1782:     {
1783:       return ("BranchElement(" + getName() + ") "
1784:           + getStartOffset() + "," + getEndOffset() + "\n");
1785:     }
1786:   }
1787: 
1788:   /**
1789:    * Stores the changes when a <code>Document</code> is beeing modified.
1790:    */
1791:   public class DefaultDocumentEvent extends CompoundEdit
1792:     implements DocumentEvent
1793:   {
1794:     /** The serialization UID (compatible with JDK1.5). */
1795:     private static final long serialVersionUID = 5230037221564563284L;
1796: 
1797:     /** The starting offset of the change. */
1798:     private int offset;
1799: 
1800:     /** The length of the change. */
1801:     private int length;
1802: 
1803:     /** The type of change. */
1804:     private DocumentEvent.EventType type;
1805: 
1806:     /**
1807:      * Maps <code>Element</code> to their change records.
1808:      */
1809:     Hashtable changes;
1810: 
1811:     /**
1812:      * Creates a new <code>DefaultDocumentEvent</code>.
1813:      *
1814:      * @param offset the starting offset of the change
1815:      * @param length the length of the change
1816:      * @param type the type of change
1817:      */
1818:     public DefaultDocumentEvent(int offset, int length,
1819:                 DocumentEvent.EventType type)
1820:     {
1821:       this.offset = offset;
1822:       this.length = length;
1823:       this.type = type;
1824:       changes = new Hashtable();
1825:     }
1826: 
1827:     /**
1828:      * Adds an UndoableEdit to this <code>DocumentEvent</code>. If this
1829:      * edit is an instance of {@link ElementEdit}, then this record can
1830:      * later be fetched by calling {@link #getChange}.
1831:      *
1832:      * @param edit the undoable edit to add
1833:      */
1834:     public boolean addEdit(UndoableEdit edit)
1835:     {
1836:       // XXX - Fully qualify ElementChange to work around gcj bug #2499.
1837:       if (edit instanceof DocumentEvent.ElementChange)
1838:         {
1839:           DocumentEvent.ElementChange elEdit =
1840:             (DocumentEvent.ElementChange) edit;
1841:           changes.put(elEdit.getElement(), elEdit);
1842:         }
1843:       return super.addEdit(edit);
1844:     }
1845: 
1846:     /**
1847:      * Returns the document that has been modified.
1848:      *
1849:      * @return the document that has been modified
1850:      */
1851:     public Document getDocument()
1852:     {
1853:       return AbstractDocument.this;
1854:     }
1855: 
1856:     /**
1857:      * Returns the length of the modification.
1858:      *
1859:      * @return the length of the modification
1860:      */
1861:     public int getLength()
1862:     {
1863:       return length;
1864:     }
1865: 
1866:     /**
1867:      * Returns the start offset of the modification.
1868:      *
1869:      * @return the start offset of the modification
1870:      */
1871:     public int getOffset()
1872:     {
1873:       return offset;
1874:     }
1875: 
1876:     /**
1877:      * Returns the type of the modification.
1878:      *
1879:      * @return the type of the modification
1880:      */
1881:     public DocumentEvent.EventType getType()
1882:     {
1883:       return type;
1884:     }
1885: 
1886:     /**
1887:      * Returns the changes for an element.
1888:      *
1889:      * @param elem the element for which the changes are requested
1890:      *
1891:      * @return the changes for <code>elem</code> or <code>null</code> if
1892:      *         <code>elem</code> has not been changed
1893:      */
1894:     public DocumentEvent.ElementChange getChange(Element elem)
1895:     {
1896:       // XXX - Fully qualify ElementChange to work around gcj bug #2499.
1897:       return (DocumentEvent.ElementChange) changes.get(elem);
1898:     }
1899:   }
1900:   
1901:   /**
1902:    * An implementation of {@link DocumentEvent.ElementChange} to be added
1903:    * to {@link DefaultDocumentEvent}s.
1904:    */
1905:   public static class ElementEdit extends AbstractUndoableEdit
1906:     implements DocumentEvent.ElementChange
1907:   {
1908:     /** The serial version UID of ElementEdit. */
1909:     private static final long serialVersionUID = -1216620962142928304L;
1910: 
1911:     /**
1912:      * The changed element.
1913:      */
1914:     private Element elem;
1915: 
1916:     /**
1917:      * The index of the change.
1918:      */
1919:     private int index;
1920: 
1921:     /**
1922:      * The removed elements.
1923:      */
1924:     private Element[] removed;
1925: 
1926:     /**
1927:      * The added elements.
1928:      */
1929:     private Element[] added;
1930:     
1931:     /**
1932:      * Creates a new <code>ElementEdit</code>.
1933:      *
1934:      * @param elem the changed element
1935:      * @param index the index of the change
1936:      * @param removed the removed elements
1937:      * @param added the added elements
1938:      */
1939:     public ElementEdit(Element elem, int index,
1940:                Element[] removed, Element[] added)
1941:     {
1942:       this.elem = elem;
1943:       this.index = index;
1944:       this.removed = removed;
1945:       this.added = added;
1946:     }
1947: 
1948:     /**
1949:      * Returns the added elements.
1950:      *
1951:      * @return the added elements
1952:      */
1953:     public Element[] getChildrenAdded()
1954:     {
1955:       return added;
1956:     }
1957: 
1958:     /**
1959:      * Returns the removed elements.
1960:      *
1961:      * @return the removed elements
1962:      */
1963:     public Element[] getChildrenRemoved()
1964:     {
1965:       return removed;
1966:     }
1967: 
1968:     /**
1969:      * Returns the changed element.
1970:      *
1971:      * @return the changed element
1972:      */
1973:     public Element getElement()
1974:     {
1975:       return elem;
1976:     }
1977: 
1978:     /**
1979:      * Returns the index of the change.
1980:      *
1981:      * @return the index of the change
1982:      */
1983:     public int getIndex()
1984:     {
1985:       return index;
1986:     }
1987:   }
1988: 
1989:   /**
1990:    * An implementation of {@link Element} that represents a leaf in the
1991:    * document structure. This is used to actually store content.
1992:    */
1993:   public class LeafElement extends AbstractElement
1994:   {
1995:     /** The serialization UID (compatible with JDK1.5). */
1996:     private static final long serialVersionUID = -8906306331347768017L;
1997: 
1998:     /** Manages the start offset of this element. */
1999:     Position startPos;
2000: 
2001:     /** Manages the end offset of this element. */
2002:     Position endPos;
2003: 
2004:     /**
2005:      * Creates a new <code>LeafElement</code>.
2006:      *
2007:      * @param parent the parent of this <code>LeafElement</code>
2008:      * @param attributes the attributes to be set
2009:      * @param start the start index of this element inside the document model
2010:      * @param end the end index of this element inside the document model
2011:      */
2012:     public LeafElement(Element parent, AttributeSet attributes, int start,
2013:                        int end)
2014:     {
2015:       super(parent, attributes);
2016:     {
2017:       try
2018:         {
2019:           if (parent != null)
2020:         {
2021:           startPos = parent.getDocument().createPosition(start);
2022:           endPos = parent.getDocument().createPosition(end);
2023:         }
2024:           else
2025:         {
2026:           startPos = createPosition(start);
2027:           endPos = createPosition(end);
2028:         }
2029:         }
2030:       catch (BadLocationException ex)
2031:         {
2032:           AssertionError as;
2033:           as = new AssertionError("BadLocationException thrown "
2034:                       + "here. start=" + start
2035:                       + ", end=" + end
2036:                       + ", length=" + getLength());
2037:           as.initCause(ex);
2038:           throw as;
2039:         }
2040:     }
2041:     }
2042: 
2043:     /**
2044:      * Returns <code>null</code> since <code>LeafElement</code>s cannot have
2045:      * children.
2046:      *
2047:      * @return <code>null</code> since <code>LeafElement</code>s cannot have
2048:      *         children
2049:      */
2050:     public Enumeration children()
2051:     {
2052:       return null;
2053:     }
2054: 
2055:     /**
2056:      * Returns <code>false</code> since <code>LeafElement</code>s cannot have
2057:      * children.
2058:      *
2059:      * @return <code>false</code> since <code>LeafElement</code>s cannot have
2060:      *         children
2061:      */
2062:     public boolean getAllowsChildren()
2063:     {
2064:       return false;
2065:     }
2066: 
2067:     /**
2068:      * Returns <code>null</code> since <code>LeafElement</code>s cannot have
2069:      * children.
2070:      *
2071:      * @return <code>null</code> since <code>LeafElement</code>s cannot have
2072:      *         children
2073:      */
2074:     public Element getElement(int index)
2075:     {
2076:       return null;
2077:     }
2078: 
2079:     /**
2080:      * Returns <code>0</code> since <code>LeafElement</code>s cannot have
2081:      * children.
2082:      *
2083:      * @return <code>0</code> since <code>LeafElement</code>s cannot have
2084:      *         children
2085:      */
2086:     public int getElementCount()
2087:     {
2088:       return 0;
2089:     }
2090: 
2091:     /**
2092:      * Returns <code>-1</code> since <code>LeafElement</code>s cannot have
2093:      * children.
2094:      *
2095:      * @return <code>-1</code> since <code>LeafElement</code>s cannot have
2096:      *         children
2097:      */
2098:     public int getElementIndex(int offset)
2099:     {
2100:       return -1;
2101:     }
2102: 
2103:     /**
2104:      * Returns the end offset of this <code>Element</code> inside the
2105:      * document.
2106:      *
2107:      * @return the end offset of this <code>Element</code> inside the
2108:      *         document
2109:      */
2110:     public int getEndOffset()
2111:     {
2112:       return endPos.getOffset();
2113:     }
2114: 
2115:     /**
2116:      * Returns the name of this <code>Element</code>. This is
2117:      * {@link #ContentElementName} in this case.
2118:      *
2119:      * @return the name of this <code>Element</code>
2120:      */
2121:     public String getName()
2122:     {
2123:       String name = super.getName();
2124:       if (name == null)
2125:         name = ContentElementName;
2126:       return name;
2127:     }
2128: 
2129:     /**
2130:      * Returns the start offset of this <code>Element</code> inside the
2131:      * document.
2132:      *
2133:      * @return the start offset of this <code>Element</code> inside the
2134:      *         document
2135:      */
2136:     public int getStartOffset()
2137:     {
2138:       return startPos.getOffset();
2139:     }
2140: 
2141:     /**
2142:      * Returns <code>true</code>.
2143:      *
2144:      * @return <code>true</code>
2145:      */
2146:     public boolean isLeaf()
2147:     {
2148:       return true;
2149:     }
2150: 
2151:     /**
2152:      * Returns a string representation of this <code>Element</code>.
2153:      *
2154:      * @return a string representation of this <code>Element</code>
2155:      */
2156:     public String toString()
2157:     {
2158:       return ("LeafElement(" + getName() + ") "
2159:           + getStartOffset() + "," + getEndOffset() + "\n");
2160:     }
2161:   }
2162: }