< prev index next >

src/java.desktop/share/classes/javax/swing/text/AbstractDocument.java

Print this page




  30 import java.text.Bidi;
  31 
  32 import javax.swing.UIManager;
  33 import javax.swing.undo.*;
  34 import javax.swing.event.*;
  35 import javax.swing.tree.TreeNode;
  36 
  37 import sun.font.BidiUtils;
  38 import sun.swing.SwingUtilities2;
  39 
  40 /**
  41  * An implementation of the document interface to serve as a
  42  * basis for implementing various kinds of documents.  At this
  43  * level there is very little policy, so there is a corresponding
  44  * increase in difficulty of use.
  45  * <p>
  46  * This class implements a locking mechanism for the document.  It
  47  * allows multiple readers or one writer, and writers must wait until
  48  * all observers of the document have been notified of a previous
  49  * change before beginning another mutation to the document.  The
  50  * read lock is acquired and released using the <code>render</code>
  51  * method.  A write lock is acquired by the methods that mutate the
  52  * document, and are held for the duration of the method call.
  53  * Notification is done on the thread that produced the mutation,
  54  * and the thread has full read access to the document for the
  55  * duration of the notification, but other readers are kept out
  56  * until the notification has finished.  The notification is a
  57  * beans event notification which does not allow any further
  58  * mutations until all listeners have been notified.
  59  * <p>
  60  * Any models subclassed from this class and used in conjunction
  61  * with a text component that has a look and feel implementation
  62  * that is derived from BasicTextUI may be safely updated
  63  * asynchronously, because all access to the View hierarchy
  64  * is serialized by BasicTextUI if the document is of type
  65  * <code>AbstractDocument</code>.  The locking assumes that an
  66  * independent thread will access the View hierarchy only from
  67  * the DocumentListener methods, and that there will be only
  68  * one event thread active at a time.
  69  * <p>
  70  * If concurrency support is desired, there are the following
  71  * additional implications.  The code path for any DocumentListener
  72  * implementation and any UndoListener implementation must be threadsafe,
  73  * and not access the component lock if trying to be safe from deadlocks.
  74  * The <code>repaint</code> and <code>revalidate</code> methods
  75  * on JComponent are safe.
  76  * <p>
  77  * AbstractDocument models an implied break at the end of the document.
  78  * Among other things this allows you to position the caret after the last
  79  * character. As a result of this, <code>getLength</code> returns one less
  80  * than the length of the Content. If you create your own Content, be
  81  * sure and initialize it to have an additional character. Refer to
  82  * StringContent and GapContent for examples of this. Another implication
  83  * of this is that Elements that model the implied end character will have
  84  * an endOffset == (getLength() + 1). For example, in DefaultStyledDocument
  85  * <code>getParagraphElement(getLength()).getEndOffset() == getLength() + 1
  86  * </code>.
  87  * <p>
  88  * <strong>Warning:</strong>
  89  * Serialized objects of this class will not be compatible with
  90  * future Swing releases. The current serialization support is
  91  * appropriate for short term storage or RMI between applications running
  92  * the same version of Swing.  As of 1.4, support for long term storage
  93  * of all JavaBeans&trade;
  94  * has been added to the <code>java.beans</code> package.
  95  * Please see {@link java.beans.XMLEncoder}.
  96  *
  97  * @author  Timothy Prinzing
  98  */
  99 @SuppressWarnings("serial") // Same-version serialization only
 100 public abstract class AbstractDocument implements Document, Serializable {
 101 
 102     /**
 103      * Constructs a new <code>AbstractDocument</code>, wrapped around some
 104      * specified content storage mechanism.
 105      *
 106      * @param data the content
 107      */
 108     protected AbstractDocument(Content data) {
 109         this(data, StyleContext.getDefaultStyleContext());
 110     }
 111 
 112     /**
 113      * Constructs a new <code>AbstractDocument</code>, wrapped around some
 114      * specified content storage mechanism.
 115      *
 116      * @param data the content
 117      * @param context the attribute context
 118      */
 119     protected AbstractDocument(Content data, AttributeContext context) {
 120         this.data = data;
 121         this.context = context;
 122         bidiRoot = new BidiRootElement();
 123 
 124         if (defaultI18NProperty == null) {
 125             // determine default setting for i18n support
 126             String o = java.security.AccessController.doPrivileged(
 127                 new java.security.PrivilegedAction<String>() {
 128                     public String run() {
 129                         return System.getProperty(I18NProperty);
 130                     }
 131                 }
 132             );
 133             if (o != null) {


 138         }
 139         putProperty( I18NProperty, defaultI18NProperty);
 140 
 141         //REMIND(bcb) This creates an initial bidi element to account for
 142         //the \n that exists by default in the content.  Doing it this way
 143         //seems to expose a little too much knowledge of the content given
 144         //to us by the sub-class.  Consider having the sub-class' constructor
 145         //make an initial call to insertUpdate.
 146         writeLock();
 147         try {
 148             Element[] p = new Element[1];
 149             p[0] = new BidiElement( bidiRoot, 0, 1, 0 );
 150             bidiRoot.replace(0,0,p);
 151         } finally {
 152             writeUnlock();
 153         }
 154     }
 155 
 156     /**
 157      * Supports managing a set of properties. Callers
 158      * can use the <code>documentProperties</code> dictionary
 159      * to annotate the document with document-wide properties.
 160      *
 161      * @return a non-<code>null</code> <code>Dictionary</code>
 162      * @see #setDocumentProperties
 163      */
 164     public Dictionary<Object,Object> getDocumentProperties() {
 165         if (documentProperties == null) {
 166             documentProperties = new Hashtable<Object, Object>(2);
 167         }
 168         return documentProperties;
 169     }
 170 
 171     /**
 172      * Replaces the document properties dictionary for this document.
 173      *
 174      * @param x the new dictionary
 175      * @see #getDocumentProperties
 176      */
 177     public void setDocumentProperties(Dictionary<Object,Object> x) {
 178         documentProperties = x;
 179     }
 180 
 181     /**


 280         // Process the listeners last to first, notifying
 281         // those that are interested in this event
 282         for (int i = listeners.length-2; i>=0; i-=2) {
 283             if (listeners[i]==UndoableEditListener.class) {
 284                 // Lazily create the event:
 285                 // if (e == null)
 286                 // e = new ListSelectionEvent(this, firstIndex, lastIndex);
 287                 ((UndoableEditListener)listeners[i+1]).undoableEditHappened(e);
 288             }
 289         }
 290     }
 291 
 292     /**
 293      * Returns an array of all the objects currently registered
 294      * as <code><em>Foo</em>Listener</code>s
 295      * upon this document.
 296      * <code><em>Foo</em>Listener</code>s are registered using the
 297      * <code>add<em>Foo</em>Listener</code> method.
 298      *
 299      * <p>
 300      * You can specify the <code>listenerType</code> argument
 301      * with a class literal, such as
 302      * <code><em>Foo</em>Listener.class</code>.
 303      * For example, you can query a
 304      * document <code>d</code>
 305      * for its document listeners with the following code:
 306      *
 307      * <pre>DocumentListener[] mls = (DocumentListener[])(d.getListeners(DocumentListener.class));</pre>
 308      *
 309      * If no such listeners exist, this method returns an empty array.
 310      *
 311      * @param <T> the listener type
 312      * @param listenerType the type of listeners requested
 313      * @return an array of all objects registered as
 314      *          <code><em>Foo</em>Listener</code>s on this component,
 315      *          or an empty array if no such
 316      *          listeners have been added
 317      * @exception ClassCastException if <code>listenerType</code>
 318      *          doesn't specify a class or interface that implements
 319      *          <code>java.util.EventListener</code>
 320      *
 321      * @see #getDocumentListeners
 322      * @see #getUndoableEditListeners
 323      *
 324      * @since 1.3
 325      */
 326     public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
 327         return listenerList.getListeners(listenerType);
 328     }
 329 
 330     /**
 331      * Gets the asynchronous loading priority.  If less than zero,
 332      * the document should not be loaded asynchronously.
 333      *
 334      * @return the asynchronous loading priority, or <code>-1</code>
 335      *   if the document should not be loaded asynchronously
 336      */
 337     public int getAsynchronousLoadPriority() {
 338         Integer loadPriority = (Integer)
 339             getProperty(AbstractDocument.AsyncLoadPriority);
 340         if (loadPriority != null) {
 341             return loadPriority.intValue();
 342         }
 343         return -1;
 344     }
 345 
 346     /**
 347      * Sets the asynchronous loading priority.
 348      * @param p the new asynchronous loading priority; a value
 349      *   less than zero indicates that the document should not be
 350      *   loaded asynchronously
 351      */
 352     public void setAsynchronousLoadPriority(int p) {
 353         Integer loadPriority = (p >= 0) ? Integer.valueOf(p) : null;
 354         putProperty(AbstractDocument.AsyncLoadPriority, loadPriority);
 355     }
 356 
 357     /**
 358      * Sets the <code>DocumentFilter</code>. The <code>DocumentFilter</code>
 359      * is passed <code>insert</code> and <code>remove</code> to conditionally
 360      * allow inserting/deleting of the text.  A <code>null</code> value
 361      * indicates that no filtering will occur.
 362      *
 363      * @param filter the <code>DocumentFilter</code> used to constrain text
 364      * @see #getDocumentFilter
 365      * @since 1.4
 366      */
 367     public void setDocumentFilter(DocumentFilter filter) {
 368         documentFilter = filter;
 369     }
 370 
 371     /**
 372      * Returns the <code>DocumentFilter</code> that is responsible for
 373      * filtering of insertion/removal. A <code>null</code> return value
 374      * implies no filtering is to occur.
 375      *
 376      * @since 1.4
 377      * @see #setDocumentFilter
 378      * @return the DocumentFilter
 379      */
 380     public DocumentFilter getDocumentFilter() {
 381         return documentFilter;
 382     }
 383 
 384     // --- Document methods -----------------------------------------
 385 
 386     /**
 387      * This allows the model to be safely rendered in the presence
 388      * of currency, if the model supports being updated asynchronously.
 389      * The given runnable will be executed in a way that allows it
 390      * to safely read the model with no changes while the runnable
 391      * is being executed.  The runnable itself may <em>not</em>
 392      * make any mutations.
 393      * <p>


 419             r.run();
 420         } finally {
 421             readUnlock();
 422         }
 423     }
 424 
 425     /**
 426      * Returns the length of the data.  This is the number of
 427      * characters of content that represents the users data.
 428      *
 429      * @return the length &gt;= 0
 430      * @see Document#getLength
 431      */
 432     public int getLength() {
 433         return data.length() - 1;
 434     }
 435 
 436     /**
 437      * Adds a document listener for notification of any changes.
 438      *
 439      * @param listener the <code>DocumentListener</code> to add
 440      * @see Document#addDocumentListener
 441      */
 442     public void addDocumentListener(DocumentListener listener) {
 443         listenerList.add(DocumentListener.class, listener);
 444     }
 445 
 446     /**
 447      * Removes a document listener.
 448      *
 449      * @param listener the <code>DocumentListener</code> to remove
 450      * @see Document#removeDocumentListener
 451      */
 452     public void removeDocumentListener(DocumentListener listener) {
 453         listenerList.remove(DocumentListener.class, listener);
 454     }
 455 
 456     /**
 457      * Returns an array of all the document listeners
 458      * registered on this document.
 459      *
 460      * @return all of this document's <code>DocumentListener</code>s
 461      *         or an empty array if no document listeners are
 462      *         currently registered
 463      *
 464      * @see #addDocumentListener
 465      * @see #removeDocumentListener
 466      * @since 1.4
 467      */
 468     public DocumentListener[] getDocumentListeners() {
 469         return listenerList.getListeners(DocumentListener.class);
 470     }
 471 
 472     /**
 473      * Adds an undo listener for notification of any changes.
 474      * Undo/Redo operations performed on the <code>UndoableEdit</code>
 475      * will cause the appropriate DocumentEvent to be fired to keep
 476      * the view(s) in sync with the model.
 477      *
 478      * @param listener the <code>UndoableEditListener</code> to add
 479      * @see Document#addUndoableEditListener
 480      */
 481     public void addUndoableEditListener(UndoableEditListener listener) {
 482         listenerList.add(UndoableEditListener.class, listener);
 483     }
 484 
 485     /**
 486      * Removes an undo listener.
 487      *
 488      * @param listener the <code>UndoableEditListener</code> to remove
 489      * @see Document#removeDocumentListener
 490      */
 491     public void removeUndoableEditListener(UndoableEditListener listener) {
 492         listenerList.remove(UndoableEditListener.class, listener);
 493     }
 494 
 495     /**
 496      * Returns an array of all the undoable edit listeners
 497      * registered on this document.
 498      *
 499      * @return all of this document's <code>UndoableEditListener</code>s
 500      *         or an empty array if no undoable edit listeners are
 501      *         currently registered
 502      *
 503      * @see #addUndoableEditListener
 504      * @see #removeUndoableEditListener
 505      *
 506      * @since 1.4
 507      */
 508     public UndoableEditListener[] getUndoableEditListeners() {
 509         return listenerList.getListeners(UndoableEditListener.class);
 510     }
 511 
 512     /**
 513      * A convenience method for looking up a property value. It is
 514      * equivalent to:
 515      * <pre>
 516      * getDocumentProperties().get(key);
 517      * </pre>
 518      *
 519      * @param key the non-<code>null</code> property key
 520      * @return the value of this property or <code>null</code>
 521      * @see #getDocumentProperties
 522      */
 523     public final Object getProperty(Object key) {
 524         return getDocumentProperties().get(key);
 525     }
 526 
 527 
 528     /**
 529      * A convenience method for storing up a property value.  It is
 530      * equivalent to:
 531      * <pre>
 532      * getDocumentProperties().put(key, value);
 533      * </pre>
 534      * If <code>value</code> is <code>null</code> this method will
 535      * remove the property.
 536      *
 537      * @param key the non-<code>null</code> key
 538      * @param value the property value
 539      * @see #getDocumentProperties
 540      */
 541     public final void putProperty(Object key, Object value) {
 542         if (value != null) {
 543             getDocumentProperties().put(key, value);
 544         } else {
 545             getDocumentProperties().remove(key);
 546         }
 547         if( key == TextAttribute.RUN_DIRECTION
 548             && Boolean.TRUE.equals(getProperty(I18NProperty)) )
 549         {
 550             //REMIND - this needs to flip on the i18n property if run dir
 551             //is rtl and the i18n property is not already on.
 552             writeLock();
 553             try {
 554                 DefaultDocumentEvent e
 555                     = new DefaultDocumentEvent(0, getLength(),
 556                                                DocumentEvent.EventType.INSERT);
 557                 updateBidi( e );


 579      * @see Document#remove
 580      */
 581     public void remove(int offs, int len) throws BadLocationException {
 582         DocumentFilter filter = getDocumentFilter();
 583 
 584         writeLock();
 585         try {
 586             if (filter != null) {
 587                 filter.remove(getFilterBypass(), offs, len);
 588             }
 589             else {
 590                 handleRemove(offs, len);
 591             }
 592         } finally {
 593             writeUnlock();
 594         }
 595     }
 596 
 597     /**
 598      * Performs the actual work of the remove. It is assumed the caller
 599      * will have obtained a <code>writeLock</code> before invoking this.
 600      */
 601     void handleRemove(int offs, int len) throws BadLocationException {
 602         if (len > 0) {
 603             if (offs < 0 || (offs + len) > getLength()) {
 604                 throw new BadLocationException("Invalid remove",
 605                                                getLength() + 1);
 606             }
 607             DefaultDocumentEvent chng =
 608                     new DefaultDocumentEvent(offs, len, DocumentEvent.EventType.REMOVE);
 609 
 610             boolean isComposedTextElement;
 611             // Check whether the position of interest is the composed text
 612             isComposedTextElement = Utilities.isComposedTextElement(this, offs);
 613 
 614             removeUpdate(chng);
 615             UndoableEdit u = data.remove(offs, len);
 616             if (u != null) {
 617                 chng.addEdit(u);
 618             }
 619             postRemoveUpdate(chng);
 620             // Mark the edit as done.
 621             chng.end();
 622             fireRemoveUpdate(chng);
 623             // only fire undo if Content implementation supports it
 624             // undo for the composed text is not supported for now
 625             if ((u != null) && !isComposedTextElement) {
 626                 fireUndoableEditUpdate(new UndoableEditEvent(this, chng));
 627             }
 628         }
 629     }
 630 
 631     /**
 632      * Deletes the region of text from <code>offset</code> to
 633      * <code>offset + length</code>, and replaces it with <code>text</code>.
 634      * It is up to the implementation as to how this is implemented, some
 635      * implementations may treat this as two distinct operations: a remove
 636      * followed by an insert, others may treat the replace as one atomic
 637      * operation.
 638      *
 639      * @param offset index of child element
 640      * @param length length of text to delete, may be 0 indicating don't
 641      *               delete anything
 642      * @param text text to insert, <code>null</code> indicates no text to insert
 643      * @param attrs AttributeSet indicating attributes of inserted text,
 644      *              <code>null</code>
 645      *              is legal, and typically treated as an empty attributeset,
 646      *              but exact interpretation is left to the subclass
 647      * @exception BadLocationException the given position is not a valid
 648      *            position within the document
 649      * @since 1.4
 650      */
 651     public void replace(int offset, int length, String text,
 652                         AttributeSet attrs) throws BadLocationException {
 653         if (length == 0 && (text == null || text.length() == 0)) {
 654             return;
 655         }
 656         DocumentFilter filter = getDocumentFilter();
 657 
 658         writeLock();
 659         try {
 660             if (filter != null) {
 661                 filter.replace(getFilterBypass(), offset, length, text,
 662                                attrs);
 663             }
 664             else {


 873     public Element[] getRootElements() {
 874         Element[] elems = new Element[2];
 875         elems[0] = getDefaultRootElement();
 876         elems[1] = getBidiRootElement();
 877         return elems;
 878     }
 879 
 880     /**
 881      * Returns the root element that views should be based upon
 882      * unless some other mechanism for assigning views to element
 883      * structures is provided.
 884      *
 885      * @return the root element
 886      * @see Document#getDefaultRootElement
 887      */
 888     public abstract Element getDefaultRootElement();
 889 
 890     // ---- local methods -----------------------------------------
 891 
 892     /**
 893      * Returns the <code>FilterBypass</code>. This will create one if one
 894      * does not yet exist.
 895      */
 896     private DocumentFilter.FilterBypass getFilterBypass() {
 897         if (filterBypass == null) {
 898             filterBypass = new DefaultFilterBypass();
 899         }
 900         return filterBypass;
 901     }
 902 
 903     /**
 904      * Returns the root element of the bidirectional structure for this
 905      * document.  Its children represent character runs with a given
 906      * Unicode bidi level.
 907      * @return the root element of the bidirectional structure for this
 908      * document
 909      */
 910     public Element getBidiRootElement() {
 911         return bidiRoot;
 912     }
 913 
 914     /**
 915      * Returns true if the text in the range <code>p0</code> to
 916      * <code>p1</code> is left to right.
 917      */
 918     static boolean isLeftToRight(Document doc, int p0, int p1) {
 919         if (Boolean.TRUE.equals(doc.getProperty(I18NProperty))) {
 920             if (doc instanceof AbstractDocument) {
 921                 AbstractDocument adoc = (AbstractDocument) doc;
 922                 Element bidiRoot = adoc.getBidiRootElement();
 923                 int index = bidiRoot.getElementIndex(p0);
 924                 Element bidiElem = bidiRoot.getElement(index);
 925                 if (bidiElem.getEndOffset() >= p1) {
 926                     AttributeSet bidiAttrs = bidiElem.getAttributes();
 927                     return ((StyleConstants.getBidiLevel(bidiAttrs) % 2) == 0);
 928                 }
 929             }
 930         }
 931         return true;
 932     }
 933 
 934     /**
 935      * Get the paragraph element containing the given position.  Sub-classes
 936      * must define for themselves what exactly constitutes a paragraph.  They


1281      * Creates a document branch element, that can contain other elements.
1282      *
1283      * @param parent the parent element
1284      * @param a the attributes
1285      * @return the element
1286      */
1287     protected Element createBranchElement(Element parent, AttributeSet a) {
1288         return new BranchElement(parent, a);
1289     }
1290 
1291     // --- Document locking ----------------------------------
1292 
1293     /**
1294      * Fetches the current writing thread if there is one.
1295      * This can be used to distinguish whether a method is
1296      * being called as part of an existing modification or
1297      * if a lock needs to be acquired and a new transaction
1298      * started.
1299      *
1300      * @return the thread actively modifying the document
1301      *  or <code>null</code> if there are no modifications in progress
1302      */
1303     protected final synchronized Thread getCurrentWriter() {
1304         return currWriter;
1305     }
1306 
1307     /**
1308      * Acquires a lock to begin mutating the document this lock
1309      * protects.  There can be no writing, notification of changes, or
1310      * reading going on in order to gain the lock.  Additionally a thread is
1311      * allowed to gain more than one <code>writeLock</code>,
1312      * as long as it doesn't attempt to gain additional <code>writeLock</code>s
1313      * from within document notification.  Attempting to gain a
1314      * <code>writeLock</code> from within a DocumentListener notification will
1315      * result in an <code>IllegalStateException</code>.  The ability
1316      * to obtain more than one <code>writeLock</code> per thread allows
1317      * subclasses to gain a writeLock, perform a number of operations, then
1318      * release the lock.
1319      * <p>
1320      * Calls to <code>writeLock</code>
1321      * must be balanced with calls to <code>writeUnlock</code>, else the
1322      * <code>Document</code> will be left in a locked state so that no
1323      * reading or writing can be done.
1324      *
1325      * @exception IllegalStateException thrown on illegal lock
1326      *  attempt.  If the document is implemented properly, this can
1327      *  only happen if a document listener attempts to mutate the
1328      *  document.  This situation violates the bean event model
1329      *  where order of delivery is not guaranteed and all listeners
1330      *  should be notified before further mutations are allowed.
1331      */
1332     protected final synchronized void writeLock() {
1333         try {
1334             while ((numReaders > 0) || (currWriter != null)) {
1335                 if (Thread.currentThread() == currWriter) {
1336                     if (notifyingListeners) {
1337                         // Assuming one doesn't do something wrong in a
1338                         // subclass this should only happen if a
1339                         // DocumentListener tries to mutate the document.
1340                         throw new IllegalStateException(
1341                                       "Attempt to mutate in notification");
1342                     }
1343                     numWriters++;
1344                     return;
1345                 }
1346                 wait();
1347             }
1348             currWriter = Thread.currentThread();
1349             numWriters = 1;
1350         } catch (InterruptedException e) {
1351             throw new Error("Interrupted attempt to acquire write lock");
1352         }
1353     }
1354 
1355     /**
1356      * Releases a write lock previously obtained via <code>writeLock</code>.
1357      * After decrementing the lock count if there are no outstanding locks
1358      * this will allow a new writer, or readers.
1359      *
1360      * @see #writeLock
1361      */
1362     protected final synchronized void writeUnlock() {
1363         if (--numWriters <= 0) {
1364             numWriters = 0;
1365             currWriter = null;
1366             notifyAll();
1367         }
1368     }
1369 
1370     /**
1371      * Acquires a lock to begin reading some state from the
1372      * document.  There can be multiple readers at the same time.
1373      * Writing blocks the readers until notification of the change
1374      * to the listeners has been completed.  This method should
1375      * be used very carefully to avoid unintended compromise
1376      * of the document.  It should always be balanced with a
1377      * <code>readUnlock</code>.
1378      *
1379      * @see #readUnlock
1380      */
1381     public final synchronized void readLock() {
1382         try {
1383             while (currWriter != null) {
1384                 if (currWriter == Thread.currentThread()) {
1385                     // writer has full read access.... may try to acquire
1386                     // lock in notification
1387                     return;
1388                 }
1389                 wait();
1390             }
1391             numReaders += 1;
1392         } catch (InterruptedException e) {
1393             throw new Error("Interrupted attempt to acquire read lock");
1394         }
1395     }
1396 
1397     /**


1460             public void validateObject() {
1461                 try {
1462                     writeLock();
1463                     DefaultDocumentEvent e = new DefaultDocumentEvent
1464                                    (0, getLength(),
1465                                     DocumentEvent.EventType.INSERT);
1466                     updateBidi( e );
1467                 }
1468                 finally {
1469                     writeUnlock();
1470                 }
1471             }
1472         }, 0);
1473     }
1474 
1475     // ----- member variables ------------------------------------------
1476 
1477     private transient int numReaders;
1478     private transient Thread currWriter;
1479     /**
1480      * The number of writers, all obtained from <code>currWriter</code>.
1481      */
1482     private transient int numWriters;
1483     /**
1484      * True will notifying listeners.
1485      */
1486     private transient boolean notifyingListeners;
1487 
1488     private static Boolean defaultI18NProperty;
1489 
1490     /**
1491      * Storage for document-wide properties.
1492      */
1493     private Dictionary<Object,Object> documentProperties = null;
1494 
1495     /**
1496      * The event listener list for the document.
1497      */
1498     protected EventListenerList listenerList = new EventListenerList();
1499 
1500     /**


1600          *
1601          * @param offset the offset in the content &gt;= 0
1602          * @return a Position
1603          * @exception BadLocationException for an invalid offset
1604          */
1605         public Position createPosition(int offset) throws BadLocationException;
1606 
1607         /**
1608          * Current length of the sequence of character content.
1609          *
1610          * @return the length &gt;= 0
1611          */
1612         public int length();
1613 
1614         /**
1615          * Inserts a string of characters into the sequence.
1616          *
1617          * @param where   offset into the sequence to make the insertion &gt;= 0
1618          * @param str     string to insert
1619          * @return  if the implementation supports a history mechanism,
1620          *    a reference to an <code>Edit</code> implementation will be returned,
1621          *    otherwise returns <code>null</code>
1622          * @exception BadLocationException  thrown if the area covered by
1623          *   the arguments is not contained in the character sequence
1624          */
1625         public UndoableEdit insertString(int where, String str) throws BadLocationException;
1626 
1627         /**
1628          * Removes some portion of the sequence.
1629          *
1630          * @param where   The offset into the sequence to make the
1631          *   insertion &gt;= 0.
1632          * @param nitems  The number of items in the sequence to remove &gt;= 0.
1633          * @return  If the implementation supports a history mechanism,
1634          *    a reference to an Edit implementation will be returned,
1635          *    otherwise null.
1636          * @exception BadLocationException  Thrown if the area covered by
1637          *   the arguments is not contained in the character sequence.
1638          */
1639         public UndoableEdit remove(int where, int nitems) throws BadLocationException;
1640 
1641         /**


1657          * @param txt the target location to copy into
1658          * @exception BadLocationException  Thrown if the area covered by
1659          *   the arguments is not contained in the character sequence.
1660          */
1661         public void getChars(int where, int len, Segment txt) throws BadLocationException;
1662     }
1663 
1664     /**
1665      * An interface that can be used to allow MutableAttributeSet
1666      * implementations to use pluggable attribute compression
1667      * techniques.  Each mutation of the attribute set can be
1668      * used to exchange a previous AttributeSet instance with
1669      * another, preserving the possibility of the AttributeSet
1670      * remaining immutable.  An implementation is provided by
1671      * the StyleContext class.
1672      *
1673      * The Element implementations provided by this class use
1674      * this interface to provide their MutableAttributeSet
1675      * implementations, so that different AttributeSet compression
1676      * techniques can be employed.  The method
1677      * <code>getAttributeContext</code> should be implemented to
1678      * return the object responsible for implementing the desired
1679      * compression technique.
1680      *
1681      * @see StyleContext
1682      */
1683     public interface AttributeContext {
1684 
1685         /**
1686          * Adds an attribute to the given set, and returns
1687          * the new representative set.
1688          *
1689          * @param old the old attribute set
1690          * @param name the non-null attribute name
1691          * @param value the attribute value
1692          * @return the updated attribute set
1693          * @see MutableAttributeSet#addAttribute
1694          */
1695         public AttributeSet addAttribute(AttributeSet old, Object name, Object value);
1696 
1697         /**


1750          *
1751          * @param a the attribute set to reclaim
1752          */
1753         public void reclaim(AttributeSet a);
1754     }
1755 
1756     /**
1757      * Implements the abstract part of an element.  By default elements
1758      * support attributes by having a field that represents the immutable
1759      * part of the current attribute set for the element.  The element itself
1760      * implements MutableAttributeSet which can be used to modify the set
1761      * by fetching a new immutable set.  The immutable sets are provided
1762      * by the AttributeContext associated with the document.
1763      * <p>
1764      * <strong>Warning:</strong>
1765      * Serialized objects of this class will not be compatible with
1766      * future Swing releases. The current serialization support is
1767      * appropriate for short term storage or RMI between applications running
1768      * the same version of Swing.  As of 1.4, support for long term storage
1769      * of all JavaBeans&trade;
1770      * has been added to the <code>java.beans</code> package.
1771      * Please see {@link java.beans.XMLEncoder}.
1772      */
1773     @SuppressWarnings("serial") // Same-version serialization only
1774     public abstract class AbstractElement implements Element, MutableAttributeSet, Serializable, TreeNode {
1775 
1776         /**
1777          * Creates a new AbstractElement.
1778          *
1779          * @param parent the parent element
1780          * @param a the attributes for the element
1781          * @since 1.4
1782          */
1783         public AbstractElement(Element parent, AttributeSet a) {
1784             this.parent = parent;
1785             attributes = getAttributeContext().getEmptySet();
1786             if (a != null) {
1787                 addAttributes(a);
1788             }
1789         }
1790 


1938             return attributes.containsAttribute(name, value);
1939         }
1940 
1941 
1942         /**
1943          * Checks whether the element contains all the attributes.
1944          *
1945          * @param attrs the attributes to check
1946          * @return true if the element contains all the attributes
1947          * @see AttributeSet#containsAttributes
1948          */
1949         public boolean containsAttributes(AttributeSet attrs) {
1950             return attributes.containsAttributes(attrs);
1951         }
1952 
1953         /**
1954          * Gets the resolving parent.
1955          * If not overridden, the resolving parent defaults to
1956          * the parent element.
1957          *
1958          * @return the attributes from the parent, <code>null</code> if none
1959          * @see AttributeSet#getResolveParent
1960          */
1961         public AttributeSet getResolveParent() {
1962             AttributeSet a = attributes.getResolveParent();
1963             if ((a == null) && (parent != null)) {
1964                 a = parent.getAttributes();
1965             }
1966             return a;
1967         }
1968 
1969         // --- MutableAttributeSet ----------------------------------
1970         // should fetch a new immutable record for the field
1971         // "attributes".
1972 
1973         /**
1974          * Adds an attribute to the element.
1975          *
1976          * @param name the non-null attribute name
1977          * @param value the attribute value
1978          * @see MutableAttributeSet#addAttribute


2132         public abstract int getElementCount();
2133 
2134         /**
2135          * Gets the child element index closest to the given model offset.
2136          *
2137          * @param offset the offset &gt;= 0
2138          * @return the element index &gt;= 0
2139          */
2140         public abstract int getElementIndex(int offset);
2141 
2142         /**
2143          * Checks whether the element is a leaf.
2144          *
2145          * @return true if a leaf
2146          */
2147         public abstract boolean isLeaf();
2148 
2149         // --- TreeNode methods -------------------------------------
2150 
2151         /**
2152          * Returns the child <code>TreeNode</code> at index
2153          * <code>childIndex</code>.
2154          */
2155         public TreeNode getChildAt(int childIndex) {
2156             return (TreeNode)getElement(childIndex);
2157         }
2158 
2159         /**
2160          * Returns the number of children <code>TreeNode</code>'s
2161          * receiver contains.
2162          * @return the number of children <code>TreeNodews</code>'s
2163          * receiver contains
2164          */
2165         public int getChildCount() {
2166             return getElementCount();
2167         }
2168 
2169         /**
2170          * Returns the parent <code>TreeNode</code> of the receiver.
2171          * @return the parent <code>TreeNode</code> of the receiver
2172          */
2173         public TreeNode getParent() {
2174             return (TreeNode)getParentElement();
2175         }
2176 
2177         /**
2178          * Returns the index of <code>node</code> in the receivers children.
2179          * If the receiver does not contain <code>node</code>, -1 will be
2180          * returned.
2181          * @param node the location of interest
2182          * @return the index of <code>node</code> in the receiver's
2183          * children, or -1 if absent
2184          */
2185         public int getIndex(TreeNode node) {
2186             for(int counter = getChildCount() - 1; counter >= 0; counter--)
2187                 if(getChildAt(counter) == node)
2188                     return counter;
2189             return -1;
2190         }
2191 
2192         /**
2193          * Returns true if the receiver allows children.
2194          * @return true if the receiver allows children, otherwise false
2195          */
2196         public abstract boolean getAllowsChildren();
2197 
2198 
2199         /**
2200          * Returns the children of the receiver as an
2201          * <code>Enumeration</code>.
2202          * @return the children of the receiver as an <code>Enumeration</code>
2203          */
2204         public abstract Enumeration<TreeNode> children();
2205 
2206 
2207         // --- serialization ---------------------------------------------
2208 
2209         private void writeObject(ObjectOutputStream s) throws IOException {
2210             s.defaultWriteObject();
2211             StyleContext.writeAttributeSet(s, attributes);
2212         }
2213 
2214         private void readObject(ObjectInputStream s)
2215             throws ClassNotFoundException, IOException
2216         {
2217             s.defaultReadObject();
2218             MutableAttributeSet attr = new SimpleAttributeSet();
2219             StyleContext.readAttributeSet(s, attr);
2220             AttributeContext context = getAttributeContext();
2221             attributes = context.addAttributes(SimpleAttributeSet.EMPTY, attr);
2222         }
2223 
2224         // ---- variables -----------------------------------------------------
2225 
2226         private Element parent;
2227         private transient AttributeSet attributes;
2228 
2229     }
2230 
2231     /**
2232      * Implements a composite element that contains other elements.
2233      * <p>
2234      * <strong>Warning:</strong>
2235      * Serialized objects of this class will not be compatible with
2236      * future Swing releases. The current serialization support is
2237      * appropriate for short term storage or RMI between applications running
2238      * the same version of Swing.  As of 1.4, support for long term storage
2239      * of all JavaBeans&trade;
2240      * has been added to the <code>java.beans</code> package.
2241      * Please see {@link java.beans.XMLEncoder}.
2242      */
2243     @SuppressWarnings("serial") // Same-version serialization only
2244     public class BranchElement extends AbstractElement {
2245 
2246         /**
2247          * Constructs a composite element that initially contains
2248          * no children.
2249          *
2250          * @param parent  The parent element
2251          * @param a the attributes for the element
2252          * @since 1.4
2253          */
2254         public BranchElement(Element parent, AttributeSet a) {
2255             super(parent, a);
2256             children = new AbstractElement[1];
2257             nchildren = 0;
2258             lastIndex = -1;
2259         }
2260 


2444          * @return true if a leaf
2445          */
2446         public boolean isLeaf() {
2447             return false;
2448         }
2449 
2450 
2451         // ------ TreeNode ----------------------------------------------
2452 
2453         /**
2454          * Returns true if the receiver allows children.
2455          * @return true if the receiver allows children, otherwise false
2456          */
2457         public boolean getAllowsChildren() {
2458             return true;
2459         }
2460 
2461 
2462         /**
2463          * Returns the children of the receiver as an
2464          * <code>Enumeration</code>.
2465          * @return the children of the receiver
2466          */
2467         public Enumeration<TreeNode> children() {
2468             if(nchildren == 0)
2469                 return null;
2470 
2471             Vector<TreeNode> tempVector = new Vector<>(nchildren);
2472 
2473             for(int counter = 0; counter < nchildren; counter++)
2474                 tempVector.addElement(children[counter]);
2475             return tempVector.elements();
2476         }
2477 
2478         // ------ members ----------------------------------------------
2479 
2480         private AbstractElement[] children;
2481         private int nchildren;
2482         private int lastIndex;
2483     }
2484 
2485     /**
2486      * Implements an element that directly represents content of
2487      * some kind.
2488      * <p>
2489      * <strong>Warning:</strong>
2490      * Serialized objects of this class will not be compatible with
2491      * future Swing releases. The current serialization support is
2492      * appropriate for short term storage or RMI between applications running
2493      * the same version of Swing.  As of 1.4, support for long term storage
2494      * of all JavaBeans&trade;
2495      * has been added to the <code>java.beans</code> package.
2496      * Please see {@link java.beans.XMLEncoder}.
2497      *
2498      * @see     Element
2499      */
2500     @SuppressWarnings("serial") // Same-version serialization only
2501     public class LeafElement extends AbstractElement {
2502 
2503         /**
2504          * Constructs an element that represents content within the
2505          * document (has no children).
2506          *
2507          * @param parent  The parent element
2508          * @param a       The element attributes
2509          * @param offs0   The start offset &gt;= 0
2510          * @param offs1   The end offset &gt;= offs0
2511          * @since 1.4
2512          */
2513         public LeafElement(Element parent, AttributeSet a, int offs0, int offs1) {
2514             super(parent, a);
2515             try {


2598          *
2599          * @return true if a leaf
2600          */
2601         public boolean isLeaf() {
2602             return true;
2603         }
2604 
2605         // ------ TreeNode ----------------------------------------------
2606 
2607         /**
2608          * Returns true if the receiver allows children.
2609          * @return true if the receiver allows children, otherwise false
2610          */
2611         public boolean getAllowsChildren() {
2612             return false;
2613         }
2614 
2615 
2616         /**
2617          * Returns the children of the receiver as an
2618          * <code>Enumeration</code>.
2619          * @return the children of the receiver
2620          */
2621         @Override
2622         public Enumeration<TreeNode> children() {
2623             return null;
2624         }
2625 
2626         // --- serialization ---------------------------------------------
2627 
2628         private void writeObject(ObjectOutputStream s) throws IOException {
2629             s.defaultWriteObject();
2630             s.writeInt(p0.getOffset());
2631             s.writeInt(p1.getOffset());
2632         }
2633 
2634         private void readObject(ObjectInputStream s)
2635             throws ClassNotFoundException, IOException
2636         {
2637             s.defaultReadObject();
2638 




  30 import java.text.Bidi;
  31 
  32 import javax.swing.UIManager;
  33 import javax.swing.undo.*;
  34 import javax.swing.event.*;
  35 import javax.swing.tree.TreeNode;
  36 
  37 import sun.font.BidiUtils;
  38 import sun.swing.SwingUtilities2;
  39 
  40 /**
  41  * An implementation of the document interface to serve as a
  42  * basis for implementing various kinds of documents.  At this
  43  * level there is very little policy, so there is a corresponding
  44  * increase in difficulty of use.
  45  * <p>
  46  * This class implements a locking mechanism for the document.  It
  47  * allows multiple readers or one writer, and writers must wait until
  48  * all observers of the document have been notified of a previous
  49  * change before beginning another mutation to the document.  The
  50  * read lock is acquired and released using the {@code render}
  51  * method.  A write lock is acquired by the methods that mutate the
  52  * document, and are held for the duration of the method call.
  53  * Notification is done on the thread that produced the mutation,
  54  * and the thread has full read access to the document for the
  55  * duration of the notification, but other readers are kept out
  56  * until the notification has finished.  The notification is a
  57  * beans event notification which does not allow any further
  58  * mutations until all listeners have been notified.
  59  * <p>
  60  * Any models subclassed from this class and used in conjunction
  61  * with a text component that has a look and feel implementation
  62  * that is derived from BasicTextUI may be safely updated
  63  * asynchronously, because all access to the View hierarchy
  64  * is serialized by BasicTextUI if the document is of type
  65  * {@code AbstractDocument}.  The locking assumes that an
  66  * independent thread will access the View hierarchy only from
  67  * the DocumentListener methods, and that there will be only
  68  * one event thread active at a time.
  69  * <p>
  70  * If concurrency support is desired, there are the following
  71  * additional implications.  The code path for any DocumentListener
  72  * implementation and any UndoListener implementation must be threadsafe,
  73  * and not access the component lock if trying to be safe from deadlocks.
  74  * The {@code repaint} and {@code revalidate} methods
  75  * on JComponent are safe.
  76  * <p>
  77  * AbstractDocument models an implied break at the end of the document.
  78  * Among other things this allows you to position the caret after the last
  79  * character. As a result of this, {@code getLength} returns one less
  80  * than the length of the Content. If you create your own Content, be
  81  * sure and initialize it to have an additional character. Refer to
  82  * StringContent and GapContent for examples of this. Another implication
  83  * of this is that Elements that model the implied end character will have
  84  * an endOffset == (getLength() + 1). For example, in DefaultStyledDocument
  85  * {@code getParagraphElement(getLength()).getEndOffset() == getLength() + 1}.

  86  * <p>
  87  * <strong>Warning:</strong>
  88  * Serialized objects of this class will not be compatible with
  89  * future Swing releases. The current serialization support is
  90  * appropriate for short term storage or RMI between applications running
  91  * the same version of Swing.  As of 1.4, support for long term storage
  92  * of all JavaBeans&trade;
  93  * has been added to the {@code java.beans} package.
  94  * Please see {@link java.beans.XMLEncoder}.
  95  *
  96  * @author  Timothy Prinzing
  97  */
  98 @SuppressWarnings("serial") // Same-version serialization only
  99 public abstract class AbstractDocument implements Document, Serializable {
 100 
 101     /**
 102      * Constructs a new {@code AbstractDocument}, wrapped around some
 103      * specified content storage mechanism.
 104      *
 105      * @param data the content
 106      */
 107     protected AbstractDocument(Content data) {
 108         this(data, StyleContext.getDefaultStyleContext());
 109     }
 110 
 111     /**
 112      * Constructs a new {@code AbstractDocument}, wrapped around some
 113      * specified content storage mechanism.
 114      *
 115      * @param data the content
 116      * @param context the attribute context
 117      */
 118     protected AbstractDocument(Content data, AttributeContext context) {
 119         this.data = data;
 120         this.context = context;
 121         bidiRoot = new BidiRootElement();
 122 
 123         if (defaultI18NProperty == null) {
 124             // determine default setting for i18n support
 125             String o = java.security.AccessController.doPrivileged(
 126                 new java.security.PrivilegedAction<String>() {
 127                     public String run() {
 128                         return System.getProperty(I18NProperty);
 129                     }
 130                 }
 131             );
 132             if (o != null) {


 137         }
 138         putProperty( I18NProperty, defaultI18NProperty);
 139 
 140         //REMIND(bcb) This creates an initial bidi element to account for
 141         //the \n that exists by default in the content.  Doing it this way
 142         //seems to expose a little too much knowledge of the content given
 143         //to us by the sub-class.  Consider having the sub-class' constructor
 144         //make an initial call to insertUpdate.
 145         writeLock();
 146         try {
 147             Element[] p = new Element[1];
 148             p[0] = new BidiElement( bidiRoot, 0, 1, 0 );
 149             bidiRoot.replace(0,0,p);
 150         } finally {
 151             writeUnlock();
 152         }
 153     }
 154 
 155     /**
 156      * Supports managing a set of properties. Callers
 157      * can use the {@code documentProperties} dictionary
 158      * to annotate the document with document-wide properties.
 159      *
 160      * @return a non-{@code null Dictionary}
 161      * @see #setDocumentProperties
 162      */
 163     public Dictionary<Object,Object> getDocumentProperties() {
 164         if (documentProperties == null) {
 165             documentProperties = new Hashtable<Object, Object>(2);
 166         }
 167         return documentProperties;
 168     }
 169 
 170     /**
 171      * Replaces the document properties dictionary for this document.
 172      *
 173      * @param x the new dictionary
 174      * @see #getDocumentProperties
 175      */
 176     public void setDocumentProperties(Dictionary<Object,Object> x) {
 177         documentProperties = x;
 178     }
 179 
 180     /**


 279         // Process the listeners last to first, notifying
 280         // those that are interested in this event
 281         for (int i = listeners.length-2; i>=0; i-=2) {
 282             if (listeners[i]==UndoableEditListener.class) {
 283                 // Lazily create the event:
 284                 // if (e == null)
 285                 // e = new ListSelectionEvent(this, firstIndex, lastIndex);
 286                 ((UndoableEditListener)listeners[i+1]).undoableEditHappened(e);
 287             }
 288         }
 289     }
 290 
 291     /**
 292      * Returns an array of all the objects currently registered
 293      * as <code><em>Foo</em>Listener</code>s
 294      * upon this document.
 295      * <code><em>Foo</em>Listener</code>s are registered using the
 296      * <code>add<em>Foo</em>Listener</code> method.
 297      *
 298      * <p>
 299      * You can specify the {@code listenerType} argument
 300      * with a class literal, such as
 301      * <code><em>Foo</em>Listener.class</code>.
 302      * For example, you can query a
 303      * document {@code d}
 304      * for its document listeners with the following code:
 305      *
 306      * <pre>DocumentListener[] mls = (DocumentListener[])(d.getListeners(DocumentListener.class));</pre>
 307      *
 308      * If no such listeners exist, this method returns an empty array.
 309      *
 310      * @param <T> the listener type
 311      * @param listenerType the type of listeners requested
 312      * @return an array of all objects registered as
 313      *          <code><em>Foo</em>Listener</code>s on this component,
 314      *          or an empty array if no such
 315      *          listeners have been added
 316      * @exception ClassCastException if {@code listenerType}
 317      *          doesn't specify a class or interface that implements
 318      *          {@code java.util.EventListener}
 319      *
 320      * @see #getDocumentListeners
 321      * @see #getUndoableEditListeners
 322      *
 323      * @since 1.3
 324      */
 325     public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
 326         return listenerList.getListeners(listenerType);
 327     }
 328 
 329     /**
 330      * Gets the asynchronous loading priority.  If less than zero,
 331      * the document should not be loaded asynchronously.
 332      *
 333      * @return the asynchronous loading priority, or {@code -1}
 334      *   if the document should not be loaded asynchronously
 335      */
 336     public int getAsynchronousLoadPriority() {
 337         Integer loadPriority = (Integer)
 338             getProperty(AbstractDocument.AsyncLoadPriority);
 339         if (loadPriority != null) {
 340             return loadPriority.intValue();
 341         }
 342         return -1;
 343     }
 344 
 345     /**
 346      * Sets the asynchronous loading priority.
 347      * @param p the new asynchronous loading priority; a value
 348      *   less than zero indicates that the document should not be
 349      *   loaded asynchronously
 350      */
 351     public void setAsynchronousLoadPriority(int p) {
 352         Integer loadPriority = (p >= 0) ? Integer.valueOf(p) : null;
 353         putProperty(AbstractDocument.AsyncLoadPriority, loadPriority);
 354     }
 355 
 356     /**
 357      * Sets the {@code DocumentFilter}. The {@code DocumentFilter}
 358      * is passed {@code insert} and {@code remove} to conditionally
 359      * allow inserting/deleting of the text.  A {@code null} value
 360      * indicates that no filtering will occur.
 361      *
 362      * @param filter the {@code DocumentFilter} used to constrain text
 363      * @see #getDocumentFilter
 364      * @since 1.4
 365      */
 366     public void setDocumentFilter(DocumentFilter filter) {
 367         documentFilter = filter;
 368     }
 369 
 370     /**
 371      * Returns the {@code DocumentFilter} that is responsible for
 372      * filtering of insertion/removal. A {@code null} return value
 373      * implies no filtering is to occur.
 374      *
 375      * @since 1.4
 376      * @see #setDocumentFilter
 377      * @return the DocumentFilter
 378      */
 379     public DocumentFilter getDocumentFilter() {
 380         return documentFilter;
 381     }
 382 
 383     // --- Document methods -----------------------------------------
 384 
 385     /**
 386      * This allows the model to be safely rendered in the presence
 387      * of currency, if the model supports being updated asynchronously.
 388      * The given runnable will be executed in a way that allows it
 389      * to safely read the model with no changes while the runnable
 390      * is being executed.  The runnable itself may <em>not</em>
 391      * make any mutations.
 392      * <p>


 418             r.run();
 419         } finally {
 420             readUnlock();
 421         }
 422     }
 423 
 424     /**
 425      * Returns the length of the data.  This is the number of
 426      * characters of content that represents the users data.
 427      *
 428      * @return the length &gt;= 0
 429      * @see Document#getLength
 430      */
 431     public int getLength() {
 432         return data.length() - 1;
 433     }
 434 
 435     /**
 436      * Adds a document listener for notification of any changes.
 437      *
 438      * @param listener the {@code DocumentListener} to add
 439      * @see Document#addDocumentListener
 440      */
 441     public void addDocumentListener(DocumentListener listener) {
 442         listenerList.add(DocumentListener.class, listener);
 443     }
 444 
 445     /**
 446      * Removes a document listener.
 447      *
 448      * @param listener the {@code DocumentListener} to remove
 449      * @see Document#removeDocumentListener
 450      */
 451     public void removeDocumentListener(DocumentListener listener) {
 452         listenerList.remove(DocumentListener.class, listener);
 453     }
 454 
 455     /**
 456      * Returns an array of all the document listeners
 457      * registered on this document.
 458      *
 459      * @return all of this document's {@code DocumentListener}s
 460      *         or an empty array if no document listeners are
 461      *         currently registered
 462      *
 463      * @see #addDocumentListener
 464      * @see #removeDocumentListener
 465      * @since 1.4
 466      */
 467     public DocumentListener[] getDocumentListeners() {
 468         return listenerList.getListeners(DocumentListener.class);
 469     }
 470 
 471     /**
 472      * Adds an undo listener for notification of any changes.
 473      * Undo/Redo operations performed on the {@code UndoableEdit}
 474      * will cause the appropriate DocumentEvent to be fired to keep
 475      * the view(s) in sync with the model.
 476      *
 477      * @param listener the {@code UndoableEditListener} to add
 478      * @see Document#addUndoableEditListener
 479      */
 480     public void addUndoableEditListener(UndoableEditListener listener) {
 481         listenerList.add(UndoableEditListener.class, listener);
 482     }
 483 
 484     /**
 485      * Removes an undo listener.
 486      *
 487      * @param listener the {@code UndoableEditListener} to remove
 488      * @see Document#removeDocumentListener
 489      */
 490     public void removeUndoableEditListener(UndoableEditListener listener) {
 491         listenerList.remove(UndoableEditListener.class, listener);
 492     }
 493 
 494     /**
 495      * Returns an array of all the undoable edit listeners
 496      * registered on this document.
 497      *
 498      * @return all of this document's {@code UndoableEditListener}s
 499      *         or an empty array if no undoable edit listeners are
 500      *         currently registered
 501      *
 502      * @see #addUndoableEditListener
 503      * @see #removeUndoableEditListener
 504      *
 505      * @since 1.4
 506      */
 507     public UndoableEditListener[] getUndoableEditListeners() {
 508         return listenerList.getListeners(UndoableEditListener.class);
 509     }
 510 
 511     /**
 512      * A convenience method for looking up a property value. It is
 513      * equivalent to:
 514      * <pre>
 515      * getDocumentProperties().get(key);
 516      * </pre>
 517      *
 518      * @param key the non-{@code null} property key
 519      * @return the value of this property or {@code null}
 520      * @see #getDocumentProperties
 521      */
 522     public final Object getProperty(Object key) {
 523         return getDocumentProperties().get(key);
 524     }
 525 
 526 
 527     /**
 528      * A convenience method for storing up a property value.  It is
 529      * equivalent to:
 530      * <pre>
 531      * getDocumentProperties().put(key, value);
 532      * </pre>
 533      * If {@code value} is {@code null} this method will
 534      * remove the property.
 535      *
 536      * @param key the non-{@code null} key
 537      * @param value the property value
 538      * @see #getDocumentProperties
 539      */
 540     public final void putProperty(Object key, Object value) {
 541         if (value != null) {
 542             getDocumentProperties().put(key, value);
 543         } else {
 544             getDocumentProperties().remove(key);
 545         }
 546         if( key == TextAttribute.RUN_DIRECTION
 547             && Boolean.TRUE.equals(getProperty(I18NProperty)) )
 548         {
 549             //REMIND - this needs to flip on the i18n property if run dir
 550             //is rtl and the i18n property is not already on.
 551             writeLock();
 552             try {
 553                 DefaultDocumentEvent e
 554                     = new DefaultDocumentEvent(0, getLength(),
 555                                                DocumentEvent.EventType.INSERT);
 556                 updateBidi( e );


 578      * @see Document#remove
 579      */
 580     public void remove(int offs, int len) throws BadLocationException {
 581         DocumentFilter filter = getDocumentFilter();
 582 
 583         writeLock();
 584         try {
 585             if (filter != null) {
 586                 filter.remove(getFilterBypass(), offs, len);
 587             }
 588             else {
 589                 handleRemove(offs, len);
 590             }
 591         } finally {
 592             writeUnlock();
 593         }
 594     }
 595 
 596     /**
 597      * Performs the actual work of the remove. It is assumed the caller
 598      * will have obtained a {@code writeLock} before invoking this.
 599      */
 600     void handleRemove(int offs, int len) throws BadLocationException {
 601         if (len > 0) {
 602             if (offs < 0 || (offs + len) > getLength()) {
 603                 throw new BadLocationException("Invalid remove",
 604                                                getLength() + 1);
 605             }
 606             DefaultDocumentEvent chng =
 607                     new DefaultDocumentEvent(offs, len, DocumentEvent.EventType.REMOVE);
 608 
 609             boolean isComposedTextElement;
 610             // Check whether the position of interest is the composed text
 611             isComposedTextElement = Utilities.isComposedTextElement(this, offs);
 612 
 613             removeUpdate(chng);
 614             UndoableEdit u = data.remove(offs, len);
 615             if (u != null) {
 616                 chng.addEdit(u);
 617             }
 618             postRemoveUpdate(chng);
 619             // Mark the edit as done.
 620             chng.end();
 621             fireRemoveUpdate(chng);
 622             // only fire undo if Content implementation supports it
 623             // undo for the composed text is not supported for now
 624             if ((u != null) && !isComposedTextElement) {
 625                 fireUndoableEditUpdate(new UndoableEditEvent(this, chng));
 626             }
 627         }
 628     }
 629 
 630     /**
 631      * Deletes the region of text from {@code offset} to
 632      * {@code offset + length}, and replaces it with {@code text}.
 633      * It is up to the implementation as to how this is implemented, some
 634      * implementations may treat this as two distinct operations: a remove
 635      * followed by an insert, others may treat the replace as one atomic
 636      * operation.
 637      *
 638      * @param offset index of child element
 639      * @param length length of text to delete, may be 0 indicating don't
 640      *               delete anything
 641      * @param text text to insert, {@code null} indicates no text to insert
 642      * @param attrs AttributeSet indicating attributes of inserted text,
 643      *              {@code null}
 644      *              is legal, and typically treated as an empty attributeset,
 645      *              but exact interpretation is left to the subclass
 646      * @exception BadLocationException the given position is not a valid
 647      *            position within the document
 648      * @since 1.4
 649      */
 650     public void replace(int offset, int length, String text,
 651                         AttributeSet attrs) throws BadLocationException {
 652         if (length == 0 && (text == null || text.length() == 0)) {
 653             return;
 654         }
 655         DocumentFilter filter = getDocumentFilter();
 656 
 657         writeLock();
 658         try {
 659             if (filter != null) {
 660                 filter.replace(getFilterBypass(), offset, length, text,
 661                                attrs);
 662             }
 663             else {


 872     public Element[] getRootElements() {
 873         Element[] elems = new Element[2];
 874         elems[0] = getDefaultRootElement();
 875         elems[1] = getBidiRootElement();
 876         return elems;
 877     }
 878 
 879     /**
 880      * Returns the root element that views should be based upon
 881      * unless some other mechanism for assigning views to element
 882      * structures is provided.
 883      *
 884      * @return the root element
 885      * @see Document#getDefaultRootElement
 886      */
 887     public abstract Element getDefaultRootElement();
 888 
 889     // ---- local methods -----------------------------------------
 890 
 891     /**
 892      * Returns the {@code FilterBypass}. This will create one if one
 893      * does not yet exist.
 894      */
 895     private DocumentFilter.FilterBypass getFilterBypass() {
 896         if (filterBypass == null) {
 897             filterBypass = new DefaultFilterBypass();
 898         }
 899         return filterBypass;
 900     }
 901 
 902     /**
 903      * Returns the root element of the bidirectional structure for this
 904      * document.  Its children represent character runs with a given
 905      * Unicode bidi level.
 906      * @return the root element of the bidirectional structure for this
 907      * document
 908      */
 909     public Element getBidiRootElement() {
 910         return bidiRoot;
 911     }
 912 
 913     /**
 914      * Returns true if the text in the range {@code p0} to
 915      * {@code p1} is left to right.
 916      */
 917     static boolean isLeftToRight(Document doc, int p0, int p1) {
 918         if (Boolean.TRUE.equals(doc.getProperty(I18NProperty))) {
 919             if (doc instanceof AbstractDocument) {
 920                 AbstractDocument adoc = (AbstractDocument) doc;
 921                 Element bidiRoot = adoc.getBidiRootElement();
 922                 int index = bidiRoot.getElementIndex(p0);
 923                 Element bidiElem = bidiRoot.getElement(index);
 924                 if (bidiElem.getEndOffset() >= p1) {
 925                     AttributeSet bidiAttrs = bidiElem.getAttributes();
 926                     return ((StyleConstants.getBidiLevel(bidiAttrs) % 2) == 0);
 927                 }
 928             }
 929         }
 930         return true;
 931     }
 932 
 933     /**
 934      * Get the paragraph element containing the given position.  Sub-classes
 935      * must define for themselves what exactly constitutes a paragraph.  They


1280      * Creates a document branch element, that can contain other elements.
1281      *
1282      * @param parent the parent element
1283      * @param a the attributes
1284      * @return the element
1285      */
1286     protected Element createBranchElement(Element parent, AttributeSet a) {
1287         return new BranchElement(parent, a);
1288     }
1289 
1290     // --- Document locking ----------------------------------
1291 
1292     /**
1293      * Fetches the current writing thread if there is one.
1294      * This can be used to distinguish whether a method is
1295      * being called as part of an existing modification or
1296      * if a lock needs to be acquired and a new transaction
1297      * started.
1298      *
1299      * @return the thread actively modifying the document
1300      *  or {@code null} if there are no modifications in progress
1301      */
1302     protected final synchronized Thread getCurrentWriter() {
1303         return currWriter;
1304     }
1305 
1306     /**
1307      * Acquires a lock to begin mutating the document this lock
1308      * protects.  There can be no writing, notification of changes, or
1309      * reading going on in order to gain the lock.  Additionally a thread is
1310      * allowed to gain more than one {@code writeLock},
1311      * as long as it doesn't attempt to gain additional {@code writeLock}s
1312      * from within document notification.  Attempting to gain a
1313      * {@code writeLock} from within a DocumentListener notification will
1314      * result in an {@code IllegalStateException}.  The ability
1315      * to obtain more than one {@code writeLock} per thread allows
1316      * subclasses to gain a writeLock, perform a number of operations, then
1317      * release the lock.
1318      * <p>
1319      * Calls to {@code writeLock}
1320      * must be balanced with calls to {@code writeUnlock}, else the
1321      * {@code Document} will be left in a locked state so that no
1322      * reading or writing can be done.
1323      *
1324      * @exception IllegalStateException thrown on illegal lock
1325      *  attempt.  If the document is implemented properly, this can
1326      *  only happen if a document listener attempts to mutate the
1327      *  document.  This situation violates the bean event model
1328      *  where order of delivery is not guaranteed and all listeners
1329      *  should be notified before further mutations are allowed.
1330      */
1331     protected final synchronized void writeLock() {
1332         try {
1333             while ((numReaders > 0) || (currWriter != null)) {
1334                 if (Thread.currentThread() == currWriter) {
1335                     if (notifyingListeners) {
1336                         // Assuming one doesn't do something wrong in a
1337                         // subclass this should only happen if a
1338                         // DocumentListener tries to mutate the document.
1339                         throw new IllegalStateException(
1340                                       "Attempt to mutate in notification");
1341                     }
1342                     numWriters++;
1343                     return;
1344                 }
1345                 wait();
1346             }
1347             currWriter = Thread.currentThread();
1348             numWriters = 1;
1349         } catch (InterruptedException e) {
1350             throw new Error("Interrupted attempt to acquire write lock");
1351         }
1352     }
1353 
1354     /**
1355      * Releases a write lock previously obtained via {@code writeLock}.
1356      * After decrementing the lock count if there are no outstanding locks
1357      * this will allow a new writer, or readers.
1358      *
1359      * @see #writeLock
1360      */
1361     protected final synchronized void writeUnlock() {
1362         if (--numWriters <= 0) {
1363             numWriters = 0;
1364             currWriter = null;
1365             notifyAll();
1366         }
1367     }
1368 
1369     /**
1370      * Acquires a lock to begin reading some state from the
1371      * document.  There can be multiple readers at the same time.
1372      * Writing blocks the readers until notification of the change
1373      * to the listeners has been completed.  This method should
1374      * be used very carefully to avoid unintended compromise
1375      * of the document.  It should always be balanced with a
1376      * {@code readUnlock}.
1377      *
1378      * @see #readUnlock
1379      */
1380     public final synchronized void readLock() {
1381         try {
1382             while (currWriter != null) {
1383                 if (currWriter == Thread.currentThread()) {
1384                     // writer has full read access.... may try to acquire
1385                     // lock in notification
1386                     return;
1387                 }
1388                 wait();
1389             }
1390             numReaders += 1;
1391         } catch (InterruptedException e) {
1392             throw new Error("Interrupted attempt to acquire read lock");
1393         }
1394     }
1395 
1396     /**


1459             public void validateObject() {
1460                 try {
1461                     writeLock();
1462                     DefaultDocumentEvent e = new DefaultDocumentEvent
1463                                    (0, getLength(),
1464                                     DocumentEvent.EventType.INSERT);
1465                     updateBidi( e );
1466                 }
1467                 finally {
1468                     writeUnlock();
1469                 }
1470             }
1471         }, 0);
1472     }
1473 
1474     // ----- member variables ------------------------------------------
1475 
1476     private transient int numReaders;
1477     private transient Thread currWriter;
1478     /**
1479      * The number of writers, all obtained from {@code currWriter}.
1480      */
1481     private transient int numWriters;
1482     /**
1483      * True will notifying listeners.
1484      */
1485     private transient boolean notifyingListeners;
1486 
1487     private static Boolean defaultI18NProperty;
1488 
1489     /**
1490      * Storage for document-wide properties.
1491      */
1492     private Dictionary<Object,Object> documentProperties = null;
1493 
1494     /**
1495      * The event listener list for the document.
1496      */
1497     protected EventListenerList listenerList = new EventListenerList();
1498 
1499     /**


1599          *
1600          * @param offset the offset in the content &gt;= 0
1601          * @return a Position
1602          * @exception BadLocationException for an invalid offset
1603          */
1604         public Position createPosition(int offset) throws BadLocationException;
1605 
1606         /**
1607          * Current length of the sequence of character content.
1608          *
1609          * @return the length &gt;= 0
1610          */
1611         public int length();
1612 
1613         /**
1614          * Inserts a string of characters into the sequence.
1615          *
1616          * @param where   offset into the sequence to make the insertion &gt;= 0
1617          * @param str     string to insert
1618          * @return  if the implementation supports a history mechanism,
1619          *    a reference to an {@code Edit} implementation will be returned,
1620          *    otherwise returns {@code null}
1621          * @exception BadLocationException  thrown if the area covered by
1622          *   the arguments is not contained in the character sequence
1623          */
1624         public UndoableEdit insertString(int where, String str) throws BadLocationException;
1625 
1626         /**
1627          * Removes some portion of the sequence.
1628          *
1629          * @param where   The offset into the sequence to make the
1630          *   insertion &gt;= 0.
1631          * @param nitems  The number of items in the sequence to remove &gt;= 0.
1632          * @return  If the implementation supports a history mechanism,
1633          *    a reference to an Edit implementation will be returned,
1634          *    otherwise null.
1635          * @exception BadLocationException  Thrown if the area covered by
1636          *   the arguments is not contained in the character sequence.
1637          */
1638         public UndoableEdit remove(int where, int nitems) throws BadLocationException;
1639 
1640         /**


1656          * @param txt the target location to copy into
1657          * @exception BadLocationException  Thrown if the area covered by
1658          *   the arguments is not contained in the character sequence.
1659          */
1660         public void getChars(int where, int len, Segment txt) throws BadLocationException;
1661     }
1662 
1663     /**
1664      * An interface that can be used to allow MutableAttributeSet
1665      * implementations to use pluggable attribute compression
1666      * techniques.  Each mutation of the attribute set can be
1667      * used to exchange a previous AttributeSet instance with
1668      * another, preserving the possibility of the AttributeSet
1669      * remaining immutable.  An implementation is provided by
1670      * the StyleContext class.
1671      *
1672      * The Element implementations provided by this class use
1673      * this interface to provide their MutableAttributeSet
1674      * implementations, so that different AttributeSet compression
1675      * techniques can be employed.  The method
1676      * {@code getAttributeContext} should be implemented to
1677      * return the object responsible for implementing the desired
1678      * compression technique.
1679      *
1680      * @see StyleContext
1681      */
1682     public interface AttributeContext {
1683 
1684         /**
1685          * Adds an attribute to the given set, and returns
1686          * the new representative set.
1687          *
1688          * @param old the old attribute set
1689          * @param name the non-null attribute name
1690          * @param value the attribute value
1691          * @return the updated attribute set
1692          * @see MutableAttributeSet#addAttribute
1693          */
1694         public AttributeSet addAttribute(AttributeSet old, Object name, Object value);
1695 
1696         /**


1749          *
1750          * @param a the attribute set to reclaim
1751          */
1752         public void reclaim(AttributeSet a);
1753     }
1754 
1755     /**
1756      * Implements the abstract part of an element.  By default elements
1757      * support attributes by having a field that represents the immutable
1758      * part of the current attribute set for the element.  The element itself
1759      * implements MutableAttributeSet which can be used to modify the set
1760      * by fetching a new immutable set.  The immutable sets are provided
1761      * by the AttributeContext associated with the document.
1762      * <p>
1763      * <strong>Warning:</strong>
1764      * Serialized objects of this class will not be compatible with
1765      * future Swing releases. The current serialization support is
1766      * appropriate for short term storage or RMI between applications running
1767      * the same version of Swing.  As of 1.4, support for long term storage
1768      * of all JavaBeans&trade;
1769      * has been added to the {@code java.beans} package.
1770      * Please see {@link java.beans.XMLEncoder}.
1771      */
1772     @SuppressWarnings("serial") // Same-version serialization only
1773     public abstract class AbstractElement implements Element, MutableAttributeSet, Serializable, TreeNode {
1774 
1775         /**
1776          * Creates a new AbstractElement.
1777          *
1778          * @param parent the parent element
1779          * @param a the attributes for the element
1780          * @since 1.4
1781          */
1782         public AbstractElement(Element parent, AttributeSet a) {
1783             this.parent = parent;
1784             attributes = getAttributeContext().getEmptySet();
1785             if (a != null) {
1786                 addAttributes(a);
1787             }
1788         }
1789 


1937             return attributes.containsAttribute(name, value);
1938         }
1939 
1940 
1941         /**
1942          * Checks whether the element contains all the attributes.
1943          *
1944          * @param attrs the attributes to check
1945          * @return true if the element contains all the attributes
1946          * @see AttributeSet#containsAttributes
1947          */
1948         public boolean containsAttributes(AttributeSet attrs) {
1949             return attributes.containsAttributes(attrs);
1950         }
1951 
1952         /**
1953          * Gets the resolving parent.
1954          * If not overridden, the resolving parent defaults to
1955          * the parent element.
1956          *
1957          * @return the attributes from the parent, {@code null} if none
1958          * @see AttributeSet#getResolveParent
1959          */
1960         public AttributeSet getResolveParent() {
1961             AttributeSet a = attributes.getResolveParent();
1962             if ((a == null) && (parent != null)) {
1963                 a = parent.getAttributes();
1964             }
1965             return a;
1966         }
1967 
1968         // --- MutableAttributeSet ----------------------------------
1969         // should fetch a new immutable record for the field
1970         // "attributes".
1971 
1972         /**
1973          * Adds an attribute to the element.
1974          *
1975          * @param name the non-null attribute name
1976          * @param value the attribute value
1977          * @see MutableAttributeSet#addAttribute


2131         public abstract int getElementCount();
2132 
2133         /**
2134          * Gets the child element index closest to the given model offset.
2135          *
2136          * @param offset the offset &gt;= 0
2137          * @return the element index &gt;= 0
2138          */
2139         public abstract int getElementIndex(int offset);
2140 
2141         /**
2142          * Checks whether the element is a leaf.
2143          *
2144          * @return true if a leaf
2145          */
2146         public abstract boolean isLeaf();
2147 
2148         // --- TreeNode methods -------------------------------------
2149 
2150         /**
2151          * Returns the child {@code TreeNode} at index
2152          * {@code childIndex}.
2153          */
2154         public TreeNode getChildAt(int childIndex) {
2155             return (TreeNode)getElement(childIndex);
2156         }
2157 
2158         /**
2159          * Returns the number of children {@code TreeNode}'s
2160          * receiver contains.
2161          * @return the number of children {@code TreeNodews}'s
2162          * receiver contains
2163          */
2164         public int getChildCount() {
2165             return getElementCount();
2166         }
2167 
2168         /**
2169          * Returns the parent {@code TreeNode} of the receiver.
2170          * @return the parent {@code TreeNode} of the receiver
2171          */
2172         public TreeNode getParent() {
2173             return (TreeNode)getParentElement();
2174         }
2175 
2176         /**
2177          * Returns the index of {@code node} in the receivers children.
2178          * If the receiver does not contain {@code node}, -1 will be
2179          * returned.
2180          * @param node the location of interest
2181          * @return the index of {@code node} in the receiver's
2182          * children, or -1 if absent
2183          */
2184         public int getIndex(TreeNode node) {
2185             for(int counter = getChildCount() - 1; counter >= 0; counter--)
2186                 if(getChildAt(counter) == node)
2187                     return counter;
2188             return -1;
2189         }
2190 
2191         /**
2192          * Returns true if the receiver allows children.
2193          * @return true if the receiver allows children, otherwise false
2194          */
2195         public abstract boolean getAllowsChildren();
2196 
2197 
2198         /**
2199          * Returns the children of the receiver as an
2200          * {@code Enumeration}.
2201          * @return the children of the receiver as an {@code Enumeration}
2202          */
2203         public abstract Enumeration<TreeNode> children();
2204 
2205 
2206         // --- serialization ---------------------------------------------
2207 
2208         private void writeObject(ObjectOutputStream s) throws IOException {
2209             s.defaultWriteObject();
2210             StyleContext.writeAttributeSet(s, attributes);
2211         }
2212 
2213         private void readObject(ObjectInputStream s)
2214             throws ClassNotFoundException, IOException
2215         {
2216             s.defaultReadObject();
2217             MutableAttributeSet attr = new SimpleAttributeSet();
2218             StyleContext.readAttributeSet(s, attr);
2219             AttributeContext context = getAttributeContext();
2220             attributes = context.addAttributes(SimpleAttributeSet.EMPTY, attr);
2221         }
2222 
2223         // ---- variables -----------------------------------------------------
2224 
2225         private Element parent;
2226         private transient AttributeSet attributes;
2227 
2228     }
2229 
2230     /**
2231      * Implements a composite element that contains other elements.
2232      * <p>
2233      * <strong>Warning:</strong>
2234      * Serialized objects of this class will not be compatible with
2235      * future Swing releases. The current serialization support is
2236      * appropriate for short term storage or RMI between applications running
2237      * the same version of Swing.  As of 1.4, support for long term storage
2238      * of all JavaBeans&trade;
2239      * has been added to the {@code java.beans} package.
2240      * Please see {@link java.beans.XMLEncoder}.
2241      */
2242     @SuppressWarnings("serial") // Same-version serialization only
2243     public class BranchElement extends AbstractElement {
2244 
2245         /**
2246          * Constructs a composite element that initially contains
2247          * no children.
2248          *
2249          * @param parent  The parent element
2250          * @param a the attributes for the element
2251          * @since 1.4
2252          */
2253         public BranchElement(Element parent, AttributeSet a) {
2254             super(parent, a);
2255             children = new AbstractElement[1];
2256             nchildren = 0;
2257             lastIndex = -1;
2258         }
2259 


2443          * @return true if a leaf
2444          */
2445         public boolean isLeaf() {
2446             return false;
2447         }
2448 
2449 
2450         // ------ TreeNode ----------------------------------------------
2451 
2452         /**
2453          * Returns true if the receiver allows children.
2454          * @return true if the receiver allows children, otherwise false
2455          */
2456         public boolean getAllowsChildren() {
2457             return true;
2458         }
2459 
2460 
2461         /**
2462          * Returns the children of the receiver as an
2463          * {@code Enumeration}.
2464          * @return the children of the receiver
2465          */
2466         public Enumeration<TreeNode> children() {
2467             if(nchildren == 0)
2468                 return null;
2469 
2470             Vector<TreeNode> tempVector = new Vector<>(nchildren);
2471 
2472             for(int counter = 0; counter < nchildren; counter++)
2473                 tempVector.addElement(children[counter]);
2474             return tempVector.elements();
2475         }
2476 
2477         // ------ members ----------------------------------------------
2478 
2479         private AbstractElement[] children;
2480         private int nchildren;
2481         private int lastIndex;
2482     }
2483 
2484     /**
2485      * Implements an element that directly represents content of
2486      * some kind.
2487      * <p>
2488      * <strong>Warning:</strong>
2489      * Serialized objects of this class will not be compatible with
2490      * future Swing releases. The current serialization support is
2491      * appropriate for short term storage or RMI between applications running
2492      * the same version of Swing.  As of 1.4, support for long term storage
2493      * of all JavaBeans&trade;
2494      * has been added to the {@code java.beans} package.
2495      * Please see {@link java.beans.XMLEncoder}.
2496      *
2497      * @see     Element
2498      */
2499     @SuppressWarnings("serial") // Same-version serialization only
2500     public class LeafElement extends AbstractElement {
2501 
2502         /**
2503          * Constructs an element that represents content within the
2504          * document (has no children).
2505          *
2506          * @param parent  The parent element
2507          * @param a       The element attributes
2508          * @param offs0   The start offset &gt;= 0
2509          * @param offs1   The end offset &gt;= offs0
2510          * @since 1.4
2511          */
2512         public LeafElement(Element parent, AttributeSet a, int offs0, int offs1) {
2513             super(parent, a);
2514             try {


2597          *
2598          * @return true if a leaf
2599          */
2600         public boolean isLeaf() {
2601             return true;
2602         }
2603 
2604         // ------ TreeNode ----------------------------------------------
2605 
2606         /**
2607          * Returns true if the receiver allows children.
2608          * @return true if the receiver allows children, otherwise false
2609          */
2610         public boolean getAllowsChildren() {
2611             return false;
2612         }
2613 
2614 
2615         /**
2616          * Returns the children of the receiver as an
2617          * {@code Enumeration}.
2618          * @return the children of the receiver
2619          */
2620         @Override
2621         public Enumeration<TreeNode> children() {
2622             return null;
2623         }
2624 
2625         // --- serialization ---------------------------------------------
2626 
2627         private void writeObject(ObjectOutputStream s) throws IOException {
2628             s.defaultWriteObject();
2629             s.writeInt(p0.getOffset());
2630             s.writeInt(p1.getOffset());
2631         }
2632 
2633         private void readObject(ObjectInputStream s)
2634             throws ClassNotFoundException, IOException
2635         {
2636             s.defaultReadObject();
2637 


< prev index next >