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™
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 >= 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 >= 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 >= 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 >= 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 >= 0.
1632 * @param nitems The number of items in the sequence to remove >= 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™
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 >= 0
2138 * @return the element index >= 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™
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™
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 >= 0
2510 * @param offs1 The end offset >= 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™
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 >= 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 >= 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 >= 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 >= 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 >= 0.
1631 * @param nitems The number of items in the sequence to remove >= 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™
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 >= 0
2137 * @return the element index >= 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™
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™
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 >= 0
2509 * @param offs1 The end offset >= 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
|