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