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