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