1. /*
  2. * @(#)StyleContext.java 1.58 01/11/29
  3. *
  4. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing.text;
  8. import java.awt.*;
  9. import java.util.*;
  10. import java.io.*;
  11. import javax.swing.SwingUtilities;
  12. import javax.swing.event.ChangeListener;
  13. import javax.swing.event.EventListenerList;
  14. import javax.swing.event.ChangeEvent;
  15. /**
  16. * A pool of styles and their associated resources. This class determines
  17. * the lifetime of a group of resources by being a container that holds
  18. * caches for various resources such as font and color that get reused
  19. * by the various style definitions. This can be shared by multiple
  20. * documents if desired to maximize the sharing of related resources.
  21. * <p>
  22. * This class also provides efficient support for small sets of attributes
  23. * and compresses them by sharing across uses and taking advantage of
  24. * their immutable nature. Since many styles are replicated, the potential
  25. * for sharing is significant, and copies can be extremely cheap.
  26. * Larger sets reduce the possibility of sharing, and therefore revert
  27. * automatically to a less space-efficient implementation.
  28. * <p>
  29. * <strong>Warning:</strong>
  30. * Serialized objects of this class will not be compatible with
  31. * future Swing releases. The current serialization support is appropriate
  32. * for short term storage or RMI between applications running the same
  33. * version of Swing. A future release of Swing will provide support for
  34. * long term persistence.
  35. *
  36. * @author Timothy Prinzing
  37. * @version 1.58 11/29/01
  38. */
  39. public class StyleContext implements Serializable, AbstractDocument.AttributeContext {
  40. /**
  41. * Returns default AttributeContext shared by all documents that
  42. * don't bother to define/supply their own context.
  43. *
  44. * @return the context
  45. */
  46. public static final StyleContext getDefaultStyleContext() {
  47. if (defaultContext == null) {
  48. defaultContext = new StyleContext();
  49. }
  50. return defaultContext;
  51. }
  52. private static StyleContext defaultContext;
  53. /**
  54. * Creates a new StyleContext object.
  55. */
  56. public StyleContext() {
  57. styles = new NamedStyle(null);
  58. addStyle(DEFAULT_STYLE, null);
  59. }
  60. /**
  61. * Adds a new style into the style hierarchy. Style attributes
  62. * resolve from bottom up so an attribute specified in a child
  63. * will override an attribute specified in the parent.
  64. *
  65. * @param nm the name of the style (must be unique within the
  66. * collection of named styles in the document). The name may
  67. * be null if the style is unnamed, but the caller is responsible
  68. * for managing the reference returned as an unnamed style can't
  69. * be fetched by name. An unnamed style may be useful for things
  70. * like character attribute overrides such as found in a style
  71. * run.
  72. * @param parent the parent style. This may be null if unspecified
  73. * attributes need not be resolved in some other style.
  74. * @return the created style
  75. */
  76. public Style addStyle(String nm, Style parent) {
  77. Style style = new NamedStyle(nm, parent);
  78. if (nm != null) {
  79. // add a named style, a class of attributes
  80. styles.addAttribute(nm, style);
  81. }
  82. return style;
  83. }
  84. /**
  85. * Removes a named style previously added to the document.
  86. *
  87. * @param nm the name of the style to remove
  88. */
  89. public void removeStyle(String nm) {
  90. styles.removeAttribute(nm);
  91. }
  92. /**
  93. * Fetches a named style previously added to the document
  94. *
  95. * @param nm the name of the style
  96. * @return the style
  97. */
  98. public Style getStyle(String nm) {
  99. return (Style) styles.getAttribute(nm);
  100. }
  101. /**
  102. * Fetches the names of the styles defined.
  103. *
  104. * @return the list of names as an enumeration
  105. */
  106. public Enumeration getStyleNames() {
  107. return styles.getAttributeNames();
  108. }
  109. /**
  110. * Adds a listener to track when styles are added
  111. * or removed.
  112. *
  113. * @param l the change listener
  114. */
  115. public void addChangeListener(ChangeListener l) {
  116. styles.addChangeListener(l);
  117. }
  118. /**
  119. * Removes a listener that was tracking styles being
  120. * added or removed.
  121. *
  122. * @param l the change listener
  123. */
  124. public void removeChangeListener(ChangeListener l) {
  125. styles.removeChangeListener(l);
  126. }
  127. /**
  128. * Gets the font from an attribute set. This is
  129. * implemented to try and fetch a cached font
  130. * for the given AttributeSet, and if that fails
  131. * the font features are resolved and the
  132. * font is fetched from the low-level font cache.
  133. *
  134. * @param attr the attribute set
  135. * @return the font
  136. */
  137. public Font getFont(AttributeSet attr) {
  138. // PENDING(prinz) add cache behavior
  139. int style = Font.PLAIN;
  140. if (StyleConstants.isBold(attr)) {
  141. style |= Font.BOLD;
  142. }
  143. if (StyleConstants.isItalic(attr)) {
  144. style |= Font.ITALIC;
  145. }
  146. String family = StyleConstants.getFontFamily(attr);
  147. int size = StyleConstants.getFontSize(attr);
  148. /**
  149. * if either superscript or subscript is
  150. * is set, we need to reduce the font size
  151. * by 2.
  152. */
  153. if (StyleConstants.isSuperscript(attr) ||
  154. StyleConstants.isSubscript(attr)) {
  155. size -= 2;
  156. }
  157. return getFont(family, style, size);
  158. }
  159. /**
  160. * Takes a set of attributes and turn it into a foreground color
  161. * specification. This might be used to specify things
  162. * like brighter, more hue, etc. By default it simply returns
  163. * the value specified by the StyleConstants.Foreground attribute.
  164. *
  165. * @param attr the set of attributes
  166. * @return the color
  167. */
  168. public Color getForeground(AttributeSet attr) {
  169. return StyleConstants.getForeground(attr);
  170. }
  171. /**
  172. * Takes a set of attributes and turn it into a background color
  173. * specification. This might be used to specify things
  174. * like brighter, more hue, etc. By default it simply returns
  175. * the value specified by the StyleConstants.Background attribute.
  176. *
  177. * @param attr the set of attributes
  178. * @return the color
  179. */
  180. public Color getBackground(AttributeSet attr) {
  181. return StyleConstants.getForeground(attr);
  182. }
  183. /**
  184. * Gets a new font. This returns a Font from a cache
  185. * if a cached font exists. If not, a Font is added to
  186. * the cache. This is basically a low-level cache for
  187. * 1.1 font features.
  188. *
  189. * @param family the font family (such as "Monospaced")
  190. * @param style the style of the font (such as Font.PLAIN)
  191. * @param size the point size >= 1
  192. * @return the new font
  193. */
  194. public Font getFont(String family, int style, int size) {
  195. fontSearch.setValue(family, style, size);
  196. Font f = (Font) fontTable.get(fontSearch);
  197. if (f == null) {
  198. // haven't seen this one yet.
  199. f = new Font(family, style, size);
  200. FontKey key = new FontKey(family, style, size);
  201. fontTable.put(key, f);
  202. }
  203. return f;
  204. }
  205. /**
  206. * Returns font metrics for a font.
  207. *
  208. * @param f the font
  209. * @return the metrics
  210. */
  211. public FontMetrics getFontMetrics(Font f) {
  212. // The Toolkit implementations cache, so we just forward
  213. // to the default toolkit.
  214. return Toolkit.getDefaultToolkit().getFontMetrics(f);
  215. }
  216. // --- AttributeContext methods --------------------
  217. /**
  218. * Adds an attribute to the given set, and returns
  219. * the new representative set.
  220. * <p>
  221. * This method is thread safe, although most Swing methods
  222. * are not. Please see
  223. * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  224. * and Swing</A> for more information.
  225. *
  226. * @param old the old attribute set
  227. * @param name the non-null attribute name
  228. * @param value the attribute value
  229. * @return the updated attribute set
  230. * @see MutableAttributeSet#addAttribute
  231. */
  232. public synchronized AttributeSet addAttribute(AttributeSet old, Object name, Object value) {
  233. if ((old.getAttributeCount() + 1) <= getCompressionThreshold()) {
  234. // build a search key and find/create an immutable and unique
  235. // set.
  236. search.removeAttributes(search);
  237. search.addAttributes(old);
  238. search.addAttribute(name, value);
  239. reclaim(old);
  240. return getImmutableUniqueSet();
  241. }
  242. MutableAttributeSet ma = getMutableAttributeSet(old);
  243. ma.addAttribute(name, value);
  244. return ma;
  245. }
  246. /**
  247. * Adds a set of attributes to the element.
  248. * <p>
  249. * This method is thread safe, although most Swing methods
  250. * are not. Please see
  251. * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  252. * and Swing</A> for more information.
  253. *
  254. * @param old the old attribute set
  255. * @param attr the attributes to add
  256. * @return the updated attribute set
  257. * @see MutableAttributeSet#addAttribute
  258. */
  259. public synchronized AttributeSet addAttributes(AttributeSet old, AttributeSet attr) {
  260. if ((old.getAttributeCount() + attr.getAttributeCount()) <= getCompressionThreshold()) {
  261. // build a search key and find/create an immutable and unique
  262. // set.
  263. search.removeAttributes(search);
  264. search.addAttributes(old);
  265. search.addAttributes(attr);
  266. reclaim(old);
  267. return getImmutableUniqueSet();
  268. }
  269. MutableAttributeSet ma = getMutableAttributeSet(old);
  270. ma.addAttributes(attr);
  271. return ma;
  272. }
  273. /**
  274. * Removes an attribute from the set.
  275. * <p>
  276. * This method is thread safe, although most Swing methods
  277. * are not. Please see
  278. * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  279. * and Swing</A> for more information.
  280. *
  281. * @param old the old set of attributes
  282. * @param name the non-null attribute name
  283. * @return the updated attribute set
  284. * @see MutableAttributeSet#removeAttribute
  285. */
  286. public synchronized AttributeSet removeAttribute(AttributeSet old, Object name) {
  287. if ((old.getAttributeCount() - 1) <= getCompressionThreshold()) {
  288. // build a search key and find/create an immutable and unique
  289. // set.
  290. search.removeAttributes(search);
  291. search.addAttributes(old);
  292. search.removeAttribute(name);
  293. reclaim(old);
  294. return getImmutableUniqueSet();
  295. }
  296. MutableAttributeSet ma = getMutableAttributeSet(old);
  297. ma.removeAttribute(name);
  298. return ma;
  299. }
  300. /**
  301. * Removes a set of attributes for the element.
  302. * <p>
  303. * This method is thread safe, although most Swing methods
  304. * are not. Please see
  305. * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  306. * and Swing</A> for more information.
  307. *
  308. * @param old the old attribute set
  309. * @param names the attribute names
  310. * @return the updated attribute set
  311. * @see MutableAttributeSet#removeAttributes
  312. */
  313. public synchronized AttributeSet removeAttributes(AttributeSet old, Enumeration names) {
  314. if (old.getAttributeCount() <= getCompressionThreshold()) {
  315. // build a search key and find/create an immutable and unique
  316. // set.
  317. search.removeAttributes(search);
  318. search.addAttributes(old);
  319. search.removeAttributes(names);
  320. reclaim(old);
  321. return getImmutableUniqueSet();
  322. }
  323. MutableAttributeSet ma = getMutableAttributeSet(old);
  324. ma.removeAttributes(names);
  325. return ma;
  326. }
  327. /**
  328. * Removes a set of attributes for the element.
  329. * <p>
  330. * This method is thread safe, although most Swing methods
  331. * are not. Please see
  332. * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  333. * and Swing</A> for more information.
  334. *
  335. * @param old the old attribute set
  336. * @param attrs the attributes
  337. * @return the updated attribute set
  338. * @see MutableAttributeSet#removeAttributes
  339. */
  340. public synchronized AttributeSet removeAttributes(AttributeSet old, AttributeSet attrs) {
  341. if (old.getAttributeCount() <= getCompressionThreshold()) {
  342. // build a search key and find/create an immutable and unique
  343. // set.
  344. search.removeAttributes(search);
  345. search.addAttributes(old);
  346. search.removeAttributes(attrs);
  347. reclaim(old);
  348. return getImmutableUniqueSet();
  349. }
  350. MutableAttributeSet ma = getMutableAttributeSet(old);
  351. ma.removeAttributes(attrs);
  352. return ma;
  353. }
  354. /**
  355. * Fetches an empty AttributeSet.
  356. *
  357. * @return the set
  358. */
  359. public AttributeSet getEmptySet() {
  360. return SimpleAttributeSet.EMPTY;
  361. }
  362. /**
  363. * Returns a set no longer needed by the MutableAttributeSet implmentation.
  364. * This is useful for operation under 1.1 where there are no weak
  365. * references. This would typically be called by the finalize method
  366. * of the MutableAttributeSet implementation.
  367. * <p>
  368. * This method is thread safe, although most Swing methods
  369. * are not. Please see
  370. * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  371. * and Swing</A> for more information.
  372. *
  373. * @param a the set to reclaim
  374. */
  375. public void reclaim(AttributeSet a) {
  376. if (a instanceof SmallAttributeSet) {
  377. SmallAttributeSet sa = (SmallAttributeSet) a;
  378. sa.nrefs -= 1;
  379. if (sa.nrefs <= 0) {
  380. unusedSets += 1;
  381. if ((unusedSets > 10) && (unusedSets > (attributesPool.size() / 10))) {
  382. if (SwingUtilities.isEventDispatchThread()) {
  383. removeUnusedSets();
  384. } else {
  385. Runnable callRemoveUnused = new Runnable() {
  386. public void run() {
  387. removeUnusedSets();
  388. }
  389. };
  390. SwingUtilities.invokeLater(callRemoveUnused);
  391. }
  392. }
  393. }
  394. }
  395. }
  396. // --- local methods -----------------------------------------------
  397. /**
  398. * Returns the maximum number of key/value pairs to try and
  399. * compress into unique/immutable sets. Any sets above this
  400. * limit will use hashtables and be a MutableAttributeSet.
  401. *
  402. * @return the threshold
  403. */
  404. protected int getCompressionThreshold() {
  405. return THRESHOLD;
  406. }
  407. /**
  408. * Create a compact set of attributes that might be shared.
  409. * This is a hook for subclasses that want to alter the
  410. * behavior of SmallAttributeSet. This can be reimplemented
  411. * to return an AttributeSet that provides some sort of
  412. * attribute conversion.
  413. *
  414. * @param a The set of attributes to be represented in the
  415. * the compact form.
  416. */
  417. protected SmallAttributeSet createSmallAttributeSet(AttributeSet a) {
  418. return new SmallAttributeSet(a);
  419. }
  420. /**
  421. * Create a large set of attributes that should trade off
  422. * space for time. This set will not be shared. This is
  423. * a hook for subclasses that want to alter the behavior
  424. * of the larger attribute storage format (which is
  425. * SimpleAttributeSet by default). This can be reimplemented
  426. * to return a MutableAttributeSet that provides some sort of
  427. * attribute conversion.
  428. *
  429. * @param a The set of attributes to be represented in the
  430. * the larger form.
  431. */
  432. protected MutableAttributeSet createLargeAttributeSet(AttributeSet a) {
  433. return new SimpleAttributeSet(a);
  434. }
  435. /**
  436. * Clean the unused immutable sets out of the hashtable.
  437. */
  438. synchronized void removeUnusedSets() {
  439. Vector rmList = new Vector();
  440. Enumeration sets = attributesPool.keys();
  441. while (sets.hasMoreElements()) {
  442. SmallAttributeSet set = (SmallAttributeSet)sets.nextElement();
  443. if (set.nrefs <= 0) {
  444. rmList.addElement(set);
  445. }
  446. }
  447. sets = rmList.elements();
  448. while (sets.hasMoreElements()) {
  449. attributesPool.remove(sets.nextElement());
  450. }
  451. unusedSets = 0;
  452. }
  453. /**
  454. * Search for an existing attribute set using the current search
  455. * parameters. If a matching set is found, return it. If a match
  456. * is not found, we create a new set and add it to the pool.
  457. */
  458. AttributeSet getImmutableUniqueSet() {
  459. // PENDING(prinz) should consider finding a alternative to
  460. // generating extra garbage on search key.
  461. SmallAttributeSet key = createSmallAttributeSet(search);
  462. SmallAttributeSet a = (SmallAttributeSet) attributesPool.get(key);
  463. if (a == null) {
  464. a = key;
  465. attributesPool.put(a, a);
  466. }
  467. a.nrefs += 1;
  468. return a;
  469. }
  470. /**
  471. * Creates a mutable attribute set to hand out because the current
  472. * needs are too big to try and use a shared version.
  473. */
  474. MutableAttributeSet getMutableAttributeSet(AttributeSet a) {
  475. if (a instanceof MutableAttributeSet &&
  476. a != SimpleAttributeSet.EMPTY) {
  477. return (MutableAttributeSet) a;
  478. }
  479. return createLargeAttributeSet(a);
  480. }
  481. /**
  482. * Converts a StyleContext to a String.
  483. *
  484. * @return the string
  485. */
  486. public String toString() {
  487. removeUnusedSets();
  488. String s = "";
  489. Enumeration sets = attributesPool.keys();
  490. while (sets.hasMoreElements()) {
  491. SmallAttributeSet set = (SmallAttributeSet)sets.nextElement();
  492. s = s + set + "\n";
  493. }
  494. return s;
  495. }
  496. // --- serialization ---------------------------------------------
  497. /**
  498. * Context-specific handling of writing out attributes
  499. */
  500. public void writeAttributes(ObjectOutputStream out,
  501. AttributeSet a) throws IOException {
  502. writeAttributeSet(out, a);
  503. }
  504. /**
  505. * Context-specific handling of reading in attributes
  506. */
  507. public void readAttributes(ObjectInputStream in,
  508. MutableAttributeSet a) throws ClassNotFoundException, IOException {
  509. readAttributeSet(in, a);
  510. }
  511. /**
  512. * Writes a set of attributes to the given object stream
  513. * for the purpose of serialization. This will take
  514. * special care to deal with static attribute keys that
  515. * have been registered wit the
  516. * <code>registerStaticAttributeKey</code> method.
  517. * Any attribute key not regsitered as a static key
  518. * will be serialized directly. All values are expected
  519. * to be serializable.
  520. *
  521. * @param out the output stream
  522. * @param a the attribute set
  523. * @exception IOException on any I/O error
  524. */
  525. public static void writeAttributeSet(ObjectOutputStream out,
  526. AttributeSet a) throws IOException {
  527. int n = a.getAttributeCount();
  528. out.writeInt(n);
  529. Enumeration keys = a.getAttributeNames();
  530. while (keys.hasMoreElements()) {
  531. Object key = keys.nextElement();
  532. if (key instanceof Serializable) {
  533. out.writeObject(key);
  534. } else {
  535. Object ioFmt = freezeKeyMap.get(key);
  536. if (ioFmt == null) {
  537. throw new NotSerializableException(key.getClass().
  538. getName() + " is not serializable as a key in an AttributeSet");
  539. }
  540. out.writeObject(ioFmt);
  541. }
  542. Object value = a.getAttribute(key);
  543. if (value instanceof Serializable) {
  544. out.writeObject(value);
  545. } else {
  546. Object ioFmt = freezeKeyMap.get(value);
  547. if (ioFmt == null) {
  548. throw new NotSerializableException(value.getClass().
  549. getName() + " is not serializable as a value in an AttributeSet");
  550. }
  551. out.writeObject(ioFmt);
  552. }
  553. }
  554. }
  555. /**
  556. * Reads a set of attributes from the given object input
  557. * stream that have been previously written out with
  558. * <code>writeAttributeSet</code>. This will try to restore
  559. * keys that were static objects to the static objects in
  560. * the current virtual machine considering only those keys
  561. * that have been registered with the
  562. * <code>registerStaticAttributeKey</code> method.
  563. * The attributes retrieved from the stream will be placed
  564. * into the given mutable set.
  565. *
  566. * @param in the object stream to read the attribute data from.
  567. * @param a the attribute set to place the attribute
  568. * definitions in.
  569. * @exception ClassNotFoundException passed upward if encountered
  570. * when reading the object stream.
  571. * @exception IOException passed upward if encountered when
  572. * reading the object stream.
  573. */
  574. public static void readAttributeSet(ObjectInputStream in,
  575. MutableAttributeSet a) throws ClassNotFoundException, IOException {
  576. int n = in.readInt();
  577. for (int i = 0; i < n; i++) {
  578. Object key = in.readObject();
  579. Object value = in.readObject();
  580. if (thawKeyMap != null) {
  581. Object staticKey = thawKeyMap.get(key);
  582. if (staticKey != null) {
  583. key = staticKey;
  584. }
  585. Object staticValue = thawKeyMap.get(value);
  586. if (staticValue != null) {
  587. value = staticValue;
  588. }
  589. }
  590. a.addAttribute(key, value);
  591. }
  592. }
  593. /**
  594. * Registers an object as a static object that is being
  595. * used as a key in attribute sets. This allows the key
  596. * to be treated specially for serialization.
  597. * <p>
  598. * For operation under a 1.1 virtual machine, this
  599. * uses the value returned by <code>toString</code>
  600. * concatenated to the classname. The value returned
  601. * by toString should not have the class reference
  602. * in it (ie it should be reimplemented from the
  603. * definition in Object) in order to be the same when
  604. * recomputed later.
  605. *
  606. * @param key the non-null object key
  607. */
  608. public static void registerStaticAttributeKey(Object key) {
  609. String ioFmt = key.getClass().getName() + "." + key.toString();
  610. if (freezeKeyMap == null) {
  611. freezeKeyMap = new Hashtable();
  612. thawKeyMap = new Hashtable();
  613. }
  614. freezeKeyMap.put(key, ioFmt);
  615. thawKeyMap.put(ioFmt, key);
  616. }
  617. /**
  618. * Returns the object previously registered with
  619. * <code>registerStaticAttributeKey</code>.
  620. */
  621. public static Object getStaticAttribute(Object key) {
  622. if (thawKeyMap == null || key == null) {
  623. return null;
  624. }
  625. return thawKeyMap.get(key);
  626. }
  627. /**
  628. * Returns the String that <code>key</code> will be registered with
  629. * @see #getStaticAttribute
  630. * @see #registerStaticAttributeKey
  631. */
  632. public static Object getStaticAttributeKey(Object key) {
  633. return key.getClass().getName() + "." + key.toString();
  634. }
  635. private void writeObject(java.io.ObjectOutputStream s)
  636. throws IOException
  637. {
  638. // clean out unused sets before saving
  639. removeUnusedSets();
  640. s.defaultWriteObject();
  641. }
  642. private void readObject(ObjectInputStream s)
  643. throws ClassNotFoundException, IOException
  644. {
  645. fontSearch = new FontKey(null, 0, 0);
  646. fontTable = new Hashtable();
  647. search = new SimpleAttributeSet();
  648. attributesPool = new Hashtable();
  649. s.defaultReadObject();
  650. }
  651. // --- variables ---------------------------------------------------
  652. /**
  653. * The name given to the default logical style attached
  654. * to paragraphs.
  655. */
  656. public static final String DEFAULT_STYLE = "default";
  657. private static Hashtable freezeKeyMap;
  658. private static Hashtable thawKeyMap;
  659. private Style styles;
  660. private transient FontKey fontSearch = new FontKey(null, 0, 0);
  661. private transient Hashtable fontTable = new Hashtable();
  662. private transient Hashtable attributesPool = new Hashtable();
  663. private transient MutableAttributeSet search = new SimpleAttributeSet();
  664. /**
  665. * Number of immutable sets that are not currently
  666. * being used. This helps indicate when the sets need
  667. * to be cleaned out of the hashtable they are stored
  668. * in.
  669. */
  670. private int unusedSets;
  671. /**
  672. * The threshold for no longer sharing the set of attributes
  673. * in an immutable table.
  674. */
  675. static final int THRESHOLD = 9;
  676. /**
  677. * This class holds a small number of attributes in an array.
  678. * The storage format is key, value, key, value, etc. The size
  679. * of the set is the length of the array divided by two. By
  680. * default, this is the class that will be used to store attributes
  681. * when held in the compact sharable form.
  682. */
  683. public class SmallAttributeSet implements AttributeSet {
  684. public SmallAttributeSet(Object[] attributes) {
  685. this.attributes = attributes;
  686. }
  687. public SmallAttributeSet(AttributeSet attrs) {
  688. int n = attrs.getAttributeCount();
  689. Object[] tbl = new Object[2 * n];
  690. Enumeration names = attrs.getAttributeNames();
  691. int i = 0;
  692. while (names.hasMoreElements()) {
  693. tbl[i] = names.nextElement();
  694. tbl[i+1] = attrs.getAttribute(tbl[i]);
  695. i += 2;
  696. }
  697. attributes = tbl;
  698. }
  699. Object getLocalAttribute(Object nm) {
  700. Object[] tbl = attributes;
  701. for (int i = 0; i < tbl.length; i += 2) {
  702. if (nm.equals(tbl[i])) {
  703. return tbl[i+1];
  704. }
  705. }
  706. return null;
  707. }
  708. // --- Object methods -------------------------
  709. /**
  710. * Returns a string showing the key/value pairs
  711. */
  712. public String toString() {
  713. String s = "{";
  714. Object[] tbl = attributes;
  715. for (int i = 0; i < tbl.length; i += 2) {
  716. if (tbl[i+1] instanceof AttributeSet) {
  717. // don't recurse
  718. s = s + tbl[i] + "=" + "AttributeSet" + ",";
  719. } else {
  720. s = s + tbl[i] + "=" + tbl[i+1] + ",";
  721. }
  722. }
  723. s = s + "nrefs=" + nrefs + "}";
  724. return s;
  725. }
  726. /**
  727. * Returns a hashcode for this set of attributes.
  728. * @return a hashcode value for this set of attributes.
  729. */
  730. public int hashCode() {
  731. int code = 0;
  732. Object[] tbl = attributes;
  733. for (int i = 1; i < tbl.length; i += 2) {
  734. code ^= tbl[i].hashCode();
  735. }
  736. return code;
  737. }
  738. /**
  739. * Compares this object to the specifed object.
  740. * The result is <code>true</code> if and only if the argument is not
  741. * <code>null</code> and is a <code>Font</code> object with the same
  742. * name, style, and point size as this font.
  743. * @param obj the object to compare this font with.
  744. * @return <code>true</code> if the objects are equal;
  745. * <code>false</code> otherwise.
  746. */
  747. public boolean equals(Object obj) {
  748. if (obj instanceof AttributeSet) {
  749. AttributeSet attrs = (AttributeSet) obj;
  750. return ((getAttributeCount() == attrs.getAttributeCount()) &&
  751. containsAttributes(attrs));
  752. }
  753. return false;
  754. }
  755. /**
  756. * Clones a set of attributes. Since the set is immutable, a
  757. * clone is basically the same set.
  758. *
  759. * @return the set of attributes
  760. */
  761. public Object clone() {
  762. return this;
  763. }
  764. // --- AttributeSet methods ----------------------------
  765. /**
  766. * Gets the number of attributes that are defined.
  767. *
  768. * @return the number of attributes
  769. * @see AttributeSet#getAttributeCount
  770. */
  771. public int getAttributeCount() {
  772. return attributes.length / 2;
  773. }
  774. /**
  775. * Checks whether a given attribute is defined.
  776. *
  777. * @param key the attribute key
  778. * @return true if the attribute is defined
  779. * @see AttributeSet#isDefined
  780. */
  781. public boolean isDefined(Object key) {
  782. Object[] a = attributes;
  783. int n = a.length;
  784. for (int i = 0; i < n; i += 2) {
  785. if (key.equals(a[i])) {
  786. return true;
  787. }
  788. }
  789. return false;
  790. }
  791. /**
  792. * Checks whether two attribute sets are equal.
  793. *
  794. * @param attr the attribute set to check against
  795. * @return true if the same
  796. * @see AttributeSet#isEqual
  797. */
  798. public boolean isEqual(AttributeSet attr) {
  799. if (attr instanceof SmallAttributeSet) {
  800. return attr == this;
  801. }
  802. return ((getAttributeCount() == attr.getAttributeCount()) &&
  803. containsAttributes(attr));
  804. }
  805. /**
  806. * Copies a set of attributes.
  807. *
  808. * @return the copy
  809. * @see AttributeSet#copyAttributes
  810. */
  811. public AttributeSet copyAttributes() {
  812. return this;
  813. }
  814. /**
  815. * Gets the value of an attribute.
  816. *
  817. * @param key the attribute name
  818. * @return the attribute value
  819. * @see AttributeSet#getAttribute
  820. */
  821. public Object getAttribute(Object key) {
  822. Object value = getLocalAttribute(key);
  823. if (value == null) {
  824. AttributeSet parent = getResolveParent();
  825. if (parent != null)
  826. value = parent.getAttribute(key);
  827. }
  828. return value;
  829. }
  830. /**
  831. * Gets the names of all attributes.
  832. *
  833. * @return the attribute names
  834. * @see AttributeSet#getAttributeNames
  835. */
  836. public Enumeration getAttributeNames() {
  837. return new KeyEnumeration(attributes);
  838. }
  839. /**
  840. * Checks whether a given attribute name/value is defined.
  841. *
  842. * @param name the attribute name
  843. * @param value the attribute value
  844. * @return true if the name/value is defined
  845. * @see AttributeSet#containsAttribute
  846. */
  847. public boolean containsAttribute(Object name, Object value) {
  848. return value.equals(getAttribute(name));
  849. }
  850. /**
  851. * Checks whether the attribute set contains all of
  852. * the given attributes.
  853. *
  854. * @param attrs the attributes to check
  855. * @return true if the element contains all the attributes
  856. * @see AttributeSet#containsAttributes
  857. */
  858. public boolean containsAttributes(AttributeSet attrs) {
  859. boolean result = true;
  860. Enumeration names = attrs.getAttributeNames();
  861. while (result && names.hasMoreElements()) {
  862. Object name = names.nextElement();
  863. result = attrs.getAttribute(name).equals(getAttribute(name));
  864. }
  865. return result;
  866. }
  867. /**
  868. * If not overriden, the resolving parent defaults to
  869. * the parent element.
  870. *
  871. * @return the attributes from the parent
  872. * @see AttributeSet#getResolveParent
  873. */
  874. public AttributeSet getResolveParent() {
  875. return (AttributeSet) getLocalAttribute(StyleConstants.ResolveAttribute);
  876. }
  877. // --- variables -----------------------------------------
  878. Object[] attributes;
  879. int nrefs;
  880. }
  881. /**
  882. * An enumeration of the keys in a SmallAttributeSet.
  883. */
  884. class KeyEnumeration implements Enumeration {
  885. KeyEnumeration(Object[] attr) {
  886. this.attr = attr;
  887. i = 0;
  888. }
  889. /**
  890. * Tests if this enumeration contains more elements.
  891. *
  892. * @return <code>true</code> if this enumeration contains more elements;
  893. * <code>false</code> otherwise.
  894. * @since JDK1.0
  895. */
  896. public boolean hasMoreElements() {
  897. return i < attr.length;
  898. }
  899. /**
  900. * Returns the next element of this enumeration.
  901. *
  902. * @return the next element of this enumeration.
  903. * @exception NoSuchElementException if no more elements exist.
  904. * @since JDK1.0
  905. */
  906. public Object nextElement() {
  907. if (i < attr.length) {
  908. Object o = attr[i];
  909. i += 2;
  910. return o;
  911. }
  912. throw new NoSuchElementException();
  913. }
  914. Object[] attr;
  915. int i;
  916. }
  917. /**
  918. * Sorts the key strings so that they can be very quickly compared
  919. * in the attribute set searchs.
  920. */
  921. class KeyBuilder {
  922. public void initialize(AttributeSet a) {
  923. if (a instanceof SmallAttributeSet) {
  924. initialize(((SmallAttributeSet)a).attributes);
  925. } else {
  926. keys.removeAllElements();
  927. data.removeAllElements();
  928. Enumeration names = a.getAttributeNames();
  929. while (names.hasMoreElements()) {
  930. Object name = names.nextElement();
  931. addAttribute(name, a.getAttribute(name));
  932. }
  933. }
  934. }
  935. /**
  936. * Initialize with a set of already sorted
  937. * keys (data from an existing SmallAttributeSet).
  938. */
  939. private void initialize(Object[] sorted) {
  940. keys.removeAllElements();
  941. data.removeAllElements();
  942. int n = sorted.length;
  943. for (int i = 0; i < n; i += 2) {
  944. keys.addElement(sorted[i]);
  945. data.addElement(sorted[i+1]);
  946. }
  947. }
  948. /**
  949. * Creates a table of sorted key/value entries
  950. * suitable for creation of an instance of
  951. * SmallAttributeSet.
  952. */
  953. public Object[] createTable() {
  954. int n = keys.size();
  955. Object[] tbl = new Object[2 * n];
  956. for (int i = 0; i < n; i ++) {
  957. int offs = 2 * i;
  958. tbl[offs] = keys.elementAt(i);
  959. tbl[offs + 1] = data.elementAt(i);
  960. }
  961. return tbl;
  962. }
  963. /**
  964. * The number of key/value pairs contained
  965. * in the current key being forged.
  966. */
  967. int getCount() {
  968. return keys.size();
  969. }
  970. /**
  971. * Adds a key/value to the set.
  972. */
  973. public void addAttribute(Object key, Object value) {
  974. keys.addElement(key);
  975. data.addElement(value);
  976. }
  977. /**
  978. * Adds a set of key/value pairs to the set.
  979. */
  980. public void addAttributes(AttributeSet attr) {
  981. if (attr instanceof SmallAttributeSet) {
  982. // avoid searching the keys, they are already interned.
  983. Object[] tbl = ((SmallAttributeSet)attr).attributes;
  984. int n = tbl.length;
  985. for (int i = 0; i < n; i += 2) {
  986. addAttribute(tbl[i], tbl[i+1]);
  987. }
  988. } else {
  989. Enumeration names = attr.getAttributeNames();
  990. while (names.hasMoreElements()) {
  991. Object name = names.nextElement();
  992. addAttribute(name, attr.getAttribute(name));
  993. }
  994. }
  995. }
  996. /**
  997. * Removes the given name from the set.
  998. */
  999. public void removeAttribute(Object key) {
  1000. int n = keys.size();
  1001. for (int i = 0; i < n; i++) {
  1002. if (keys.elementAt(i).equals(key)) {
  1003. keys.removeElementAt(i);
  1004. data.removeElementAt(i);
  1005. return;
  1006. }
  1007. }
  1008. }
  1009. /**
  1010. * Removes the set of keys from the set.
  1011. */
  1012. public void removeAttributes(Enumeration names) {
  1013. while (names.hasMoreElements()) {
  1014. Object name = names.nextElement();
  1015. removeAttribute(name);
  1016. }
  1017. }
  1018. /**
  1019. * Removes the set of matching attributes from the set.
  1020. */
  1021. public void removeAttributes(AttributeSet attr) {
  1022. Enumeration names = attr.getAttributeNames();
  1023. while (names.hasMoreElements()) {
  1024. Object name = names.nextElement();
  1025. Object value = attr.getAttribute(name);
  1026. removeSearchAttribute(name, value);
  1027. }
  1028. }
  1029. private void removeSearchAttribute(Object ikey, Object value) {
  1030. int n = keys.size();
  1031. for (int i = 0; i < n; i++) {
  1032. if (keys.elementAt(i).equals(ikey)) {
  1033. if (data.elementAt(i).equals(value)) {
  1034. keys.removeElementAt(i);
  1035. data.removeElementAt(i);
  1036. }
  1037. return;
  1038. }
  1039. }
  1040. }
  1041. private Vector keys = new Vector();
  1042. private Vector data = new Vector();
  1043. }
  1044. /**
  1045. * key for a font table
  1046. */
  1047. static class FontKey {
  1048. private String family;
  1049. private int style;
  1050. private int size;
  1051. /**
  1052. * Constructs a font key.
  1053. */
  1054. public FontKey(String family, int style, int size) {
  1055. setValue(family, style, size);
  1056. }
  1057. public void setValue(String family, int style, int size) {
  1058. this.family = (family != null) ? family.intern() : null;
  1059. this.style = style;
  1060. this.size = size;
  1061. }
  1062. /**
  1063. * Returns a hashcode for this font.
  1064. * @return a hashcode value for this font.
  1065. */
  1066. public int hashCode() {
  1067. return family.hashCode() ^ style ^ size;
  1068. }
  1069. /**
  1070. * Compares this object to the specifed object.
  1071. * The result is <code>true</code> if and only if the argument is not
  1072. * <code>null</code> and is a <code>Font</code> object with the same
  1073. * name, style, and point size as this font.
  1074. * @param obj the object to compare this font with.
  1075. * @return <code>true</code> if the objects are equal;
  1076. * <code>false</code> otherwise.
  1077. */
  1078. public boolean equals(Object obj) {
  1079. if (obj instanceof FontKey) {
  1080. FontKey font = (FontKey)obj;
  1081. return (size == font.size) && (style == font.style) && (family == font.family);
  1082. }
  1083. return false;
  1084. }
  1085. }
  1086. /**
  1087. * A collection of attributes, typically used to represent
  1088. * character and paragraph styles. This is an implementation
  1089. * of MutableAttributeSet that can be observed if desired.
  1090. * These styles will take advantage of immutability while
  1091. * the sets are small enough, and may be substantially more
  1092. * efficient than something like SimpleAttributeSet.
  1093. * <p>
  1094. * <strong>Warning:</strong>
  1095. * Serialized objects of this class will not be compatible with
  1096. * future Swing releases. The current serialization support is appropriate
  1097. * for short term storage or RMI between applications running the same
  1098. * version of Swing. A future release of Swing will provide support for
  1099. * long term persistence.
  1100. */
  1101. public class NamedStyle implements Style, Serializable {
  1102. /**
  1103. * Creates a new named style.
  1104. *
  1105. * @param name the style name, null for unnamed
  1106. * @param parent the parent style, null if none
  1107. */
  1108. public NamedStyle(String name, Style parent) {
  1109. attributes = getEmptySet();
  1110. if (name != null) {
  1111. setName(name);
  1112. }
  1113. if (parent != null) {
  1114. setResolveParent(parent);
  1115. }
  1116. }
  1117. /**
  1118. * Creates a new named style.
  1119. *
  1120. * @param parent the parent style, null if none
  1121. */
  1122. public NamedStyle(Style parent) {
  1123. this(null, parent);
  1124. }
  1125. /**
  1126. * Creates a new named style, with a null name and parent.
  1127. */
  1128. public NamedStyle() {
  1129. attributes = getEmptySet();
  1130. }
  1131. /**
  1132. * Converts the style to a string.
  1133. *
  1134. * @return the string
  1135. */
  1136. public String toString() {
  1137. return "NamedStyle:" + getName() + " " + attributes;
  1138. }
  1139. /**
  1140. * Fetches the name of the style. A style is not required to be named,
  1141. * so null is returned if there is no name associated with the style.
  1142. *
  1143. * @return the name
  1144. */
  1145. public String getName() {
  1146. if (isDefined(StyleConstants.NameAttribute)) {
  1147. return (String) getAttribute(StyleConstants.NameAttribute);
  1148. }
  1149. return null;
  1150. }
  1151. /**
  1152. * Changes the name of the style. Does nothing with a null name.
  1153. *
  1154. * @param name the new name
  1155. */
  1156. public void setName(String name) {
  1157. if (name != null) {
  1158. this.addAttribute(StyleConstants.NameAttribute, name);
  1159. }
  1160. }
  1161. /**
  1162. * Adds a change listener.
  1163. *
  1164. * @param l the change listener
  1165. * @see Attributes#addChangeListener
  1166. */
  1167. public void addChangeListener(ChangeListener l) {
  1168. listenerList.add(ChangeListener.class, l);
  1169. }
  1170. /**
  1171. * Removes a change listener.
  1172. *
  1173. * @param l the change listener
  1174. * @see Attributes#removeChangeListener
  1175. */
  1176. public void removeChangeListener(ChangeListener l) {
  1177. listenerList.remove(ChangeListener.class, l);
  1178. }
  1179. /**
  1180. * Notifies all listeners that have registered interest for
  1181. * notification on this event type. The event instance
  1182. * is lazily created using the parameters passed into
  1183. * the fire method.
  1184. *
  1185. * @see EventListenerList
  1186. */
  1187. protected void fireStateChanged() {
  1188. // Guaranteed to return a non-null array
  1189. Object[] listeners = listenerList.getListenerList();
  1190. // Process the listeners last to first, notifying
  1191. // those that are interested in this event
  1192. for (int i = listeners.length-2; i>=0; i-=2) {
  1193. if (listeners[i]==ChangeListener.class) {
  1194. // Lazily create the event:
  1195. if (changeEvent == null)
  1196. changeEvent = new ChangeEvent(this);
  1197. ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
  1198. }
  1199. }
  1200. }
  1201. // --- AttributeSet ----------------------------
  1202. // delegated to the immutable field "attributes"
  1203. /**
  1204. * Gets the number of attributes that are defined.
  1205. *
  1206. * @return the number of attributes >= 0
  1207. * @see AttributeSet#getAttributeCount
  1208. */
  1209. public int getAttributeCount() {
  1210. return attributes.getAttributeCount();
  1211. }
  1212. /**
  1213. * Checks whether a given attribute is defined.
  1214. *
  1215. * @param attrName the non-null attribute name
  1216. * @return true if the attribute is defined
  1217. * @see AttributeSet#isDefined
  1218. */
  1219. public boolean isDefined(Object attrName) {
  1220. return attributes.isDefined(attrName);
  1221. }
  1222. /**
  1223. * Checks whether two attribute sets are equal.
  1224. *
  1225. * @param attr the attribute set to check against
  1226. * @return true if the same
  1227. * @see AttributeSet#isEqual
  1228. */
  1229. public boolean isEqual(AttributeSet attr) {
  1230. return attributes.isEqual(attr);
  1231. }
  1232. /**
  1233. * Copies a set of attributes.
  1234. *
  1235. * @return the copy
  1236. * @see AttributeSet#copyAttributes
  1237. */
  1238. public AttributeSet copyAttributes() {
  1239. NamedStyle a = new NamedStyle();
  1240. a.attributes = attributes.copyAttributes();
  1241. return a;
  1242. }
  1243. /**
  1244. * Gets the value of an attribute.
  1245. *
  1246. * @param attrName the non-null attribute name
  1247. * @return the attribute value
  1248. * @see AttributeSet#getAttribute
  1249. */
  1250. public Object getAttribute(Object attrName) {
  1251. return attributes.getAttribute(attrName);
  1252. }
  1253. /**
  1254. * Gets the names of all attributes.
  1255. *
  1256. * @return the attribute names as an enumeration
  1257. * @see AttributeSet#getAttributeNames
  1258. */
  1259. public Enumeration getAttributeNames() {
  1260. return attributes.getAttributeNames();
  1261. }
  1262. /**
  1263. * Checks whether a given attribute name/value is defined.
  1264. *
  1265. * @param name the non-null attribute name
  1266. * @param value the attribute value
  1267. * @return true if the name/value is defined
  1268. * @see AttributeSet#containsAttribute
  1269. */
  1270. public boolean containsAttribute(Object name, Object value) {
  1271. return attributes.containsAttribute(name, value);
  1272. }
  1273. /**
  1274. * Checks whether the element contains all the attributes.
  1275. *
  1276. * @param attrs the attributes to check
  1277. * @return true if the element contains all the attributes
  1278. * @see AttributeSet#containsAttributes
  1279. */
  1280. public boolean containsAttributes(AttributeSet attrs) {
  1281. return attributes.containsAttributes(attrs);
  1282. }
  1283. /**
  1284. * Gets attributes from the parent.
  1285. * If not overriden, the resolving parent defaults to
  1286. * the parent element.
  1287. *
  1288. * @return the attributes from the parent
  1289. * @see AttributeSet#getResolveParent
  1290. */
  1291. public AttributeSet getResolveParent() {
  1292. return attributes.getResolveParent();
  1293. }
  1294. // --- MutableAttributeSet ----------------------------------
  1295. // should fetch a new immutable record for the field
  1296. // "attributes".
  1297. /**
  1298. * Adds an attribute.
  1299. *
  1300. * @param name the non-null attribute name
  1301. * @param value the attribute value
  1302. * @see MutableAttributeSet#addAttribute
  1303. */
  1304. public void addAttribute(Object name, Object value) {
  1305. StyleContext context = StyleContext.this;
  1306. attributes = context.addAttribute(attributes, name, value);
  1307. fireStateChanged();
  1308. }
  1309. /**
  1310. * Adds a set of attributes to the element.
  1311. *
  1312. * @param attr the attributes to add
  1313. * @see MutableAttributeSet#addAttribute
  1314. */
  1315. public void addAttributes(AttributeSet attr) {
  1316. StyleContext context = StyleContext.this;
  1317. attributes = context.addAttributes(attributes, attr);
  1318. fireStateChanged();
  1319. }
  1320. /**
  1321. * Removes an attribute from the set.
  1322. *
  1323. * @param name the non-null attribute name
  1324. * @see MutableAttributeSet#removeAttribute
  1325. */
  1326. public void removeAttribute(Object name) {
  1327. StyleContext context = StyleContext.this;
  1328. attributes = context.removeAttribute(attributes, name);
  1329. fireStateChanged();
  1330. }
  1331. /**
  1332. * Removes a set of attributes for the element.
  1333. *
  1334. * @param names the attribute names
  1335. * @see MutableAttributeSet#removeAttributes
  1336. */
  1337. public void removeAttributes(Enumeration names) {
  1338. StyleContext context = StyleContext.this;
  1339. attributes = context.removeAttributes(attributes, names);
  1340. fireStateChanged();
  1341. }
  1342. /**
  1343. * Removes a set of attributes for the element.
  1344. *
  1345. * @param attrs the attributes
  1346. * @see MutableAttributeSet#removeAttributes
  1347. */
  1348. public void removeAttributes(AttributeSet attrs) {
  1349. StyleContext context = StyleContext.this;
  1350. if (attrs == this) {
  1351. attributes = context.getEmptySet();
  1352. } else {
  1353. attributes = context.removeAttributes(attributes, attrs);
  1354. }
  1355. fireStateChanged();
  1356. }
  1357. /**
  1358. * Sets the resolving parent.
  1359. *
  1360. * @param parent the parent, null if none
  1361. * @see MutableAttributeSet#setResolveParent
  1362. */
  1363. public void setResolveParent(AttributeSet parent) {
  1364. if (parent != null) {
  1365. addAttribute(StyleConstants.ResolveAttribute, parent);
  1366. } else {
  1367. removeAttribute(StyleConstants.ResolveAttribute);
  1368. }
  1369. }
  1370. // --- serialization ---------------------------------------------
  1371. private void writeObject(ObjectOutputStream s) throws IOException {
  1372. s.defaultWriteObject();
  1373. writeAttributeSet(s, attributes);
  1374. }
  1375. private void readObject(ObjectInputStream s)
  1376. throws ClassNotFoundException, IOException
  1377. {
  1378. s.defaultReadObject();
  1379. attributes = SimpleAttributeSet.EMPTY;
  1380. readAttributeSet(s, this);
  1381. }
  1382. // --- member variables -----------------------------------------------
  1383. /**
  1384. * The change listeners for the model.
  1385. */
  1386. protected EventListenerList listenerList = new EventListenerList();
  1387. /**
  1388. * Only one ChangeEvent is needed per model instance since the
  1389. * event's only (read-only) state is the source property. The source
  1390. * of events generated here is always "this".
  1391. */
  1392. protected transient ChangeEvent changeEvent = null;
  1393. /**
  1394. * Inner AttributeSet implementation, which may be an
  1395. * immutable unique set being shared.
  1396. */
  1397. private transient AttributeSet attributes;
  1398. }
  1399. }