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