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