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:     if (undo != null)
 545:       event.addEdit(undo);
 546: 
 547:     insertUpdate(event, attributes);
 548:     writeUnlock();
 549: 
 550:     fireInsertUpdate(event);
 551:     if (undo != null)
 552:       fireUndoableEditUpdate(new UndoableEditEvent(this, undo));
 553:   }
 554: 
 555:   /**
 556:    * Called to indicate that text has been inserted into this
 557:    * <code>Document</code>. The default implementation does nothing.
 558:    * This method is executed within a write lock.
 559:    *
 560:    * @param chng the <code>DefaultDocumentEvent</code> describing the change
 561:    * @param attr the attributes of the changed content
 562:    */
 563:   protected void insertUpdate(DefaultDocumentEvent chng, AttributeSet attr)
 564:   {
 565:     // Do nothing here. Subclasses may want to override this.
 566:   }
 567: 
 568:   /**
 569:    * Called after some content has been removed from this
 570:    * <code>Document</code>. The default implementation does nothing.
 571:    * This method is executed within a write lock.
 572:    *
 573:    * @param chng the <code>DefaultDocumentEvent</code> describing the change
 574:    */
 575:   protected void postRemoveUpdate(DefaultDocumentEvent chng)
 576:   {
 577:     // Do nothing here. Subclasses may want to override this.
 578:   }
 579: 
 580:   /**
 581:    * Stores a property in this <code>Document</code>'s property list.
 582:    *
 583:    * @param key the key of the property to be stored
 584:    * @param value the value of the property to be stored
 585:    */
 586:   public void putProperty(Object key, Object value)
 587:   {
 588:     // FIXME: make me thread-safe
 589:     if (properties == null)
 590:       properties = new Hashtable();
 591: 
 592:     properties.put(key, value);
 593:   }
 594: 
 595:   /**
 596:    * Blocks until a read lock can be obtained.  Must block if there is
 597:    * currently a writer modifying the <code>Document</code>.
 598:    */
 599:   public void readLock()
 600:   {
 601:     if (currentWriter != null && currentWriter.equals(Thread.currentThread()))
 602:       return;
 603:     synchronized (documentCV)
 604:       {
 605:         while (currentWriter != null || numWritersWaiting > 0)
 606:           {
 607:             try
 608:               {
 609:                 documentCV.wait();
 610:               }
 611:             catch (InterruptedException ie)
 612:               {
 613:                 throw new Error("interrupted trying to get a readLock");
 614:               }
 615:           }
 616:           numReaders++;
 617:       }
 618:   }
 619: 
 620:   /**
 621:    * Releases the read lock. If this was the only reader on this
 622:    * <code>Document</code>, writing may begin now.
 623:    */
 624:   public void readUnlock()
 625:   {
 626:     // Note we could have a problem here if readUnlock was called without a
 627:     // prior call to readLock but the specs simply warn users to ensure that
 628:     // balance by using a finally block:
 629:     // readLock()
 630:     // try
 631:     // { 
 632:     //   doSomethingHere 
 633:     // }
 634:     // finally
 635:     // {
 636:     //   readUnlock();
 637:     // }
 638:     
 639:     // All that the JDK seems to check for is that you don't call unlock
 640:     // more times than you've previously called lock, but it doesn't make
 641:     // sure that the threads calling unlock were the same ones that called lock
 642: 
 643:     // FIXME: the reference implementation throws a 
 644:     // javax.swing.text.StateInvariantError here
 645:     if (numReaders == 0)
 646:       throw new IllegalStateException("document lock failure");
 647:     
 648:     synchronized (documentCV)
 649:     {
 650:       // If currentWriter is not null, the application code probably had a 
 651:       // writeLock and then tried to obtain a readLock, in which case 
 652:       // numReaders wasn't incremented
 653:       if (currentWriter == null)
 654:         {
 655:           numReaders --;
 656:           if (numReaders == 0 && numWritersWaiting != 0)
 657:             documentCV.notify();
 658:         }
 659:     }
 660:   }
 661: 
 662:   /**
 663:    * Removes a piece of content from this <code>Document</code>.
 664:    *
 665:    * @param offset the start offset of the fragment to be removed
 666:    * @param length the length of the fragment to be removed
 667:    *
 668:    * @throws BadLocationException if <code>offset</code> or
 669:    *         <code>offset + length</code> or invalid locations within this
 670:    *         document
 671:    */
 672:   public void remove(int offset, int length) throws BadLocationException
 673:   {
 674:     DefaultDocumentEvent event =
 675:       new DefaultDocumentEvent(offset, length,
 676:                    DocumentEvent.EventType.REMOVE);
 677:     
 678:     removeUpdate(event);
 679: 
 680:     boolean shouldFire = content.getString(offset, length).length() != 0;
 681:     
 682:     writeLock();
 683:     UndoableEdit temp = content.remove(offset, length);
 684:     writeUnlock();
 685:     
 686:     postRemoveUpdate(event);
 687:     
 688:     if (shouldFire)
 689:       fireRemoveUpdate(event);
 690:   }
 691: 
 692:   /**
 693:    * Replaces a piece of content in this <code>Document</code> with
 694:    * another piece of content.
 695:    *
 696:    * @param offset the start offset of the fragment to be removed
 697:    * @param length the length of the fragment to be removed
 698:    * @param text the text to replace the content with
 699:    * @param attributes the text attributes to assign to the new content
 700:    *
 701:    * @throws BadLocationException if <code>offset</code> or
 702:    *         <code>offset + length</code> or invalid locations within this
 703:    *         document
 704:    *
 705:    * @since 1.4
 706:    */
 707:   public void replace(int offset, int length, String text,
 708:               AttributeSet attributes)
 709:     throws BadLocationException
 710:   {
 711:     remove(offset, length);
 712:     insertString(offset, text, attributes);
 713:   }
 714: 
 715:   /**
 716:    * Adds a <code>DocumentListener</code> object to this document.
 717:    *
 718:    * @param listener the listener to add
 719:    */
 720:   public void addDocumentListener(DocumentListener listener)
 721:   {
 722:     listenerList.add(DocumentListener.class, listener);
 723:   }
 724: 
 725:   /**
 726:    * Removes a <code>DocumentListener</code> object from this document.
 727:    *
 728:    * @param listener the listener to remove
 729:    */
 730:   public void removeDocumentListener(DocumentListener listener)
 731:   {
 732:     listenerList.remove(DocumentListener.class, listener);
 733:   }
 734: 
 735:   /**
 736:    * Returns all registered <code>DocumentListener</code>s.
 737:    *
 738:    * @return all registered <code>DocumentListener</code>s
 739:    */
 740:   public DocumentListener[] getDocumentListeners()
 741:   {
 742:     return (DocumentListener[]) getListeners(DocumentListener.class);
 743:   }
 744: 
 745:   /**
 746:    * Adds an {@link UndoableEditListener} to this <code>Document</code>.
 747:    *
 748:    * @param listener the listener to add
 749:    */
 750:   public void addUndoableEditListener(UndoableEditListener listener)
 751:   {
 752:     listenerList.add(UndoableEditListener.class, listener);
 753:   }
 754: 
 755:   /**
 756:    * Removes an {@link UndoableEditListener} from this <code>Document</code>.
 757:    *
 758:    * @param listener the listener to remove
 759:    */
 760:   public void removeUndoableEditListener(UndoableEditListener listener)
 761:   {
 762:     listenerList.remove(UndoableEditListener.class, listener);
 763:   }
 764: 
 765:   /**
 766:    * Returns all registered {@link UndoableEditListener}s.
 767:    *
 768:    * @return all registered {@link UndoableEditListener}s
 769:    */
 770:   public UndoableEditListener[] getUndoableEditListeners()
 771:   {
 772:     return (UndoableEditListener[]) getListeners(UndoableEditListener.class);
 773:   }
 774: 
 775:   /**
 776:    * Called before some content gets removed from this <code>Document</code>.
 777:    * The default implementation does nothing but may be overridden by
 778:    * subclasses to modify the <code>Document</code> structure in response
 779:    * to a remove request. The method is executed within a write lock.
 780:    *
 781:    * @param chng the <code>DefaultDocumentEvent</code> describing the change
 782:    */
 783:   protected void removeUpdate(DefaultDocumentEvent chng)
 784:   {
 785:     // Do nothing here. Subclasses may wish to override this.
 786:   }
 787: 
 788:   /**
 789:    * Called to render this <code>Document</code> visually. It obtains a read
 790:    * lock, ensuring that no changes will be made to the <code>document</code>
 791:    * during the rendering process. It then calls the {@link Runnable#run()}
 792:    * method on <code>runnable</code>. This method <em>must not</em> attempt
 793:    * to modifiy the <code>Document</code>, since a deadlock will occur if it
 794:    * tries to obtain a write lock. When the {@link Runnable#run()} method
 795:    * completes (either naturally or by throwing an exception), the read lock
 796:    * is released. Note that there is nothing in this method related to
 797:    * the actual rendering. It could be used to execute arbitrary code within
 798:    * a read lock.
 799:    *
 800:    * @param runnable the {@link Runnable} to execute
 801:    */
 802:   public void render(Runnable runnable)
 803:   {
 804:     readLock();
 805:     try
 806:     {
 807:       runnable.run();
 808:     }
 809:     finally
 810:     {
 811:       readUnlock();
 812:     }
 813:   }
 814: 
 815:   /**
 816:    * Sets the asynchronous loading priority for this <code>Document</code>.
 817:    * A value of <code>-1</code> indicates that this <code>Document</code>
 818:    * should be loaded synchronously.
 819:    *
 820:    * @param p the asynchronous loading priority to set
 821:    */
 822:   public void setAsynchronousLoadPriority(int p)
 823:   {
 824:     // TODO: Implement this properly.
 825:   }
 826: 
 827:   /**
 828:    * Sets the properties of this <code>Document</code>.
 829:    *
 830:    * @param p the document properties to set
 831:    */
 832:   public void setDocumentProperties(Dictionary p)
 833:   {
 834:     // FIXME: make me thread-safe
 835:     properties = p;
 836:   }
 837: 
 838:   /**
 839:    * Blocks until a write lock can be obtained.  Must wait if there are 
 840:    * readers currently reading or another thread is currently writing.
 841:    */
 842:   protected void writeLock()
 843:   {
 844:     if (currentWriter!= null && currentWriter.equals(Thread.currentThread()))
 845:       return;
 846:     synchronized (documentCV)
 847:       {
 848:         numWritersWaiting++;
 849:         while (numReaders > 0)
 850:           {
 851:             try
 852:               {
 853:                 documentCV.wait();
 854:               }
 855:             catch (InterruptedException ie)
 856:               {
 857:                 throw new Error("interruped while trying to obtain write lock");
 858:               }
 859:           }
 860:         numWritersWaiting --;
 861:         currentWriter = Thread.currentThread();
 862:       }
 863:   }
 864: 
 865:   /**
 866:    * Releases the write lock. This allows waiting readers or writers to
 867:    * obtain the lock.
 868:    */
 869:   protected void writeUnlock()
 870:   {
 871:     synchronized (documentCV)
 872:     {
 873:         if (Thread.currentThread().equals(currentWriter))
 874:           {
 875:             currentWriter = null;
 876:             documentCV.notifyAll();
 877:           }
 878:     }
 879:   }
 880: 
 881:   /**
 882:    * Returns the currently installed {@link DocumentFilter} for this
 883:    * <code>Document</code>.
 884:    *
 885:    * @return the currently installed {@link DocumentFilter} for this
 886:    *         <code>Document</code>
 887:    *
 888:    * @since 1.4
 889:    */
 890:   public DocumentFilter getDocumentFilter()
 891:   {
 892:     return documentFilter;
 893:   }
 894: 
 895:   /**
 896:    * Sets the {@link DocumentFilter} for this <code>Document</code>.
 897:    *
 898:    * @param filter the <code>DocumentFilter</code> to set
 899:    *
 900:    * @since 1.4
 901:    */
 902:   public void setDocumentFilter(DocumentFilter filter)
 903:   {
 904:     this.documentFilter = filter;
 905:   }
 906: 
 907:   /**
 908:    * Dumps diagnostic information to the specified <code>PrintStream</code>.
 909:    *
 910:    * @param out the stream to write the diagnostic information to
 911:    */
 912:   public void dump(PrintStream out)
 913:   {
 914:     ((AbstractElement) getDefaultRootElement()).dump(out, 0);
 915:   }
 916: 
 917:   /**
 918:    * Defines a set of methods for managing text attributes for one or more
 919:    * <code>Document</code>s.
 920:    *
 921:    * Replicating {@link AttributeSet}s throughout a <code>Document</code> can
 922:    * be very expensive. Implementations of this interface are intended to
 923:    * provide intelligent management of <code>AttributeSet</code>s, eliminating
 924:    * costly duplication.
 925:    *
 926:    * @see StyleContext
 927:    */
 928:   public interface AttributeContext
 929:   {
 930:     /**
 931:      * Returns an {@link AttributeSet} that contains the attributes
 932:      * of <code>old</code> plus the new attribute specified by
 933:      * <code>name</code> and <code>value</code>.
 934:      *
 935:      * @param old the attribute set to be merged with the new attribute
 936:      * @param name the name of the attribute to be added
 937:      * @param value the value of the attribute to be added
 938:      *
 939:      * @return the old attributes plus the new attribute
 940:      */
 941:     AttributeSet addAttribute(AttributeSet old, Object name, Object value);
 942: 
 943:     /**
 944:      * Returns an {@link AttributeSet} that contains the attributes
 945:      * of <code>old</code> plus the new attributes in <code>attributes</code>.
 946:      *
 947:      * @param old the set of attributes where to add the new attributes
 948:      * @param attributes the attributes to be added
 949:      *
 950:      * @return an {@link AttributeSet} that contains the attributes
 951:      *         of <code>old</code> plus the new attributes in
 952:      *         <code>attributes</code>
 953:      */
 954:     AttributeSet addAttributes(AttributeSet old, AttributeSet attributes);
 955: 
 956:     /**
 957:      * Returns an empty {@link AttributeSet}.
 958:      *
 959:      * @return  an empty {@link AttributeSet}
 960:      */
 961:     AttributeSet getEmptySet();
 962: 
 963:     /**
 964:      * Called to indicate that the attributes in <code>attributes</code> are
 965:      * no longer used.
 966:      *
 967:      * @param attributes the attributes are no longer used
 968:      */
 969:     void reclaim(AttributeSet attributes);
 970: 
 971:     /**
 972:      * Returns a {@link AttributeSet} that has the attribute with the specified
 973:      * <code>name</code> removed from <code>old</code>.
 974:      *
 975:      * @param old the attribute set from which an attribute is removed
 976:      * @param name the name of the attribute to be removed
 977:      *
 978:      * @return the attributes of <code>old</code> minus the attribute
 979:      *         specified by <code>name</code>
 980:      */
 981:     AttributeSet removeAttribute(AttributeSet old, Object name);
 982: 
 983:     /**
 984:      * Removes all attributes in <code>attributes</code> from <code>old</code>
 985:      * and returns the resulting <code>AttributeSet</code>.
 986:      *
 987:      * @param old the set of attributes from which to remove attributes
 988:      * @param attributes the attributes to be removed from <code>old</code>
 989:      *
 990:      * @return the attributes of <code>old</code> minus the attributes in
 991:      *         <code>attributes</code>
 992:      */
 993:     AttributeSet removeAttributes(AttributeSet old, AttributeSet attributes);
 994: 
 995:     /**
 996:      * Removes all attributes specified by <code>names</code> from
 997:      * <code>old</code> and returns the resulting <code>AttributeSet</code>.
 998:      *
 999:      * @param old the set of attributes from which to remove attributes
1000:      * @param names the names of the attributes to be removed from
1001:      *        <code>old</code>
1002:      *
1003:      * @return the attributes of <code>old</code> minus the attributes in
1004:      *         <code>attributes</code>
1005:      */
1006:     AttributeSet removeAttributes(AttributeSet old, Enumeration names);
1007:   }
1008: 
1009:   /**
1010:    * A sequence of data that can be edited. This is were the actual content
1011:    * in <code>AbstractDocument</code>'s is stored.
1012:    */
1013:   public interface Content
1014:   {
1015:     /**
1016:      * Creates a {@link Position} that keeps track of the location at
1017:      * <code>offset</code>.
1018:      *
1019:      * @return a {@link Position} that keeps track of the location at
1020:      *         <code>offset</code>.
1021:      *
1022:      * @throw BadLocationException if <code>offset</code> is not a valid
1023:      *        location in this <code>Content</code> model
1024:      */
1025:     Position createPosition(int offset) throws BadLocationException;
1026: 
1027:     /**
1028:      * Returns the length of the content.
1029:      *
1030:      * @return the length of the content
1031:      */
1032:     int length();
1033: 
1034:     /**
1035:      * Inserts a string into the content model.
1036:      *
1037:      * @param where the offset at which to insert the string
1038:      * @param str the string to be inserted
1039:      *
1040:      * @return an <code>UndoableEdit</code> or <code>null</code> if undo is
1041:      *         not supported by this <code>Content</code> model
1042:      *
1043:      * @throws BadLocationException if <code>where</code> is not a valid
1044:      *         location in this <code>Content</code> model
1045:      */
1046:     UndoableEdit insertString(int where, String str)
1047:       throws BadLocationException;
1048: 
1049:     /**
1050:      * Removes a piece of content from the content model.
1051:      *
1052:      * @param where the offset at which to remove content
1053:      * @param nitems the number of characters to be removed
1054:      *
1055:      * @return an <code>UndoableEdit</code> or <code>null</code> if undo is
1056:      *         not supported by this <code>Content</code> model
1057:      *
1058:      * @throws BadLocationException if <code>where</code> is not a valid
1059:      *         location in this <code>Content</code> model
1060:      */
1061:     UndoableEdit remove(int where, int nitems) throws BadLocationException;
1062: 
1063:     /**
1064:      * Returns a piece of content.
1065:      *
1066:      * @param where the start offset of the requested fragment
1067:      * @param len the length of the requested fragment
1068:      *
1069:      * @return the requested fragment
1070:      * @throws BadLocationException if <code>offset</code> or
1071:      *         <code>offset + len</code>is not a valid
1072:      *         location in this <code>Content</code> model
1073:      */
1074:     String getString(int where, int len) throws BadLocationException;
1075: 
1076:     /**
1077:      * Fetches a piece of content and stores it in <code>txt</code>.
1078:      *
1079:      * @param where the start offset of the requested fragment
1080:      * @param len the length of the requested fragment
1081:      * @param txt the <code>Segment</code> where to fragment is stored into
1082:      *
1083:      * @throws BadLocationException if <code>offset</code> or
1084:      *         <code>offset + len</code>is not a valid
1085:      *         location in this <code>Content</code> model
1086:      */
1087:     void getChars(int where, int len, Segment txt) throws BadLocationException;
1088:   }
1089: 
1090:   /**
1091:    * An abstract base implementation of the {@link Element} interface.
1092:    */
1093:   public abstract class AbstractElement
1094:     implements Element, MutableAttributeSet, TreeNode, Serializable
1095:   {
1096:     /** The serialization UID (compatible with JDK1.5). */
1097:     private static final long serialVersionUID = 1712240033321461704L;
1098: 
1099:     /** The number of characters that this Element spans. */
1100:     int count;
1101: 
1102:     /** The starting offset of this Element. */
1103:     int offset;
1104: 
1105:     /** The attributes of this Element. */
1106:     AttributeSet attributes;
1107: 
1108:     /** The parent element. */
1109:     Element element_parent;
1110: 
1111:     /** The parent in the TreeNode interface. */
1112:     TreeNode tree_parent;
1113: 
1114:     /** The children of this element. */
1115:     Vector tree_children;
1116: 
1117:     /**
1118:      * Creates a new instance of <code>AbstractElement</code> with a
1119:      * specified parent <code>Element</code> and <code>AttributeSet</code>.
1120:      *
1121:      * @param p the parent of this <code>AbstractElement</code>
1122:      * @param s the attributes to be assigned to this
1123:      *        <code>AbstractElement</code>
1124:      */
1125:     public AbstractElement(Element p, AttributeSet s)
1126:     {
1127:       element_parent = p;
1128:       AttributeContext ctx = getAttributeContext();
1129:       attributes = ctx.getEmptySet();
1130:       if (s != null)
1131:         attributes = ctx.addAttributes(attributes, s);
1132:     }
1133: 
1134:     /**
1135:      * Returns the child nodes of this <code>Element</code> as an
1136:      * <code>Enumeration</code> of {@link TreeNode}s.
1137:      *
1138:      * @return the child nodes of this <code>Element</code> as an
1139:      *         <code>Enumeration</code> of {@link TreeNode}s
1140:      */
1141:     public abstract Enumeration children();
1142: 
1143:     /**
1144:      * Returns <code>true</code> if this <code>AbstractElement</code>
1145:      * allows children.
1146:      *
1147:      * @return <code>true</code> if this <code>AbstractElement</code>
1148:      *         allows children
1149:      */
1150:     public abstract boolean getAllowsChildren();
1151: 
1152:     /**
1153:      * Returns the child of this <code>AbstractElement</code> at
1154:      * <code>index</code>.
1155:      *
1156:      * @param index the position in the child list of the child element to
1157:      *        be returned
1158:      *
1159:      * @return the child of this <code>AbstractElement</code> at
1160:      *         <code>index</code>
1161:      */
1162:     public TreeNode getChildAt(int index)
1163:     {
1164:       return (TreeNode) tree_children.get(index);
1165:     }
1166: 
1167:     /**
1168:      * Returns the number of children of this <code>AbstractElement</code>.
1169:      *
1170:      * @return the number of children of this <code>AbstractElement</code>
1171:      */
1172:     public int getChildCount()
1173:     {
1174:       return tree_children.size();
1175:     }
1176: 
1177:     /**
1178:      * Returns the index of a given child <code>TreeNode</code> or
1179:      * <code>-1</code> if <code>node</code> is not a child of this
1180:      * <code>AbstractElement</code>.
1181:      *
1182:      * @param node the node for which the index is requested
1183:      *
1184:      * @return the index of a given child <code>TreeNode</code> or
1185:      *         <code>-1</code> if <code>node</code> is not a child of this
1186:      *         <code>AbstractElement</code>
1187:      */
1188:     public int getIndex(TreeNode node)
1189:     {
1190:       return tree_children.indexOf(node);
1191:     }
1192: 
1193:     /**
1194:      * Returns the parent <code>TreeNode</code> of this
1195:      * <code>AbstractElement</code> or <code>null</code> if this element
1196:      * has no parent.
1197:      *
1198:      * @return the parent <code>TreeNode</code> of this
1199:      *         <code>AbstractElement</code> or <code>null</code> if this
1200:      *         element has no parent
1201:      */
1202:     public TreeNode getParent()
1203:     {
1204:       return tree_parent;
1205:     }
1206: 
1207:     /**
1208:      * Returns <code>true</code> if this <code>AbstractElement</code> is a
1209:      * leaf element, <code>false</code> otherwise.
1210:      *
1211:      * @return <code>true</code> if this <code>AbstractElement</code> is a
1212:      *         leaf element, <code>false</code> otherwise
1213:      */
1214:     public abstract boolean isLeaf();
1215: 
1216:     /**
1217:      * Adds an attribute to this element.
1218:      *
1219:      * @param name the name of the attribute to be added
1220:      * @param value the value of the attribute to be added
1221:      */
1222:     public void addAttribute(Object name, Object value)
1223:     {
1224:       attributes = getAttributeContext().addAttribute(attributes, name, value);
1225:     }
1226: 
1227:     /**
1228:      * Adds a set of attributes to this element.
1229:      *
1230:      * @param attrs the attributes to be added to this element
1231:      */
1232:     public void addAttributes(AttributeSet attrs)
1233:     {
1234:       attributes = getAttributeContext().addAttributes(attributes, attrs);
1235:     }
1236: 
1237:     /**
1238:      * Removes an attribute from this element.
1239:      *
1240:      * @param name the name of the attribute to be removed
1241:      */
1242:     public void removeAttribute(Object name)
1243:     {
1244:       attributes = getAttributeContext().removeAttribute(attributes, name);
1245:     }
1246: 
1247:     /**
1248:      * Removes a set of attributes from this element.
1249:      *
1250:      * @param attrs the attributes to be removed
1251:      */
1252:     public void removeAttributes(AttributeSet attrs)
1253:     {
1254:       attributes = getAttributeContext().removeAttributes(attributes, attrs);
1255:     }
1256: 
1257:     /**
1258:      * Removes a set of attribute from this element.
1259:      *
1260:      * @param names the names of the attributes to be removed
1261:      */
1262:     public void removeAttributes(Enumeration names)
1263:     {
1264:       attributes = getAttributeContext().removeAttributes(attributes, names);
1265:     }
1266: 
1267:     /**
1268:      * Sets the parent attribute set against which the element can resolve
1269:      * attributes that are not defined in itself.
1270:      *
1271:      * @param parent the resolve parent to set
1272:      */
1273:     public void setResolveParent(AttributeSet parent)
1274:     {
1275:       attributes = getAttributeContext().addAttribute(attributes,
1276:                                                       ResolveAttribute,
1277:                                                       parent);
1278:     }
1279: 
1280:     /**
1281:      * Returns <code>true</code> if this element contains the specified
1282:      * attribute.
1283:      *
1284:      * @param name the name of the attribute to check
1285:      * @param value the value of the attribute to check
1286:      *
1287:      * @return <code>true</code> if this element contains the specified
1288:      *         attribute
1289:      */
1290:     public boolean containsAttribute(Object name, Object value)
1291:     {
1292:       return attributes.containsAttribute(name, value);
1293:     }
1294: 
1295:     /**
1296:      * Returns <code>true</code> if this element contains all of the
1297:      * specified attributes.
1298:      *
1299:      * @param attrs the attributes to check
1300:      *
1301:      * @return <code>true</code> if this element contains all of the
1302:      *         specified attributes
1303:      */
1304:     public boolean containsAttributes(AttributeSet attrs)
1305:     {
1306:       return attributes.containsAttributes(attrs);
1307:     }
1308: 
1309:     /**
1310:      * Returns a copy of the attributes of this element.
1311:      *
1312:      * @return a copy of the attributes of this element
1313:      */
1314:     public AttributeSet copyAttributes()
1315:     {
1316:       return attributes.copyAttributes();
1317:     }
1318: 
1319:     /**
1320:      * Returns the attribute value with the specified key. If this attribute
1321:      * is not defined in this element and this element has a resolving
1322:      * parent, the search goes upward to the resolve parent chain.
1323:      *
1324:      * @param key the key of the requested attribute
1325:      *
1326:      * @return the attribute value for <code>key</code> of <code>null</code>
1327:      *         if <code>key</code> is not found locally and cannot be resolved
1328:      *         in this element's resolve parents
1329:      */
1330:     public Object getAttribute(Object key)
1331:     {
1332:       Object result = attributes.getAttribute(key);
1333:       if (result == null && element_parent != null)
1334:         {
1335:           AttributeSet parentSet = element_parent.getAttributes();
1336:           if (parentSet != null)
1337:             result = parentSet.getAttribute(key);
1338:         }
1339:       return result;
1340:     }
1341: 
1342:     /**
1343:      * Returns the number of defined attributes in this element.
1344:      *
1345:      * @return the number of defined attributes in this element
1346:      */
1347:     public int getAttributeCount()
1348:     {
1349:       return attributes.getAttributeCount();
1350:     }
1351: 
1352:     /**
1353:      * Returns the names of the attributes of this element.
1354:      *
1355:      * @return the names of the attributes of this element
1356:      */
1357:     public Enumeration getAttributeNames()
1358:     {
1359:       return attributes.getAttributeNames();
1360:     }
1361: 
1362:     /**
1363:      * Returns the resolve parent of this element.
1364:      * This is taken from the AttributeSet, but if this is null,
1365:      * this method instead returns the Element's parent's 
1366:      * AttributeSet
1367:      *
1368:      * @return the resolve parent of this element
1369:      *
1370:      * @see #setResolveParent(AttributeSet)
1371:      */
1372:     public AttributeSet getResolveParent()
1373:     {
1374:       if (attributes.getResolveParent() != null)
1375:         return attributes.getResolveParent();
1376:       return element_parent.getAttributes();
1377:     }
1378: 
1379:     /**
1380:      * Returns <code>true</code> if an attribute with the specified name
1381:      * is defined in this element, <code>false</code> otherwise.
1382:      *
1383:      * @param attrName the name of the requested attributes
1384:      *
1385:      * @return <code>true</code> if an attribute with the specified name
1386:      *         is defined in this element, <code>false</code> otherwise
1387:      */
1388:     public boolean isDefined(Object attrName)
1389:     {
1390:       return attributes.isDefined(attrName);
1391:     }
1392: 
1393:     /**
1394:      * Returns <code>true</code> if the specified <code>AttributeSet</code>
1395:      * is equal to this element's <code>AttributeSet</code>, <code>false</code>
1396:      * otherwise.
1397:      *
1398:      * @param attrs the attributes to compare this element to
1399:      *
1400:      * @return <code>true</code> if the specified <code>AttributeSet</code>
1401:      *         is equal to this element's <code>AttributeSet</code>,
1402:      *         <code>false</code> otherwise
1403:      */
1404:     public boolean isEqual(AttributeSet attrs) 
1405:     {
1406:       return attributes.isEqual(attrs);
1407:     }
1408: 
1409:     /**
1410:      * Returns the attributes of this element.
1411:      *
1412:      * @return the attributes of this element
1413:      */
1414:     public AttributeSet getAttributes()
1415:     {
1416:       return this;
1417:     }
1418: 
1419:     /**
1420:      * Returns the {@link Document} to which this element belongs.
1421:      *
1422:      * @return the {@link Document} to which this element belongs
1423:      */
1424:     public Document getDocument()
1425:     {
1426:       return AbstractDocument.this;
1427:     }
1428: 
1429:     /**
1430:      * Returns the child element at the specified <code>index</code>.
1431:      *
1432:      * @param index the index of the requested child element
1433:      *
1434:      * @return the requested element
1435:      */
1436:     public abstract Element getElement(int index);
1437: 
1438:     /**
1439:      * Returns the name of this element.
1440:      *
1441:      * @return the name of this element
1442:      */
1443:     public String getName()
1444:     {
1445:       return (String) getAttribute(NameAttribute);
1446:     }
1447: 
1448:     /**
1449:      * Returns the parent element of this element.
1450:      *
1451:      * @return the parent element of this element
1452:      */
1453:     public Element getParentElement()
1454:     {
1455:       return element_parent;
1456:     }
1457: 
1458:     /**
1459:      * Returns the offset inside the document model that is after the last
1460:      * character of this element.
1461:      *
1462:      * @return the offset inside the document model that is after the last
1463:      *         character of this element
1464:      */
1465:     public abstract int getEndOffset();
1466: 
1467:     /**
1468:      * Returns the number of child elements of this element.
1469:      *
1470:      * @return the number of child elements of this element
1471:      */
1472:     public abstract int getElementCount();
1473: 
1474:     /**
1475:      * Returns the index of the child element that spans the specified
1476:      * offset in the document model.
1477:      *
1478:      * @param offset the offset for which the responsible element is searched
1479:      *
1480:      * @return the index of the child element that spans the specified
1481:      *         offset in the document model
1482:      */
1483:     public abstract int getElementIndex(int offset);
1484: 
1485:     /**
1486:      * Returns the start offset if this element inside the document model.
1487:      *
1488:      * @return the start offset if this element inside the document model
1489:      */
1490:     public abstract int getStartOffset();
1491: 
1492:     /**
1493:      * Prints diagnostic output to the specified stream.
1494:      *
1495:      * @param stream the stream to write to
1496:      * @param indent the indentation level
1497:      */
1498:     public void dump(PrintStream stream, int indent)
1499:     {
1500:       StringBuffer b = new StringBuffer();
1501:       for (int i = 0; i < indent; ++i)
1502:         b.append(' ');
1503:       b.append('<');
1504:       b.append(getName());
1505:       // Dump attributes if there are any.
1506:       if (getAttributeCount() > 0)
1507:         {
1508:           b.append('\n');
1509:           Enumeration attNames = getAttributeNames();
1510:           while (attNames.hasMoreElements())
1511:             {
1512:               for (int i = 0; i < indent + 2; ++i)
1513:                 b.append(' ');
1514:               Object attName = attNames.nextElement();
1515:               b.append(attName);
1516:               b.append('=');
1517:               Object attribute = getAttribute(attName);
1518:               b.append(attribute);
1519:               b.append('\n');
1520:             }
1521:         }
1522:       b.append(">\n");
1523: 
1524:       // Dump element content for leaf elements.
1525:       if (isLeaf())
1526:         {
1527:           for (int i = 0; i < indent + 2; ++i)
1528:             b.append(' ');
1529:           int start = getStartOffset();
1530:           int end = getEndOffset();
1531:           b.append('[');
1532:           b.append(start);
1533:           b.append(',');
1534:           b.append(end);
1535:           b.append("][");
1536:           try
1537:             {
1538:               b.append(getDocument().getText(start, end - start));
1539:             }
1540:           catch (BadLocationException ex)
1541:             {
1542:               AssertionError err = new AssertionError("BadLocationException "
1543:                                                       + "must not be thrown "
1544:                                                       + "here.");
1545:               err.initCause(ex);
1546:           throw err;
1547:             }
1548:           b.append("]\n");
1549:         }
1550:       stream.print(b.toString());
1551: 
1552:       // Dump child elements if any.
1553:       int count = getElementCount();
1554:       for (int i = 0; i < count; ++i)
1555:         {
1556:           Element el = getElement(i);
1557:           if (el instanceof AbstractElement)
1558:             ((AbstractElement) el).dump(stream, indent + 2);
1559:         }
1560:     }
1561:   }
1562: 
1563:   /**
1564:    * An implementation of {@link Element} to represent composite
1565:    * <code>Element</code>s that contain other <code>Element</code>s.
1566:    */
1567:   public class BranchElement extends AbstractElement
1568:   {
1569:     /** The serialization UID (compatible with JDK1.5). */
1570:     private static final long serialVersionUID = -6037216547466333183L;
1571: 
1572:     /** The child elements of this BranchElement. */
1573:     private Element[] children = new Element[0];
1574: 
1575:     /**
1576:      * Creates a new <code>BranchElement</code> with the specified
1577:      * parent and attributes.
1578:      *
1579:      * @param parent the parent element of this <code>BranchElement</code>
1580:      * @param attributes the attributes to set on this
1581:      *        <code>BranchElement</code>
1582:      */
1583:     public BranchElement(Element parent, AttributeSet attributes)
1584:     {
1585:       super(parent, attributes);
1586:     }
1587: 
1588:     /**
1589:      * Returns the children of this <code>BranchElement</code>.
1590:      *
1591:      * @return the children of this <code>BranchElement</code>
1592:      */
1593:     public Enumeration children()
1594:     {
1595:       if (children.length == 0)
1596:         return null;
1597: 
1598:       Vector tmp = new Vector();
1599: 
1600:       for (int index = 0; index < children.length; ++index)
1601:     tmp.add(children[index]);
1602:       
1603:       return tmp.elements();
1604:     }
1605: 
1606:     /**
1607:      * Returns <code>true</code> since <code>BranchElements</code> allow
1608:      * child elements.
1609:      *
1610:      * @return <code>true</code> since <code>BranchElements</code> allow
1611:      *         child elements
1612:      */
1613:     public boolean getAllowsChildren()
1614:     {
1615:       return true;
1616:     }
1617: 
1618:     /**
1619:      * Returns the child element at the specified <code>index</code>.
1620:      *
1621:      * @param index the index of the requested child element
1622:      *
1623:      * @return the requested element
1624:      */
1625:     public Element getElement(int index)
1626:     {
1627:       if (index < 0 || index >= children.length)
1628:     return null;
1629: 
1630:       return children[index];
1631:     }
1632: 
1633:     /**
1634:      * Returns the number of child elements of this element.
1635:      *
1636:      * @return the number of child elements of this element
1637:      */
1638:     public int getElementCount()
1639:     {
1640:       return children.length;
1641:     }
1642: 
1643:     /**
1644:      * Returns the index of the child element that spans the specified
1645:      * offset in the document model.
1646:      *
1647:      * @param offset the offset for which the responsible element is searched
1648:      *
1649:      * @return the index of the child element that spans the specified
1650:      *         offset in the document model
1651:      */
1652:     public int getElementIndex(int offset)
1653:     {
1654:       // If offset is less than the start offset of our first child,
1655:       // return 0
1656:       if (offset < getStartOffset())
1657:         return 0;
1658:       
1659:       // XXX: There is surely a better algorithm
1660:       // as beginning from first element each time.
1661:       for (int index = 0; index < children.length - 1; ++index)
1662:         {
1663:           Element elem = children[index];
1664: 
1665:           if ((elem.getStartOffset() <= offset)
1666:                && (offset < elem.getEndOffset()))
1667:             return index;
1668:           // If the next element's start offset is greater than offset
1669:           // then we have to return the closest Element, since no Elements
1670:           // will contain the offset
1671:           if (children[index + 1].getStartOffset() > offset)
1672:             {
1673:               if ((offset - elem.getEndOffset()) > (children[index + 1].getStartOffset() - offset))
1674:                 return index + 1;
1675:               else
1676:                 return index;
1677:             }
1678:         }
1679: 
1680:       // If offset is greater than the index of the last element, return
1681:       // the index of the last element.
1682:       return getElementCount() - 1;
1683:     }
1684: 
1685:     /**
1686:      * Returns the offset inside the document model that is after the last
1687:      * character of this element.
1688:      * This is the end offset of the last child element. If this element
1689:      * has no children, this method throws a <code>NullPointerException</code>.
1690:      *
1691:      * @return the offset inside the document model that is after the last
1692:      *         character of this element
1693:      *
1694:      * @throws NullPointerException if this branch element has no children
1695:      */
1696:     public int getEndOffset()
1697:     {
1698:       if (getElementCount() == 0)
1699:         throw new NullPointerException("This BranchElement has no children.");
1700:       return children[children.length - 1].getEndOffset();
1701:     }
1702: 
1703:     /**
1704:      * Returns the name of this element. This is {@link #ParagraphElementName}
1705:      * in this case.
1706:      *
1707:      * @return the name of this element
1708:      */
1709:     public String getName()
1710:     {
1711:       return ParagraphElementName;
1712:     }
1713: 
1714:     /**
1715:      * Returns the start offset of this element inside the document model.
1716:      * This is the start offset of the first child element. If this element
1717:      * has no children, this method throws a <code>NullPointerException</code>.
1718:      *
1719:      * @return the start offset of this element inside the document model
1720:      *
1721:      * @throws NullPointerException if this branch element has no children
1722:      */
1723:     public int getStartOffset()
1724:     {
1725:       if (getElementCount() == 0)
1726:         throw new NullPointerException("This BranchElement has no children.");
1727:       return children[0].getStartOffset();
1728:     }
1729: 
1730:     /**
1731:      * Returns <code>false</code> since <code>BranchElement</code> are no
1732:      * leafes.
1733:      *
1734:      * @return <code>false</code> since <code>BranchElement</code> are no
1735:      *         leafes
1736:      */
1737:     public boolean isLeaf()
1738:     {
1739:       return false;
1740:     }
1741: 
1742:     /**
1743:      * Returns the <code>Element</code> at the specified <code>Document</code>
1744:      * offset.
1745:      *
1746:      * @return the <code>Element</code> at the specified <code>Document</code>
1747:      *         offset
1748:      *
1749:      * @see #getElementIndex(int)
1750:      */
1751:     public Element positionToElement(int position)
1752:     {
1753:       // XXX: There is surely a better algorithm
1754:       // as beginning from first element each time.
1755:       for (int index = 0; index < children.length; ++index)
1756:         {
1757:       Element elem = children[index];
1758: 
1759:       if ((elem.getStartOffset() <= position)
1760:           && (position < elem.getEndOffset()))
1761:         return elem;
1762:         }
1763: 
1764:       return null;
1765:     }
1766: 
1767:     /**
1768:      * Replaces a set of child elements with a new set of child elemens.
1769:      *
1770:      * @param offset the start index of the elements to be removed
1771:      * @param length the number of elements to be removed
1772:      * @param elements the new elements to be inserted
1773:      */
1774:     public void replace(int offset, int length, Element[] elements)
1775:     {
1776:       Element[] target = new Element[children.length - length
1777:                      + elements.length];
1778:       System.arraycopy(children, 0, target, 0, offset);
1779:       System.arraycopy(elements, 0, target, offset, elements.length);
1780:       System.arraycopy(children, offset + length, target,
1781:                offset + elements.length,
1782:                children.length - offset - length);
1783:       children = target;
1784:     }
1785: 
1786:     /**
1787:      * Returns a string representation of this element.
1788:      *
1789:      * @return a string representation of this element
1790:      */
1791:     public String toString()
1792:     {
1793:       return ("BranchElement(" + getName() + ") "
1794:           + getStartOffset() + "," + getEndOffset() + "\n");
1795:     }
1796:   }
1797: 
1798:   /**
1799:    * Stores the changes when a <code>Document</code> is beeing modified.
1800:    */
1801:   public class DefaultDocumentEvent extends CompoundEdit
1802:     implements DocumentEvent
1803:   {
1804:     /** The serialization UID (compatible with JDK1.5). */
1805:     private static final long serialVersionUID = 5230037221564563284L;
1806: 
1807:     /** The starting offset of the change. */
1808:     private int offset;
1809: 
1810:     /** The length of the change. */
1811:     private int length;
1812: 
1813:     /** The type of change. */
1814:     private DocumentEvent.EventType type;
1815: 
1816:     /**
1817:      * Maps <code>Element</code> to their change records.
1818:      */
1819:     Hashtable changes;
1820: 
1821:     /**
1822:      * Indicates if this event has been modified or not. This is used to
1823:      * determine if this event is thrown.
1824:      */
1825:     boolean modified;
1826: 
1827:     /**
1828:      * Creates a new <code>DefaultDocumentEvent</code>.
1829:      *
1830:      * @param offset the starting offset of the change
1831:      * @param length the length of the change
1832:      * @param type the type of change
1833:      */
1834:     public DefaultDocumentEvent(int offset, int length,
1835:                 DocumentEvent.EventType type)
1836:     {
1837:       this.offset = offset;
1838:       this.length = length;
1839:       this.type = type;
1840:       changes = new Hashtable();
1841:       modified = false;
1842:     }
1843: 
1844:     /**
1845:      * Adds an UndoableEdit to this <code>DocumentEvent</code>. If this
1846:      * edit is an instance of {@link ElementEdit}, then this record can
1847:      * later be fetched by calling {@link #getChange}.
1848:      *
1849:      * @param edit the undoable edit to add
1850:      */
1851:     public boolean addEdit(UndoableEdit edit)
1852:     {
1853:       // XXX - Fully qualify ElementChange to work around gcj bug #2499.
1854:       if (edit instanceof DocumentEvent.ElementChange)
1855:         {
1856:           modified = true;
1857:           DocumentEvent.ElementChange elEdit =
1858:             (DocumentEvent.ElementChange) edit;
1859:           changes.put(elEdit.getElement(), elEdit);
1860:         }
1861:       return super.addEdit(edit);
1862:     }
1863: 
1864:     /**
1865:      * Returns the document that has been modified.
1866:      *
1867:      * @return the document that has been modified
1868:      */
1869:     public Document getDocument()
1870:     {
1871:       return AbstractDocument.this;
1872:     }
1873: 
1874:     /**
1875:      * Returns the length of the modification.
1876:      *
1877:      * @return the length of the modification
1878:      */
1879:     public int getLength()
1880:     {
1881:       return length;
1882:     }
1883: 
1884:     /**
1885:      * Returns the start offset of the modification.
1886:      *
1887:      * @return the start offset of the modification
1888:      */
1889:     public int getOffset()
1890:     {
1891:       return offset;
1892:     }
1893: 
1894:     /**
1895:      * Returns the type of the modification.
1896:      *
1897:      * @return the type of the modification
1898:      */
1899:     public DocumentEvent.EventType getType()
1900:     {
1901:       return type;
1902:     }
1903: 
1904:     /**
1905:      * Returns the changes for an element.
1906:      *
1907:      * @param elem the element for which the changes are requested
1908:      *
1909:      * @return the changes for <code>elem</code> or <code>null</code> if
1910:      *         <code>elem</code> has not been changed
1911:      */
1912:     public DocumentEvent.ElementChange getChange(Element elem)
1913:     {
1914:       // XXX - Fully qualify ElementChange to work around gcj bug #2499.
1915:       return (DocumentEvent.ElementChange) changes.get(elem);
1916:     }
1917:     
1918:     /**
1919:      * Returns a String description of the change event.  This returns the
1920:      * toString method of the Vector of edits.
1921:      */
1922:     public String toString()
1923:     {
1924:       return edits.toString();
1925:     }
1926:   }
1927:   
1928:   /**
1929:    * An implementation of {@link DocumentEvent.ElementChange} to be added
1930:    * to {@link DefaultDocumentEvent}s.
1931:    */
1932:   public static class ElementEdit extends AbstractUndoableEdit
1933:     implements DocumentEvent.ElementChange
1934:   {
1935:     /** The serial version UID of ElementEdit. */
1936:     private static final long serialVersionUID = -1216620962142928304L;
1937: 
1938:     /**
1939:      * The changed element.
1940:      */
1941:     private Element elem;
1942: 
1943:     /**
1944:      * The index of the change.
1945:      */
1946:     private int index;
1947: 
1948:     /**
1949:      * The removed elements.
1950:      */
1951:     private Element[] removed;
1952: 
1953:     /**
1954:      * The added elements.
1955:      */
1956:     private Element[] added;
1957:     
1958:     /**
1959:      * Creates a new <code>ElementEdit</code>.
1960:      *
1961:      * @param elem the changed element
1962:      * @param index the index of the change
1963:      * @param removed the removed elements
1964:      * @param added the added elements
1965:      */
1966:     public ElementEdit(Element elem, int index,
1967:                Element[] removed, Element[] added)
1968:     {
1969:       this.elem = elem;
1970:       this.index = index;
1971:       this.removed = removed;
1972:       this.added = added;
1973:     }
1974: 
1975:     /**
1976:      * Returns the added elements.
1977:      *
1978:      * @return the added elements
1979:      */
1980:     public Element[] getChildrenAdded()
1981:     {
1982:       return added;
1983:     }
1984: 
1985:     /**
1986:      * Returns the removed elements.
1987:      *
1988:      * @return the removed elements
1989:      */
1990:     public Element[] getChildrenRemoved()
1991:     {
1992:       return removed;
1993:     }
1994: 
1995:     /**
1996:      * Returns the changed element.
1997:      *
1998:      * @return the changed element
1999:      */
2000:     public Element getElement()
2001:     {
2002:       return elem;
2003:     }
2004: 
2005:     /**
2006:      * Returns the index of the change.
2007:      *
2008:      * @return the index of the change
2009:      */
2010:     public int getIndex()
2011:     {
2012:       return index;
2013:     }
2014:   }
2015: 
2016:   /**
2017:    * An implementation of {@link Element} that represents a leaf in the
2018:    * document structure. This is used to actually store content.
2019:    */
2020:   public class LeafElement extends AbstractElement
2021:   {
2022:     /** The serialization UID (compatible with JDK1.5). */
2023:     private static final long serialVersionUID = -8906306331347768017L;
2024: 
2025:     /** Manages the start offset of this element. */
2026:     Position startPos;
2027: 
2028:     /** Manages the end offset of this element. */
2029:     Position endPos;
2030: 
2031:     /**
2032:      * Creates a new <code>LeafElement</code>.
2033:      *
2034:      * @param parent the parent of this <code>LeafElement</code>
2035:      * @param attributes the attributes to be set
2036:      * @param start the start index of this element inside the document model
2037:      * @param end the end index of this element inside the document model
2038:      */
2039:     public LeafElement(Element parent, AttributeSet attributes, int start,
2040:                        int end)
2041:     {
2042:       super(parent, attributes);
2043:     {
2044:       try
2045:         {
2046:           if (parent != null)
2047:         {
2048:           startPos = parent.getDocument().createPosition(start);
2049:           endPos = parent.getDocument().createPosition(end);
2050:         }
2051:           else
2052:         {
2053:           startPos = createPosition(start);
2054:           endPos = createPosition(end);
2055:         }
2056:         }
2057:       catch (BadLocationException ex)
2058:         {
2059:           AssertionError as;
2060:           as = new AssertionError("BadLocationException thrown "
2061:                       + "here. start=" + start
2062:                       + ", end=" + end
2063:                       + ", length=" + getLength());
2064:           as.initCause(ex);
2065:           throw as;
2066:         }
2067:     }
2068:     }
2069: 
2070:     /**
2071:      * Returns <code>null</code> since <code>LeafElement</code>s cannot have
2072:      * children.
2073:      *
2074:      * @return <code>null</code> since <code>LeafElement</code>s cannot have
2075:      *         children
2076:      */
2077:     public Enumeration children()
2078:     {
2079:       return null;
2080:     }
2081: 
2082:     /**
2083:      * Returns <code>false</code> since <code>LeafElement</code>s cannot have
2084:      * children.
2085:      *
2086:      * @return <code>false</code> since <code>LeafElement</code>s cannot have
2087:      *         children
2088:      */
2089:     public boolean getAllowsChildren()
2090:     {
2091:       return false;
2092:     }
2093: 
2094:     /**
2095:      * Returns <code>null</code> since <code>LeafElement</code>s cannot have
2096:      * children.
2097:      *
2098:      * @return <code>null</code> since <code>LeafElement</code>s cannot have
2099:      *         children
2100:      */
2101:     public Element getElement(int index)
2102:     {
2103:       return null;
2104:     }
2105: 
2106:     /**
2107:      * Returns <code>0</code> since <code>LeafElement</code>s cannot have
2108:      * children.
2109:      *
2110:      * @return <code>0</code> since <code>LeafElement</code>s cannot have
2111:      *         children
2112:      */
2113:     public int getElementCount()
2114:     {
2115:       return 0;
2116:     }
2117: 
2118:     /**
2119:      * Returns <code>-1</code> since <code>LeafElement</code>s cannot have
2120:      * children.
2121:      *
2122:      * @return <code>-1</code> since <code>LeafElement</code>s cannot have
2123:      *         children
2124:      */
2125:     public int getElementIndex(int offset)
2126:     {
2127:       return -1;
2128:     }
2129: 
2130:     /**
2131:      * Returns the end offset of this <code>Element</code> inside the
2132:      * document.
2133:      *
2134:      * @return the end offset of this <code>Element</code> inside the
2135:      *         document
2136:      */
2137:     public int getEndOffset()
2138:     {
2139:       return endPos.getOffset();
2140:     }
2141: 
2142:     /**
2143:      * Returns the name of this <code>Element</code>. This is
2144:      * {@link #ContentElementName} in this case.
2145:      *
2146:      * @return the name of this <code>Element</code>
2147:      */
2148:     public String getName()
2149:     {
2150:       String name = super.getName();
2151:       if (name == null)
2152:         name = ContentElementName;
2153:       return name;
2154:     }
2155: 
2156:     /**
2157:      * Returns the start offset of this <code>Element</code> inside the
2158:      * document.
2159:      *
2160:      * @return the start offset of this <code>Element</code> inside the
2161:      *         document
2162:      */
2163:     public int getStartOffset()
2164:     {
2165:       return startPos.getOffset();
2166:     }
2167: 
2168:     /**
2169:      * Returns <code>true</code>.
2170:      *
2171:      * @return <code>true</code>
2172:      */
2173:     public boolean isLeaf()
2174:     {
2175:       return true;
2176:     }
2177: 
2178:     /**
2179:      * Returns a string representation of this <code>Element</code>.
2180:      *
2181:      * @return a string representation of this <code>Element</code>
2182:      */
2183:     public String toString()
2184:     {
2185:       return ("LeafElement(" + getName() + ") "
2186:           + getStartOffset() + "," + getEndOffset() + "\n");
2187:     }
2188:   }
2189: }