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