1. /*
  2. * @(#)AbstractDocument.java 1.116 01/03/16
  3. *
  4. * Copyright 1997-2001 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package javax.swing.text;
  11. import java.util.*;
  12. import java.io.*;
  13. import java.awt.font.TextAttribute;
  14. import javax.swing.UIManager;
  15. import javax.swing.undo.*;
  16. import javax.swing.event.ChangeListener;
  17. import javax.swing.event.*;
  18. import javax.swing.tree.TreeNode;
  19. /**
  20. * An implementation of the document interface to serve as a
  21. * basis for implementing various kinds of documents. At this
  22. * level there is very little policy, so there is a corresponding
  23. * increase in difficulty of use.
  24. * <p>
  25. * This class implements a locking mechanism for the document. It
  26. * allows multiple readers or one writer, and writers must wait until
  27. * all observers of the document have been notified of a previous
  28. * change before beginning another mutation to the document. The
  29. * read lock is aquired and released using the <code>render</code>
  30. * method. A write lock is aquired by the methods that mutate the
  31. * document, and are held for the duration of the method call.
  32. * Notification is done on the thread that produced the mutation,
  33. * and the thread has full read access to the document for the
  34. * duration of the notification, but other readers are kept out
  35. * until the notification has finished. The notification is a
  36. * beans event notification which does not allow any further
  37. * mutations until all listeners have been notified.
  38. * <p>
  39. * Any models subclassed from this class and used in conjunction
  40. * with a text component that has a look and feel implementation
  41. * that is derived from BasicTextUI may be safely updated
  42. * asynchronously, because all access to the View hierarchy
  43. * is serialized by BasicTextUI if the document is of type
  44. * <code>AbstractDocument</code>. The locking assumes that an
  45. * independant thread will access the View hierarchy only from
  46. * the DocumentListener methods, and that there will be only
  47. * one event thread active at a time.
  48. * <p>
  49. * If concurrency support is desired, there are the following
  50. * additional implications. The code path for any DocumentListener
  51. * implementation and any UndoListener implementation must be threadsafe,
  52. * and not access the component lock if trying to be safe from deadlocks.
  53. * The <code>repaint</code> and <code>revalidate</code> methods
  54. * on JComponent are safe.
  55. * <p>
  56. * AbstractDocument models an implied break at the end of the document.
  57. * Among other things this allows you to position the caret after the last
  58. * character. As a result of this, <code>getLength</code> returns one less
  59. * than the length of the Content. If you create your own Content, be
  60. * sure and initialize it to have an additional character. Refer to
  61. * StringContent and GapContent for examples of this. Another implication
  62. * of this is that Elements that model the implied end character will have
  63. * an endOffset == (getLength() + 1). For example, in DefaultStyledDocument
  64. * <code>getParagraphElement(getLength()).getEndOffset() == getLength() + 1
  65. * </code>.
  66. * <p>
  67. * <strong>Warning:</strong>
  68. * Serialized objects of this class will not be compatible with
  69. * future Swing releases. The current serialization support is appropriate
  70. * for short term storage or RMI between applications running the same
  71. * version of Swing. A future release of Swing will provide support for
  72. * long term persistence.
  73. *
  74. * @author Timothy Prinzing
  75. * @version 1.116 03/16/01
  76. */
  77. public abstract class AbstractDocument implements Document, Serializable {
  78. /**
  79. * Constructs a new AbstractDocument, wrapped around some
  80. * specified content storage mechanism.
  81. *
  82. * @param data the content
  83. */
  84. protected AbstractDocument(Content data) {
  85. this(data, StyleContext.getDefaultStyleContext());
  86. }
  87. /**
  88. * Constructs a new AbstractDocument, wrapped around some
  89. * specified content storage mechanism.
  90. *
  91. * @param data the content
  92. * @param context the attribute context
  93. */
  94. protected AbstractDocument(Content data, AttributeContext context) {
  95. this.data = data;
  96. this.context = context;
  97. bidiRoot = new BidiRootElement();
  98. if (defaultI18NProperty == null) {
  99. // determine default setting for i18n support
  100. Object o = java.security.AccessController.doPrivileged(
  101. new java.security.PrivilegedAction() {
  102. public Object run() {
  103. return System.getProperty(I18NProperty);
  104. }
  105. }
  106. );
  107. if (o != null) {
  108. defaultI18NProperty = Boolean.valueOf((String)o);
  109. } else {
  110. defaultI18NProperty = Boolean.FALSE;
  111. }
  112. }
  113. putProperty( I18NProperty, defaultI18NProperty);
  114. //REMIND(bcb) This creates an initial bidi element to account for
  115. //the \n that exists by default in the content. Doing it this way
  116. //seems to expose a little too much knowledge of the content given
  117. //to us by the sub-class. Consider having the sub-class' constructor
  118. //make an initial call to insertUpdate.
  119. writeLock();
  120. try {
  121. Element[] p = new Element[1];
  122. p[0] = new BidiElement( bidiRoot, 0, 1, 0 );
  123. bidiRoot.replace(0,0,p);
  124. } finally {
  125. writeUnlock();
  126. }
  127. }
  128. /**
  129. * Support for managing a set of properties. Callers
  130. * can use the documentProperties dictionary to annotate the
  131. * document with document-wide properties.
  132. *
  133. * @return a non null Dictionary
  134. * @see #setDocumentProperties
  135. */
  136. public Dictionary getDocumentProperties() {
  137. if (documentProperties == null) {
  138. documentProperties = new Hashtable(2);
  139. }
  140. return documentProperties;
  141. }
  142. /**
  143. * Replace the document properties dictionary for this document.
  144. *
  145. * @param x the new dictionary
  146. * @see #getDocumentProperties
  147. */
  148. public void setDocumentProperties(Dictionary x) {
  149. documentProperties = x;
  150. }
  151. /**
  152. * Notifies all listeners that have registered interest for
  153. * notification on this event type. The event instance
  154. * is lazily created using the parameters passed into
  155. * the fire method.
  156. *
  157. * @param e the event
  158. * @see EventListenerList
  159. */
  160. protected void fireInsertUpdate(DocumentEvent e) {
  161. // Guaranteed to return a non-null array
  162. Object[] listeners = listenerList.getListenerList();
  163. // Process the listeners last to first, notifying
  164. // those that are interested in this event
  165. for (int i = listeners.length-2; i>=0; i-=2) {
  166. if (listeners[i]==DocumentListener.class) {
  167. // Lazily create the event:
  168. // if (e == null)
  169. // e = new ListSelectionEvent(this, firstIndex, lastIndex);
  170. ((DocumentListener)listeners[i+1]).insertUpdate(e);
  171. }
  172. }
  173. }
  174. /**
  175. * Notifies all listeners that have registered interest for
  176. * notification on this event type. The event instance
  177. * is lazily created using the parameters passed into
  178. * the fire method.
  179. *
  180. * @param e the event
  181. * @see EventListenerList
  182. */
  183. protected void fireChangedUpdate(DocumentEvent e) {
  184. // Guaranteed to return a non-null array
  185. Object[] listeners = listenerList.getListenerList();
  186. // Process the listeners last to first, notifying
  187. // those that are interested in this event
  188. for (int i = listeners.length-2; i>=0; i-=2) {
  189. if (listeners[i]==DocumentListener.class) {
  190. // Lazily create the event:
  191. // if (e == null)
  192. // e = new ListSelectionEvent(this, firstIndex, lastIndex);
  193. ((DocumentListener)listeners[i+1]).changedUpdate(e);
  194. }
  195. }
  196. }
  197. /**
  198. * Notifies all listeners that have registered interest for
  199. * notification on this event type. The event instance
  200. * is lazily created using the parameters passed into
  201. * the fire method.
  202. *
  203. * @param e the event
  204. * @see EventListenerList
  205. */
  206. protected void fireRemoveUpdate(DocumentEvent e) {
  207. // Guaranteed to return a non-null array
  208. Object[] listeners = listenerList.getListenerList();
  209. // Process the listeners last to first, notifying
  210. // those that are interested in this event
  211. for (int i = listeners.length-2; i>=0; i-=2) {
  212. if (listeners[i]==DocumentListener.class) {
  213. // Lazily create the event:
  214. // if (e == null)
  215. // e = new ListSelectionEvent(this, firstIndex, lastIndex);
  216. ((DocumentListener)listeners[i+1]).removeUpdate(e);
  217. }
  218. }
  219. }
  220. /**
  221. * Notifies all listeners that have registered interest for
  222. * notification on this event type. The event instance
  223. * is lazily created using the parameters passed into
  224. * the fire method.
  225. *
  226. * @param e the event
  227. * @see EventListenerList
  228. */
  229. protected void fireUndoableEditUpdate(UndoableEditEvent e) {
  230. // Guaranteed to return a non-null array
  231. Object[] listeners = listenerList.getListenerList();
  232. // Process the listeners last to first, notifying
  233. // those that are interested in this event
  234. for (int i = listeners.length-2; i>=0; i-=2) {
  235. if (listeners[i]==UndoableEditListener.class) {
  236. // Lazily create the event:
  237. // if (e == null)
  238. // e = new ListSelectionEvent(this, firstIndex, lastIndex);
  239. ((UndoableEditListener)listeners[i+1]).undoableEditHappened(e);
  240. }
  241. }
  242. }
  243. /**
  244. * Return an array of all the listeners of the given type that
  245. * were added to this model.
  246. *
  247. * @returns all of the objects recieving <em>listenerType</em> notifications
  248. * from this model
  249. *
  250. * @since 1.3
  251. */
  252. public EventListener[] getListeners(Class listenerType) {
  253. return listenerList.getListeners(listenerType);
  254. }
  255. /**
  256. * Get the asynchronous loading priority. If less than zero,
  257. * the document should not be loaded asynchronously.
  258. */
  259. public int getAsynchronousLoadPriority() {
  260. Integer loadPriority = (Integer)
  261. getProperty(AbstractDocument.AsyncLoadPriority);
  262. if (loadPriority != null) {
  263. return loadPriority.intValue();
  264. }
  265. return -1;
  266. }
  267. /**
  268. * Set the asynchronous loading priority.
  269. */
  270. public void setAsynchronousLoadPriority(int p) {
  271. Integer loadPriority = (p >= 0) ? new Integer(p) : null;
  272. putProperty(AbstractDocument.AsyncLoadPriority, loadPriority);
  273. }
  274. // --- Document methods -----------------------------------------
  275. /**
  276. * This allows the model to be safely rendered in the presence
  277. * of currency, if the model supports being updated asynchronously.
  278. * The given runnable will be executed in a way that allows it
  279. * to safely read the model with no changes while the runnable
  280. * is being executed. The runnable itself may <em>not</em>
  281. * make any mutations.
  282. * <p>
  283. * This is implemented to aquire a read lock for the duration
  284. * of the runnables execution. There may be multiple runnables
  285. * executing at the same time, and all writers will be blocked
  286. * while there are active rendering runnables. If the runnable
  287. * throws an exception, its lock will be safely released.
  288. * There is no protection against a runnable that never exits,
  289. * which will effectively leave the document locked for it's
  290. * lifetime.
  291. * <p>
  292. * If the given runnable attempts to make any mutations in
  293. * this implementation, a deadlock will occur. There is
  294. * no tracking of individual rendering threads to enable
  295. * detecting this situation, but a subclass could incur
  296. * the overhead of tracking them and throwing an error.
  297. * <p>
  298. * This method is thread safe, although most Swing methods
  299. * are not. Please see
  300. * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  301. * and Swing</A> for more information.
  302. *
  303. * @param r the renderer to execute.
  304. */
  305. public void render(Runnable r) {
  306. readLock();
  307. try {
  308. r.run();
  309. } finally {
  310. readUnlock();
  311. }
  312. }
  313. /**
  314. * Returns the length of the data. This is the number of
  315. * characters of content that represents the users data.
  316. *
  317. * @return the length >= 0
  318. * @see Document#getLength
  319. */
  320. public int getLength() {
  321. return data.length() - 1;
  322. }
  323. /**
  324. * Adds a document listener for notification of any changes.
  325. *
  326. * @param listener the listener
  327. * @see Document#addDocumentListener
  328. */
  329. public void addDocumentListener(DocumentListener listener) {
  330. listenerList.add(DocumentListener.class, listener);
  331. }
  332. /**
  333. * Removes a document listener.
  334. *
  335. * @param listener the listener
  336. * @see Document#removeDocumentListener
  337. */
  338. public void removeDocumentListener(DocumentListener listener) {
  339. listenerList.remove(DocumentListener.class, listener);
  340. }
  341. /**
  342. * Adds an undo listener for notification of any changes.
  343. * Undo/Redo operations performed on the UndoableEdit will
  344. * cause the appropriate DocumentEvent to be fired to keep
  345. * the view(s) in sync with the model.
  346. *
  347. * @param listener the listener
  348. * @see Document#addUndoableEditListener
  349. */
  350. public void addUndoableEditListener(UndoableEditListener listener) {
  351. listenerList.add(UndoableEditListener.class, listener);
  352. }
  353. /**
  354. * Removes an undo listener.
  355. *
  356. * @param listener the listener
  357. * @see Document#removeDocumentListener
  358. */
  359. public void removeUndoableEditListener(UndoableEditListener listener) {
  360. listenerList.remove(UndoableEditListener.class, listener);
  361. }
  362. /**
  363. * A convenience method for looking up a property value. It is
  364. * equivalent to:
  365. * <pre>
  366. * getDocumentProperties().get(key);
  367. * </pre>
  368. *
  369. * @param key the non-null property key
  370. * @return the value of this property or null
  371. * @see #getDocumentProperties
  372. */
  373. public final Object getProperty(Object key) {
  374. return getDocumentProperties().get(key);
  375. }
  376. /**
  377. * A convenience method for storing up a property value. It is
  378. * equivalent to:
  379. * <pre>
  380. * getDocumentProperties().put(key, value);
  381. * </pre>
  382. * If value is null this method will remove the property
  383. *
  384. * @param key the non-null key
  385. * @param value the value
  386. * @see #getDocumentProperties
  387. */
  388. public final void putProperty(Object key, Object value) {
  389. if (value != null) {
  390. getDocumentProperties().put(key, value);
  391. } else {
  392. getDocumentProperties().remove(key);
  393. }
  394. if( key == TextAttribute.RUN_DIRECTION
  395. && Boolean.TRUE.equals(getProperty(I18NProperty)) )
  396. {
  397. //REMIND - this needs to flip on the i18n property if run dir
  398. //is rtl and the i18n property is not already on.
  399. writeLock();
  400. try {
  401. DefaultDocumentEvent e
  402. = new DefaultDocumentEvent(0, getLength(),
  403. DocumentEvent.EventType.INSERT);
  404. updateBidi( e );
  405. } finally {
  406. writeUnlock();
  407. }
  408. }
  409. }
  410. /**
  411. * Removes some content from the document.
  412. * Removing content causes a write lock to be held while the
  413. * actual changes are taking place. Observers are notified
  414. * of the change on the thread that called this method.
  415. * <p>
  416. * This method is thread safe, although most Swing methods
  417. * are not. Please see
  418. * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  419. * and Swing</A> for more information.
  420. *
  421. * @param offs the starting offset >= 0
  422. * @param len the number of characters to remove >= 0
  423. * @exception BadLocationException the given remove position is not a valid
  424. * position within the document
  425. * @see Document#remove
  426. */
  427. public void remove(int offs, int len) throws BadLocationException {
  428. if (len > 0) {
  429. writeLock();
  430. try {
  431. DefaultDocumentEvent chng =
  432. new DefaultDocumentEvent(offs, len, DocumentEvent.EventType.REMOVE);
  433. boolean isComposedTextElement = false;
  434. // Check whether the position of interest is the composed text
  435. Element elem = getDefaultRootElement();
  436. while (!elem.isLeaf()) {
  437. elem = elem.getElement(elem.getElementIndex(offs));
  438. }
  439. isComposedTextElement = Utilities.isComposedTextElement(elem);
  440. removeUpdate(chng);
  441. UndoableEdit u = data.remove(offs, len);
  442. if (u != null) {
  443. chng.addEdit(u);
  444. }
  445. postRemoveUpdate(chng);
  446. // Mark the edit as done.
  447. chng.end();
  448. fireRemoveUpdate(chng);
  449. // only fire undo if Content implementation supports it
  450. // undo for the composed text is not supported for now
  451. if ((u != null) && !isComposedTextElement) {
  452. fireUndoableEditUpdate(new UndoableEditEvent(this, chng));
  453. }
  454. } finally {
  455. writeUnlock();
  456. }
  457. }
  458. }
  459. /**
  460. * Inserts some content into the document.
  461. * Inserting content causes a write lock to be held while the
  462. * actual changes are taking place, followed by notification
  463. * to the observers on the thread that grabbed the write lock.
  464. * <p>
  465. * This method is thread safe, although most Swing methods
  466. * are not. Please see
  467. * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  468. * and Swing</A> for more information.
  469. *
  470. * @param offs the starting offset >= 0
  471. * @param str the string to insert; does nothing with null/empty strings
  472. * @param a the attributes for the inserted content
  473. * @exception BadLocationException the given insert position is not a valid
  474. * position within the document
  475. * @see Document#insertString
  476. */
  477. public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
  478. if ((str == null) || (str.length() == 0)) {
  479. return;
  480. }
  481. writeLock();
  482. try {
  483. UndoableEdit u = data.insertString(offs, str);
  484. DefaultDocumentEvent e =
  485. new DefaultDocumentEvent(offs, str.length(), DocumentEvent.EventType.INSERT);
  486. if (u != null) {
  487. e.addEdit(u);
  488. }
  489. // see if complex glyph layout support is needed
  490. if( getProperty(I18NProperty).equals( Boolean.FALSE ) ) {
  491. // if a default direction of right-to-left has been specified,
  492. // we want complex layout even if the text is all left to right.
  493. Object d = getProperty(TextAttribute.RUN_DIRECTION);
  494. if ((d != null) && (d.equals(TextAttribute.RUN_DIRECTION_RTL))) {
  495. putProperty( I18NProperty, Boolean.TRUE);
  496. } else {
  497. int len = str.length();
  498. for (int i = 0; i < len; i++) {
  499. char c = str.charAt(i);
  500. if (Bidi.requiresBidi(c)) {
  501. putProperty( I18NProperty, Boolean.TRUE);
  502. break;
  503. }
  504. }
  505. }
  506. }
  507. insertUpdate(e, a);
  508. // Mark the edit as done.
  509. e.end();
  510. fireInsertUpdate(e);
  511. // only fire undo if Content implementation supports it
  512. // undo for the composed text is not supported for now
  513. if (u != null &&
  514. (a == null || !a.isDefined(StyleConstants.ComposedTextAttribute))) {
  515. fireUndoableEditUpdate(new UndoableEditEvent(this, e));
  516. }
  517. } finally {
  518. writeUnlock();
  519. }
  520. }
  521. /**
  522. * Gets a sequence of text from the document.
  523. *
  524. * @param offset the starting offset >= 0
  525. * @param length the number of characters to retrieve >= 0
  526. * @return the text
  527. * @exception BadLocationException the range given includes a position
  528. * that is not a valid position within the document
  529. * @see Document#getText
  530. */
  531. public String getText(int offset, int length) throws BadLocationException {
  532. if (length < 0) {
  533. throw new BadLocationException("Length must be positive", length);
  534. }
  535. String str = data.getString(offset, length);
  536. return str;
  537. }
  538. /**
  539. * Gets some text from the document potentially without
  540. * making a copy. The character array returned in the
  541. * given <code>Segment</code> should never be mutated.
  542. * This kind of access to the characters of the document
  543. * is provided to help make the rendering potentially more
  544. * efficient. The caller should make no assumptions about
  545. * the lifetime of the returned character array, which
  546. * should be copied if needed beyond the use for rendering.
  547. *
  548. * @param offset the starting offset >= 0
  549. * @param length the number of characters to retrieve >= 0
  550. * @param txt the Segment object to retrieve the text into
  551. * @exception BadLocationException the range given includes a position
  552. * that is not a valid position within the document
  553. */
  554. public void getText(int offset, int length, Segment txt) throws BadLocationException {
  555. if (length < 0) {
  556. throw new BadLocationException("Length must be positive", length);
  557. }
  558. data.getChars(offset, length, txt);
  559. }
  560. /**
  561. * Returns a position that will track change as the document
  562. * is altered.
  563. * <p>
  564. * This method is thread safe, although most Swing methods
  565. * are not. Please see
  566. * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  567. * and Swing</A> for more information.
  568. *
  569. * @param offs the position in the model >= 0
  570. * @return the position
  571. * @exception BadLocationException if the given position does not
  572. * represent a valid location in the associated document
  573. * @see Document#createPosition
  574. */
  575. public synchronized Position createPosition(int offs) throws BadLocationException {
  576. return data.createPosition(offs);
  577. }
  578. /**
  579. * Returns a position that represents the start of the document. The
  580. * position returned can be counted on to track change and stay
  581. * located at the beginning of the document.
  582. *
  583. * @return the position
  584. */
  585. public final Position getStartPosition() {
  586. Position p;
  587. try {
  588. p = createPosition(0);
  589. } catch (BadLocationException bl) {
  590. p = null;
  591. }
  592. return p;
  593. }
  594. /**
  595. * Returns a position that represents the end of the document. The
  596. * position returned can be counted on to track change and stay
  597. * located at the end of the document.
  598. *
  599. * @return the position
  600. */
  601. public final Position getEndPosition() {
  602. Position p;
  603. try {
  604. p = createPosition(data.length());
  605. } catch (BadLocationException bl) {
  606. p = null;
  607. }
  608. return p;
  609. }
  610. /**
  611. * Gets all root elements defined. Typically, there
  612. * will only be one so the default implementation
  613. * is to return the default root element.
  614. *
  615. * @return the root element
  616. */
  617. public Element[] getRootElements() {
  618. Element[] elems = new Element[2];
  619. elems[0] = getDefaultRootElement();
  620. elems[1] = getBidiRootElement();
  621. return elems;
  622. }
  623. /**
  624. * Returns the root element that views should be based upon
  625. * unless some other mechanism for assigning views to element
  626. * structures is provided.
  627. *
  628. * @return the root element
  629. * @see Document#getDefaultRootElement
  630. */
  631. public abstract Element getDefaultRootElement();
  632. // ---- local methods -----------------------------------------
  633. /**
  634. * Returns the root element of the bidirectional structure for this
  635. * document. Its children represent character runs with a given
  636. * Unicode bidi level.
  637. */
  638. public Element getBidiRootElement() {
  639. return bidiRoot;
  640. }
  641. /**
  642. * Returns true if the text in the range <code>p0</code> to
  643. * <code>p1</code> is left to right.
  644. */
  645. boolean isLeftToRight(int p0, int p1) {
  646. if(!getProperty(I18NProperty).equals(Boolean.TRUE)) {
  647. return true;
  648. }
  649. Element bidiRoot = getBidiRootElement();
  650. int index = bidiRoot.getElementIndex(p0);
  651. Element bidiElem = bidiRoot.getElement(index);
  652. if(bidiElem.getEndOffset() >= p1) {
  653. AttributeSet bidiAttrs = bidiElem.getAttributes();
  654. return ((StyleConstants.getBidiLevel(bidiAttrs) % 2) == 0);
  655. }
  656. return true;
  657. }
  658. /**
  659. * Get the paragraph element containing the given position. Sub-classes
  660. * must define for themselves what exactly constitutes a paragraph. They
  661. * should keep in mind however that a paragraph should at least be the
  662. * unit of text over which to run the Unicode bidirectional algorithm.
  663. *
  664. * @param pos the starting offset >= 0
  665. * @return the element */
  666. public abstract Element getParagraphElement(int pos);
  667. /**
  668. * Fetches the context for managing attributes. This
  669. * method effectively establishes the strategy used
  670. * for compressing AttributeSet information.
  671. *
  672. * @return the context
  673. */
  674. protected final AttributeContext getAttributeContext() {
  675. return context;
  676. }
  677. /**
  678. * Updates document structure as a result of text insertion. This
  679. * will happen within a write lock. If a subclass of
  680. * this class reimplements this method, it should delegate to the
  681. * superclass as well.
  682. *
  683. * @param chng a description of the change
  684. * @param attr the attributes for the change
  685. */
  686. protected void insertUpdate(DefaultDocumentEvent chng, AttributeSet attr) {
  687. if( getProperty(I18NProperty).equals( Boolean.TRUE ) )
  688. updateBidi( chng );
  689. // Check if a multi byte is encountered in the inserted text.
  690. if (chng.type == DocumentEvent.EventType.INSERT &&
  691. chng.getLength() > 0 &&
  692. !Boolean.TRUE.equals(getProperty(MultiByteProperty))) {
  693. if (segment == null) {
  694. segment = new Segment();
  695. }
  696. try {
  697. getText(chng.getOffset(), chng.getLength(), segment);
  698. segment.first();
  699. do {
  700. if ((int)segment.current() > 255) {
  701. putProperty(MultiByteProperty, Boolean.TRUE);
  702. break;
  703. }
  704. } while (segment.next() != Segment.DONE);
  705. } catch (BadLocationException ble) {
  706. // Should never happen
  707. }
  708. }
  709. }
  710. /**
  711. * Updates any document structure as a result of text removal. This
  712. * method is called before the text is actually removed from the Content.
  713. * This will happen within a write lock. If a subclass
  714. * of this class reimplements this method, it should delegate to the
  715. * superclass as well.
  716. *
  717. * @param chng a description of the change
  718. */
  719. protected void removeUpdate(DefaultDocumentEvent chng) {
  720. }
  721. /**
  722. * Updates any document structure as a result of text removal. This
  723. * method is called after the text has been removed from the Content.
  724. * This will happen within a write lock. If a subclass
  725. * of this class reimplements this method, it should delegate to the
  726. * superclass as well.
  727. *
  728. * @param chng a description of the change
  729. */
  730. protected void postRemoveUpdate(DefaultDocumentEvent chng) {
  731. if( getProperty(I18NProperty).equals( Boolean.TRUE ) )
  732. updateBidi( chng );
  733. }
  734. /**
  735. * Update the bidi element structure as a result of the given change
  736. * to the document. The given change will be updated to reflect the
  737. * changes made to the bidi structure.
  738. *
  739. * This method assumes that every offset in the model is contained in
  740. * exactly one paragraph. This method also assumes that it is called
  741. * after the change is made to the default element structure.
  742. */
  743. void updateBidi( DefaultDocumentEvent chng ) {
  744. // Calculate the range of paragraphs affected by the change.
  745. int firstPStart;
  746. int lastPEnd;
  747. if( chng.type == DocumentEvent.EventType.INSERT
  748. || chng.type == DocumentEvent.EventType.CHANGE )
  749. {
  750. int chngStart = chng.getOffset();
  751. int chngEnd = chngStart + chng.getLength();
  752. firstPStart = getParagraphElement(chngStart).getStartOffset();
  753. lastPEnd = getParagraphElement(chngEnd).getEndOffset();
  754. } else if( chng.type == DocumentEvent.EventType.REMOVE ) {
  755. Element paragraph = getParagraphElement( chng.getOffset() );
  756. firstPStart = paragraph.getStartOffset();
  757. lastPEnd = paragraph.getEndOffset();
  758. } else {
  759. throw new Error("Internal error: unknown event type.");
  760. }
  761. //System.out.println("updateBidi: firstPStart = " + firstPStart + " lastPEnd = " + lastPEnd );
  762. // Calculate the bidi levels for the affected range of paragraphs. The
  763. // levels array will contain a bidi level for each character in the
  764. // affected text.
  765. byte levels[] = calculateBidiLevels( firstPStart, lastPEnd );
  766. Vector newElements = new Vector();
  767. // Calculate the first span of characters in the affected range with
  768. // the same bidi level. If this level is the same as the level of the
  769. // previous bidi element (the existing bidi element containing
  770. // firstPStart-1), then merge in the previous element. If not, but
  771. // the previous element overlaps the affected range, truncate the
  772. // previous element at firstPStart.
  773. int firstSpanStart = firstPStart;
  774. int removeFromIndex = 0;
  775. if( firstSpanStart > 0 ) {
  776. int prevElemIndex = bidiRoot.getElementIndex(firstPStart-1);
  777. removeFromIndex = prevElemIndex;
  778. Element prevElem = bidiRoot.getElement(prevElemIndex);
  779. int prevLevel=StyleConstants.getBidiLevel(prevElem.getAttributes());
  780. //System.out.println("createbidiElements: prevElem= " + prevElem + " prevLevel= " + prevLevel + "level[0] = " + levels[0]);
  781. if( prevLevel==levels[0] ) {
  782. firstSpanStart = prevElem.getStartOffset();
  783. } else if( prevElem.getEndOffset() > firstPStart ) {
  784. newElements.addElement(new BidiElement(bidiRoot,
  785. prevElem.getStartOffset(),
  786. firstPStart, prevLevel));
  787. } else {
  788. removeFromIndex++;
  789. }
  790. }
  791. int firstSpanEnd = 0;
  792. while((firstSpanEnd<levels.length) && (levels[firstSpanEnd]==levels[0]))
  793. firstSpanEnd++;
  794. // Calculate the last span of characters in the affected range with
  795. // the same bidi level. If this level is the same as the level of the
  796. // next bidi element (the existing bidi element containing lastPEnd),
  797. // then merge in the next element. If not, but the next element
  798. // overlaps the affected range, adjust the next element to start at
  799. // lastPEnd.
  800. int lastSpanEnd = lastPEnd;
  801. Element newNextElem = null;
  802. int removeToIndex = bidiRoot.getElementCount() - 1;
  803. if( lastSpanEnd <= getLength() ) {
  804. int nextElemIndex = bidiRoot.getElementIndex( lastPEnd );
  805. removeToIndex = nextElemIndex;
  806. Element nextElem = bidiRoot.getElement( nextElemIndex );
  807. int nextLevel = StyleConstants.getBidiLevel(nextElem.getAttributes());
  808. if( nextLevel == levels[levels.length-1] ) {
  809. lastSpanEnd = nextElem.getEndOffset();
  810. } else if( nextElem.getStartOffset() < lastPEnd ) {
  811. newNextElem = new BidiElement(bidiRoot, lastPEnd,
  812. nextElem.getEndOffset(),
  813. nextLevel);
  814. } else {
  815. removeToIndex--;
  816. }
  817. }
  818. int lastSpanStart = levels.length;
  819. while( (lastSpanStart>firstSpanEnd)
  820. && (levels[lastSpanStart-1]==levels[levels.length-1]) )
  821. lastSpanStart--;
  822. // If the first and last spans are contiguous and have the same level,
  823. // merge them and create a single new element for the entire span.
  824. // Otherwise, create elements for the first and last spans as well as
  825. // any spans in between.
  826. if((firstSpanEnd==lastSpanStart)&&(levels[0]==levels[levels.length-1])){
  827. newElements.addElement(new BidiElement(bidiRoot, firstSpanStart,
  828. lastSpanEnd, levels[0]));
  829. } else {
  830. // Create an element for the first span.
  831. newElements.addElement(new BidiElement(bidiRoot, firstSpanStart,
  832. firstSpanEnd+firstPStart,
  833. levels[0]));
  834. // Create elements for the spans in between the first and last
  835. for( int i=firstSpanEnd; i<lastSpanStart; ) {
  836. //System.out.println("executed line 872");
  837. int j;
  838. for( j=i; (j<levels.length) && (levels[j] == levels[i]); j++ );
  839. newElements.addElement(new BidiElement(bidiRoot, firstPStart+i,
  840. firstPStart+j,
  841. (int)levels[i]));
  842. i=j;
  843. }
  844. // Create an element for the last span.
  845. newElements.addElement(new BidiElement(bidiRoot,
  846. lastSpanStart+firstPStart,
  847. lastSpanEnd,
  848. levels[levels.length-1]));
  849. }
  850. if( newNextElem != null )
  851. newElements.addElement( newNextElem );
  852. // Calculate the set of existing bidi elements which must be
  853. // removed.
  854. int removedElemCount = 0;
  855. if( bidiRoot.getElementCount() > 0 ) {
  856. removedElemCount = removeToIndex - removeFromIndex + 1;
  857. }
  858. Element[] removedElems = new Element[removedElemCount];
  859. for( int i=0; i<removedElemCount; i++ ) {
  860. removedElems[i] = bidiRoot.getElement(removeFromIndex+i);
  861. }
  862. Element[] addedElems = new Element[ newElements.size() ];
  863. newElements.copyInto( addedElems );
  864. // Update the change record.
  865. ElementEdit ee = new ElementEdit( bidiRoot, removeFromIndex,
  866. removedElems, addedElems );
  867. chng.addEdit( ee );
  868. // Update the bidi element structure.
  869. bidiRoot.replace( removeFromIndex, removedElems.length, addedElems );
  870. }
  871. /**
  872. * Calculate the levels array for a range of paragraphs.
  873. */
  874. private byte[] calculateBidiLevels( int firstPStart, int lastPEnd ) {
  875. byte levels[] = new byte[ lastPEnd - firstPStart ];
  876. int levelsEnd = 0;
  877. Boolean defaultDirection = null;
  878. Object d = getProperty(TextAttribute.RUN_DIRECTION);
  879. if (d instanceof Boolean) {
  880. defaultDirection = (Boolean) d;
  881. }
  882. // For each paragraph in the given range of paragraphs, get its
  883. // levels array and add it to the levels array for the entire span.
  884. for(int o=firstPStart; o<lastPEnd; ) {
  885. Element p = getParagraphElement( o );
  886. int pStart = p.getStartOffset();
  887. int pEnd = p.getEndOffset();
  888. // default run direction for the paragraph. This will be
  889. // null if there is no direction override specified (i.e.
  890. // the direction will be determined from the content).
  891. Boolean direction = defaultDirection;
  892. d = p.getAttributes().getAttribute(TextAttribute.RUN_DIRECTION);
  893. if (d instanceof Boolean) {
  894. direction = (Boolean) d;
  895. }
  896. //System.out.println("updateBidi: paragraph start = " + pStart + " paragraph end = " + pEnd);
  897. // Create a Bidi over this paragraph then get the level
  898. // array.
  899. String pText;
  900. try {
  901. pText = getText(pStart, pEnd-pStart);
  902. } catch (BadLocationException e ) {
  903. throw new Error("Internal error: " + e.toString());
  904. }
  905. // REMIND(bcb) we should really be using a Segment here.
  906. Bidi bidiAnalyzer;
  907. if (direction != null) {
  908. boolean ltr = direction.equals(TextAttribute.RUN_DIRECTION_LTR);
  909. bidiAnalyzer = new Bidi(pText.toCharArray(), ltr);
  910. } else {
  911. bidiAnalyzer = new Bidi( pText.toCharArray() );
  912. }
  913. byte[] pLevels = bidiAnalyzer.getLevels();
  914. System.arraycopy( pLevels, 0, levels, levelsEnd, pLevels.length );
  915. levelsEnd += pLevels.length;
  916. o = p.getEndOffset();
  917. }
  918. // REMIND(bcb) remove this code when debugging is done.
  919. if( levelsEnd != levels.length )
  920. throw new Error("levelsEnd assertion failed.");
  921. return levels;
  922. }
  923. /**
  924. * Gives a diagnostic dump.
  925. *
  926. * @param out the output stream
  927. */
  928. public void dump(PrintStream out) {
  929. Element root = getDefaultRootElement();
  930. if (root instanceof AbstractElement) {
  931. ((AbstractElement)root).dump(out, 0);
  932. }
  933. bidiRoot.dump(out,0);
  934. }
  935. /**
  936. * Gets the content for the document.
  937. *
  938. * @return the content
  939. */
  940. protected final Content getContent() {
  941. return data;
  942. }
  943. /**
  944. * Creates a document leaf element.
  945. * Hook through which elements are created to represent the
  946. * document structure. Because this implementation keeps
  947. * structure and content seperate, elements grow automatically
  948. * when content is extended so splits of existing elements
  949. * follow. The document itself gets to decide how to generate
  950. * elements to give flexibility in the type of elements used.
  951. *
  952. * @param parent the parent element
  953. * @param a the attributes for the element
  954. * @param p0 the beginning of the range >= 0
  955. * @param p1 the end of the range >= p0
  956. * @return the new element
  957. */
  958. protected Element createLeafElement(Element parent, AttributeSet a, int p0, int p1) {
  959. return new LeafElement(parent, a, p0, p1);
  960. }
  961. /**
  962. * Creates a document branch element, that can contain other elements.
  963. *
  964. * @param parent the parent element
  965. * @param a the attributes
  966. * @return the element
  967. */
  968. protected Element createBranchElement(Element parent, AttributeSet a) {
  969. return new BranchElement(parent, a);
  970. }
  971. // --- Document locking ----------------------------------
  972. /**
  973. * Fetches the current writing thread if there is one.
  974. * This can be used to distinguish whether a method is
  975. * being called as part of an existing modification or
  976. * if a lock needs to be acquired and a new transaction
  977. * started.
  978. *
  979. * @returns the thread actively modifying the document
  980. * or null if there are no modifications in progress
  981. */
  982. protected synchronized final Thread getCurrentWriter() {
  983. return currWriter;
  984. }
  985. /**
  986. * Acquires a lock to begin mutating the document this lock
  987. * protects. There can be no writing, notification of changes, or
  988. * reading going on in order to gain the lock.
  989. *
  990. * @exception IllegalStateException thrown on illegal lock
  991. * attempt. If the document is implemented properly, this can
  992. * only happen if a document listener attempts to mutate the
  993. * document. This situation violates the bean event model
  994. * where order of delivery is not guaranteed and all listeners
  995. * should be notified before further mutations are allowed.
  996. */
  997. protected synchronized final void writeLock() {
  998. try {
  999. while ((numReaders > 0) || (currWriter != null)) {
  1000. if (Thread.currentThread() == currWriter) {
  1001. // Assuming one doesn't do something wrong in a subclass
  1002. // this should only happen if a DocumentListener tries to
  1003. // mutate the document.
  1004. throw new IllegalStateException("Attempt to mutate in notification");
  1005. }
  1006. wait();
  1007. }
  1008. currWriter = Thread.currentThread();
  1009. } catch (InterruptedException e) {
  1010. throw new Error("Interrupted attempt to aquire write lock");
  1011. }
  1012. }
  1013. /**
  1014. * Releases the write lock held because the write
  1015. * operation is finished. This allows either a new
  1016. * writer or readers to aquire a lock.
  1017. */
  1018. protected synchronized final void writeUnlock() {
  1019. currWriter = null;
  1020. notifyAll();
  1021. }
  1022. /**
  1023. * Acquires a lock to begin reading some state from the
  1024. * document. There can be multiple readers at the same time.
  1025. * Writing blocks the readers until notification of the change
  1026. * to the listeners has been completed. This method should
  1027. * be used very carefully to avoid unintended compromise
  1028. * of the document. It should always be balanced with a
  1029. * <code>readUnlock</code>.
  1030. *
  1031. * @see #readUnlock
  1032. */
  1033. public synchronized final void readLock() {
  1034. try {
  1035. while (currWriter != null) {
  1036. if (currWriter == Thread.currentThread()) {
  1037. // writer has full read access.... may try to acquire
  1038. // lock in notification
  1039. return;
  1040. }
  1041. wait();
  1042. }
  1043. numReaders += 1;
  1044. } catch (InterruptedException e) {
  1045. throw new Error("Interrupted attempt to aquire read lock");
  1046. }
  1047. }
  1048. /**
  1049. * Does a read unlock. This signals that one
  1050. * of the readers is done. If there are no more readers
  1051. * then writing can begin again. This should be balanced
  1052. * with a readLock, and should occur in a finally statement
  1053. * so that the balance is guaranteed. The following is an
  1054. * example.
  1055. * <pre><code>
  1056. *   readLock();
  1057. *   try {
  1058. *   // do something
  1059. *   } finally {
  1060. *   readUnlock();
  1061. *   }
  1062. * </code></pre>
  1063. *
  1064. * @see #readLock
  1065. */
  1066. public synchronized final void readUnlock() {
  1067. if (currWriter == Thread.currentThread()) {
  1068. // writer has full read access.... may try to acquire
  1069. // lock in notification
  1070. return;
  1071. }
  1072. if (numReaders <= 0) {
  1073. throw new StateInvariantError(BAD_LOCK_STATE);
  1074. }
  1075. numReaders -= 1;
  1076. notify();
  1077. }
  1078. // --- serialization ---------------------------------------------
  1079. private void readObject(ObjectInputStream s)
  1080. throws ClassNotFoundException, IOException
  1081. {
  1082. s.defaultReadObject();
  1083. listenerList = new EventListenerList();
  1084. // Restore bidi structure
  1085. //REMIND(bcb) This creates an initial bidi element to account for
  1086. //the \n that exists by default in the content.
  1087. bidiRoot = new BidiRootElement();
  1088. try {
  1089. writeLock();
  1090. Element[] p = new Element[1];
  1091. p[0] = new BidiElement( bidiRoot, 0, 1, 0 );
  1092. bidiRoot.replace(0,0,p);
  1093. } finally {
  1094. writeUnlock();
  1095. }
  1096. // At this point bidi root is only partially correct. To fully
  1097. // restore it we need access to getDefaultRootElement. But, this
  1098. // is created by the subclass and at this point will be null. We
  1099. // thus use registerValidation.
  1100. s.registerValidation(new ObjectInputValidation() {
  1101. public void validateObject() {
  1102. try {
  1103. writeLock();
  1104. DefaultDocumentEvent e = new DefaultDocumentEvent
  1105. (0, getLength(),
  1106. DocumentEvent.EventType.INSERT);
  1107. updateBidi( e );
  1108. }
  1109. finally {
  1110. writeUnlock();
  1111. }
  1112. }
  1113. }, 0);
  1114. }
  1115. // ----- member variables ------------------------------------------
  1116. private transient int numReaders;
  1117. private transient Thread currWriter;
  1118. private static Boolean defaultI18NProperty;
  1119. /**
  1120. * Storage for document-wide properties.
  1121. */
  1122. private Dictionary documentProperties = null;
  1123. /**
  1124. * The event listener list for the document.
  1125. */
  1126. protected EventListenerList listenerList = new EventListenerList();
  1127. /**
  1128. * Where the text is actually stored, and a set of marks
  1129. * that track change as the document is edited are managed.
  1130. */
  1131. private Content data;
  1132. /**
  1133. * Factory for the attributes. This is the strategy for
  1134. * attribute compression and control of the lifetime of
  1135. * a set of attributes as a collection. This may be shared
  1136. * with other documents.
  1137. */
  1138. private AttributeContext context;
  1139. /**
  1140. * The root of the bidirectional structure for this document. Its children
  1141. * represent character runs with the same Unicode bidi level.
  1142. */
  1143. private transient BranchElement bidiRoot;
  1144. /**
  1145. * Segment used to test for multi byte condition.
  1146. */
  1147. private transient Segment segment;
  1148. private static final String BAD_LOCK_STATE = "document lock failure";
  1149. /**
  1150. * Error message to indicate a bad location.
  1151. */
  1152. protected static final String BAD_LOCATION = "document location failure";
  1153. /**
  1154. * Name of elements used to represent paragraphs
  1155. */
  1156. public static final String ParagraphElementName = "paragraph";
  1157. /**
  1158. * Name of elements used to represent content
  1159. */
  1160. public static final String ContentElementName = "content";
  1161. /**
  1162. * Name of elements used to hold sections (lines/paragraphs).
  1163. */
  1164. public static final String SectionElementName = "section";
  1165. /**
  1166. * Name of elements used to hold a unidirectional run
  1167. */
  1168. public static final String BidiElementName = "bidi level";
  1169. /**
  1170. * Name of the attribute used to specify element
  1171. * names.
  1172. */
  1173. public static final String ElementNameAttribute = "$ename";
  1174. /**
  1175. * Document property that indicates whether internationalization
  1176. * functions such as text reordering or reshaping should be
  1177. * performed. It is currently turned off while this code is
  1178. * being stabilized. It is also left as a package private for now
  1179. * until its long term benefit is decided.
  1180. */
  1181. static final String I18NProperty = "i18n";
  1182. /**
  1183. * Document property that indicates if a character has been inserted
  1184. * into the document that is more than one byte long. GlyphView uses
  1185. * this to determine if it should use BreakIterator.
  1186. */
  1187. static final Object MultiByteProperty = new Object();
  1188. /**
  1189. * Document property that indicates asynchronous loading is
  1190. * desired, with the thread priority given as the value.
  1191. */
  1192. static final String AsyncLoadPriority = "load priority";
  1193. /**
  1194. * Interface to describe a sequence of character content that
  1195. * can be edited. Implementations may or may not support a
  1196. * history mechanism which will be reflected by whether or not
  1197. * mutations return an UndoableEdit implementation.
  1198. * @see AbstractDocument
  1199. */
  1200. public interface Content {
  1201. /**
  1202. * Creates a position within the content that will
  1203. * track change as the content is mutated.
  1204. *
  1205. * @param offset the offset in the content >= 0
  1206. * @return a Position
  1207. * @exception BadLocationException for an invalid offset
  1208. */
  1209. public Position createPosition(int offset) throws BadLocationException;
  1210. /**
  1211. * Current length of the sequence of character content.
  1212. *
  1213. * @return the length >= 0
  1214. */
  1215. public int length();
  1216. /**
  1217. * Inserts a string of characters into the sequence.
  1218. *
  1219. * @param where Offset into the sequence to make the insertion >= 0.
  1220. * @param str String to insert.
  1221. * @return If the implementation supports a history mechansim,
  1222. * a reference to an Edit implementation will be returned,
  1223. * otherwise null.
  1224. * @exception BadLocationException Thrown if the area covered by
  1225. * the arguments is not contained in the character sequence.
  1226. */
  1227. public UndoableEdit insertString(int where, String str) throws BadLocationException;
  1228. /**
  1229. * Removes some portion of the sequence.
  1230. *
  1231. * @param where The offset into the sequence to make the
  1232. * insertion >= 0.
  1233. * @param nitems The number of items in the sequence to remove >= 0.
  1234. * @return If the implementation supports a history mechansim,
  1235. * a reference to an Edit implementation will be returned,
  1236. * otherwise null.
  1237. * @exception BadLocationException Thrown if the area covered by
  1238. * the arguments is not contained in the character sequence.
  1239. */
  1240. public UndoableEdit remove(int where, int nitems) throws BadLocationException;
  1241. /**
  1242. * Fetches a string of characters contained in the sequence.
  1243. *
  1244. * @param where Offset into the sequence to fetch >= 0.
  1245. * @param len number of characters to copy >= 0.
  1246. * @return the string
  1247. * @exception BadLocationException Thrown if the area covered by
  1248. * the arguments is not contained in the character sequence.
  1249. */
  1250. public String getString(int where, int len) throws BadLocationException;
  1251. /**
  1252. * Gets a sequence of characters and copies them into a Segment.
  1253. *
  1254. * @param where the starting offset >= 0
  1255. * @param len the number of characters >= 0
  1256. * @param txt the target location to copy into
  1257. * @exception BadLocationException Thrown if the area covered by
  1258. * the arguments is not contained in the character sequence.
  1259. */
  1260. public void getChars(int where, int len, Segment txt) throws BadLocationException;
  1261. }
  1262. /**
  1263. * An interface that can be used to allow MutableAttributeSet
  1264. * implementations to use pluggable attribute compression
  1265. * techniques. Each mutation of the attribute set can be
  1266. * used to exchange a previous AttributeSet instance with
  1267. * another, preserving the possibility of the AttributeSet
  1268. * remaining immutable. An implementation is provided by
  1269. * the StyleContext class.
  1270. *
  1271. * The Element implementations provided by this class use
  1272. * this interface to provide their MutableAttributeSet
  1273. * implementations, so that different AttributeSet compression
  1274. * techniques can be employed. The method
  1275. * <code>getAttributeContext</code> should be implemented to
  1276. * return the object responsible for implementing the desired
  1277. * compression technique.
  1278. *
  1279. * @see StyleContext
  1280. */
  1281. public interface AttributeContext {
  1282. /**
  1283. * Adds an attribute to the given set, and returns
  1284. * the new representative set.
  1285. *
  1286. * @param old the old attribute set
  1287. * @param name the non-null attribute name
  1288. * @param value the attribute value
  1289. * @return the updated attribute set
  1290. * @see MutableAttributeSet#addAttribute
  1291. */
  1292. public AttributeSet addAttribute(AttributeSet old, Object name, Object value);
  1293. /**
  1294. * Adds a set of attributes to the element.
  1295. *
  1296. * @param old the old attribute set
  1297. * @param attr the attributes to add
  1298. * @return the updated attribute set
  1299. * @see MutableAttributeSet#addAttribute
  1300. */
  1301. public AttributeSet addAttributes(AttributeSet old, AttributeSet attr);
  1302. /**
  1303. * Removes an attribute from the set.
  1304. *
  1305. * @param old the old attribute set
  1306. * @param name the non-null attribute name
  1307. * @return the updated attribute set
  1308. * @see MutableAttributeSet#removeAttribute
  1309. */
  1310. public AttributeSet removeAttribute(AttributeSet old, Object name);
  1311. /**
  1312. * Removes a set of attributes for the element.
  1313. *
  1314. * @param old the old attribute set
  1315. * @param names the attribute names
  1316. * @return the updated attribute set
  1317. * @see MutableAttributeSet#removeAttributes
  1318. */
  1319. public AttributeSet removeAttributes(AttributeSet old, Enumeration names);
  1320. /**
  1321. * Removes a set of attributes for the element.
  1322. *
  1323. * @param old the old attribute set
  1324. * @param attrs the attributes
  1325. * @return the updated attribute set
  1326. * @see MutableAttributeSet#removeAttributes
  1327. */
  1328. public AttributeSet removeAttributes(AttributeSet old, AttributeSet attrs);
  1329. /**
  1330. * Fetches an empty AttributeSet.
  1331. *
  1332. * @return the attribute set
  1333. */
  1334. public AttributeSet getEmptySet();
  1335. /**
  1336. * Reclaims an attribute set.
  1337. * This is a way for a MutableAttributeSet to mark that it no
  1338. * longer need a particular immutable set. This is only necessary
  1339. * in 1.1 where there are no weak references. A 1.1 implementation
  1340. * would call this in its finalize method.
  1341. *
  1342. * @param a the attribute set to reclaim
  1343. */
  1344. public void reclaim(AttributeSet a);
  1345. }
  1346. /**
  1347. * Implements the abstract part of an element. By default elements
  1348. * support attributes by having a field that represents the immutable
  1349. * part of the current attribute set for the element. The element itself
  1350. * implements MutableAttributeSet which can be used to modify the set
  1351. * by fetching a new immutable set. The immutable sets are provided
  1352. * by the AttributeContext associated with the document.
  1353. * <p>
  1354. * <strong