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