001 /* StyleContext.java -- 002 Copyright (C) 2004 Free Software Foundation, Inc. 003 004 This file is part of GNU Classpath. 005 006 GNU Classpath is free software; you can redistribute it and/or modify 007 it under the terms of the GNU General Public License as published by 008 the Free Software Foundation; either version 2, or (at your option) 009 any later version. 010 011 GNU Classpath is distributed in the hope that it will be useful, but 012 WITHOUT ANY WARRANTY; without even the implied warranty of 013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 General Public License for more details. 015 016 You should have received a copy of the GNU General Public License 017 along with GNU Classpath; see the file COPYING. If not, write to the 018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 019 02110-1301 USA. 020 021 Linking this library statically or dynamically with other modules is 022 making a combined work based on this library. Thus, the terms and 023 conditions of the GNU General Public License cover the whole 024 combination. 025 026 As a special exception, the copyright holders of this library give you 027 permission to link this library with independent modules to produce an 028 executable, regardless of the license terms of these independent 029 modules, and to copy and distribute the resulting executable under 030 terms of your choice, provided that you also meet, for each linked 031 independent module, the terms and conditions of the license of that 032 module. An independent module is a module which is not derived from 033 or based on this library. If you modify this library, you may extend 034 this exception to your version of the library, but you are not 035 obligated to do so. If you do not wish to do so, delete this 036 exception statement from your version. */ 037 038 039 package javax.swing.text; 040 041 import java.awt.Color; 042 import java.awt.Font; 043 import java.awt.FontMetrics; 044 import java.awt.Toolkit; 045 import java.io.IOException; 046 import java.io.NotSerializableException; 047 import java.io.ObjectInputStream; 048 import java.io.ObjectOutputStream; 049 import java.io.Serializable; 050 import java.lang.ref.WeakReference; 051 import java.util.Collections; 052 import java.util.Enumeration; 053 import java.util.EventListener; 054 import java.util.Hashtable; 055 import java.util.Iterator; 056 import java.util.Map; 057 import java.util.WeakHashMap; 058 059 import javax.swing.event.ChangeEvent; 060 import javax.swing.event.ChangeListener; 061 import javax.swing.event.EventListenerList; 062 063 public class StyleContext 064 implements Serializable, AbstractDocument.AttributeContext 065 { 066 /** The serialization UID (compatible with JDK1.5). */ 067 private static final long serialVersionUID = 8042858831190784241L; 068 069 public class NamedStyle 070 implements Serializable, Style 071 { 072 /** The serialization UID (compatible with JDK1.5). */ 073 private static final long serialVersionUID = -6690628971806226374L; 074 075 protected transient ChangeEvent changeEvent; 076 protected EventListenerList listenerList; 077 078 private transient AttributeSet attributes; 079 080 public NamedStyle() 081 { 082 this(null, null); 083 } 084 085 public NamedStyle(Style parent) 086 { 087 this(null, parent); 088 } 089 090 public NamedStyle(String name, Style parent) 091 { 092 attributes = getEmptySet(); 093 listenerList = new EventListenerList(); 094 if (name != null) 095 setName(name); 096 if (parent != null) 097 setResolveParent(parent); 098 } 099 100 public String getName() 101 { 102 String name = null; 103 if (isDefined(StyleConstants.NameAttribute)) 104 name = getAttribute(StyleConstants.NameAttribute).toString(); 105 return name; 106 } 107 108 public void setName(String n) 109 { 110 if (n != null) 111 addAttribute(StyleConstants.NameAttribute, n); 112 } 113 114 public void addChangeListener(ChangeListener l) 115 { 116 listenerList.add(ChangeListener.class, l); 117 } 118 119 public void removeChangeListener(ChangeListener l) 120 { 121 listenerList.remove(ChangeListener.class, l); 122 } 123 124 public <T extends EventListener> T[] getListeners(Class<T> listenerType) 125 { 126 return listenerList.getListeners(listenerType); 127 } 128 129 public ChangeListener[] getChangeListeners() 130 { 131 return (ChangeListener[]) getListeners(ChangeListener.class); 132 } 133 134 protected void fireStateChanged() 135 { 136 ChangeListener[] listeners = getChangeListeners(); 137 for (int i = 0; i < listeners.length; ++i) 138 { 139 // Lazily create event. 140 if (changeEvent == null) 141 changeEvent = new ChangeEvent(this); 142 listeners[i].stateChanged(changeEvent); 143 } 144 } 145 146 public void addAttribute(Object name, Object value) 147 { 148 attributes = StyleContext.this.addAttribute(attributes, name, value); 149 fireStateChanged(); 150 } 151 152 public void addAttributes(AttributeSet attr) 153 { 154 attributes = StyleContext.this.addAttributes(attributes, attr); 155 fireStateChanged(); 156 } 157 158 public boolean containsAttribute(Object name, Object value) 159 { 160 return attributes.containsAttribute(name, value); 161 } 162 163 public boolean containsAttributes(AttributeSet attrs) 164 { 165 return attributes.containsAttributes(attrs); 166 } 167 168 public AttributeSet copyAttributes() 169 { 170 // The RI returns a NamedStyle as copy, so do we. 171 NamedStyle copy = new NamedStyle(); 172 copy.attributes = attributes.copyAttributes(); 173 return copy; 174 } 175 176 public Object getAttribute(Object attrName) 177 { 178 return attributes.getAttribute(attrName); 179 } 180 181 public int getAttributeCount() 182 { 183 return attributes.getAttributeCount(); 184 } 185 186 public Enumeration<?> getAttributeNames() 187 { 188 return attributes.getAttributeNames(); 189 } 190 191 public boolean isDefined(Object attrName) 192 { 193 return attributes.isDefined(attrName); 194 } 195 196 public boolean isEqual(AttributeSet attr) 197 { 198 return attributes.isEqual(attr); 199 } 200 201 public void removeAttribute(Object name) 202 { 203 attributes = StyleContext.this.removeAttribute(attributes, name); 204 fireStateChanged(); 205 } 206 207 public void removeAttributes(AttributeSet attrs) 208 { 209 attributes = StyleContext.this.removeAttributes(attributes, attrs); 210 fireStateChanged(); 211 } 212 213 public void removeAttributes(Enumeration<?> names) 214 { 215 attributes = StyleContext.this.removeAttributes(attributes, names); 216 fireStateChanged(); 217 } 218 219 220 public AttributeSet getResolveParent() 221 { 222 return attributes.getResolveParent(); 223 } 224 225 public void setResolveParent(AttributeSet parent) 226 { 227 if (parent != null) 228 addAttribute(StyleConstants.ResolveAttribute, parent); 229 else 230 removeAttribute(StyleConstants.ResolveAttribute); 231 } 232 233 public String toString() 234 { 235 return "NamedStyle:" + getName() + " " + attributes; 236 } 237 238 private void writeObject(ObjectOutputStream s) 239 throws IOException 240 { 241 s.defaultWriteObject(); 242 writeAttributeSet(s, attributes); 243 } 244 245 private void readObject(ObjectInputStream s) 246 throws ClassNotFoundException, IOException 247 { 248 s.defaultReadObject(); 249 attributes = SimpleAttributeSet.EMPTY; 250 readAttributeSet(s, this); 251 } 252 } 253 254 public class SmallAttributeSet 255 implements AttributeSet 256 { 257 final Object [] attrs; 258 private AttributeSet resolveParent; 259 public SmallAttributeSet(AttributeSet a) 260 { 261 int n = a.getAttributeCount(); 262 int i = 0; 263 attrs = new Object[n * 2]; 264 Enumeration e = a.getAttributeNames(); 265 while (e.hasMoreElements()) 266 { 267 Object name = e.nextElement(); 268 Object value = a.getAttribute(name); 269 if (name == ResolveAttribute) 270 resolveParent = (AttributeSet) value; 271 attrs[i++] = name; 272 attrs[i++] = value; 273 } 274 } 275 276 public SmallAttributeSet(Object [] a) 277 { 278 attrs = a; 279 for (int i = 0; i < attrs.length; i += 2) 280 { 281 if (attrs[i] == ResolveAttribute) 282 resolveParent = (AttributeSet) attrs[i + 1]; 283 } 284 } 285 286 public Object clone() 287 { 288 return this; 289 } 290 291 public boolean containsAttribute(Object name, Object value) 292 { 293 return value.equals(getAttribute(name)); 294 } 295 296 public boolean containsAttributes(AttributeSet a) 297 { 298 boolean res = true; 299 Enumeration e = a.getAttributeNames(); 300 while (e.hasMoreElements() && res) 301 { 302 Object name = e.nextElement(); 303 res = a.getAttribute(name).equals(getAttribute(name)); 304 } 305 return res; 306 } 307 308 public AttributeSet copyAttributes() 309 { 310 return this; 311 } 312 313 public boolean equals(Object obj) 314 { 315 boolean eq = false; 316 if (obj instanceof AttributeSet) 317 { 318 AttributeSet atts = (AttributeSet) obj; 319 eq = getAttributeCount() == atts.getAttributeCount() 320 && containsAttributes(atts); 321 } 322 return eq; 323 } 324 325 public Object getAttribute(Object key) 326 { 327 Object att = null; 328 if (key == StyleConstants.ResolveAttribute) 329 att = resolveParent; 330 331 for (int i = 0; i < attrs.length && att == null; i += 2) 332 { 333 if (attrs[i].equals(key)) 334 att = attrs[i + 1]; 335 } 336 337 // Check the resolve parent, unless we're looking for the 338 // ResolveAttribute, which must not be looked up 339 if (att == null) 340 { 341 AttributeSet parent = getResolveParent(); 342 if (parent != null) 343 att = parent.getAttribute(key); 344 } 345 346 return att; 347 } 348 349 public int getAttributeCount() 350 { 351 return attrs.length / 2; 352 } 353 354 public Enumeration<?> getAttributeNames() 355 { 356 return new Enumeration() 357 { 358 int i = 0; 359 public boolean hasMoreElements() 360 { 361 return i < attrs.length; 362 } 363 public Object nextElement() 364 { 365 i += 2; 366 return attrs[i-2]; 367 } 368 }; 369 } 370 371 public AttributeSet getResolveParent() 372 { 373 return resolveParent; 374 } 375 376 public int hashCode() 377 { 378 return java.util.Arrays.asList(attrs).hashCode(); 379 } 380 381 public boolean isDefined(Object key) 382 { 383 for (int i = 0; i < attrs.length; i += 2) 384 { 385 if (attrs[i].equals(key)) 386 return true; 387 } 388 return false; 389 } 390 391 public boolean isEqual(AttributeSet attr) 392 { 393 boolean eq; 394 // If the other one is also a SmallAttributeSet, it is only considered 395 // equal if it's the same instance. 396 if (attr instanceof SmallAttributeSet) 397 eq = attr == this; 398 else 399 eq = getAttributeCount() == attr.getAttributeCount() 400 && this.containsAttributes(attr); 401 return eq; 402 } 403 404 public String toString() 405 { 406 StringBuilder sb = new StringBuilder(); 407 sb.append('{'); 408 for (int i = 0; i < attrs.length; i += 2) 409 { 410 if (attrs[i + 1] instanceof AttributeSet) 411 { 412 sb.append(attrs[i]); 413 sb.append("=AttributeSet,"); 414 } 415 else 416 { 417 sb.append(attrs[i]); 418 sb.append('='); 419 sb.append(attrs[i + 1]); 420 sb.append(','); 421 } 422 } 423 sb.append("}"); 424 return sb.toString(); 425 } 426 } 427 428 /** 429 * Register StyleConstant keys as static attribute keys for serialization. 430 */ 431 static 432 { 433 // Don't let problems while doing this prevent class loading. 434 try 435 { 436 for (Iterator i = StyleConstants.keys.iterator(); i.hasNext();) 437 registerStaticAttributeKey(i.next()); 438 } 439 catch (Throwable t) 440 { 441 t.printStackTrace(); 442 } 443 } 444 445 /** 446 * The name of the default style. 447 */ 448 public static final String DEFAULT_STYLE = "default"; 449 450 static Hashtable sharedAttributeSets = new Hashtable(); 451 static Hashtable sharedFonts = new Hashtable(); 452 453 static StyleContext defaultStyleContext; 454 static final int compressionThreshold = 9; 455 456 /** 457 * These attribute keys are handled specially in serialization. 458 */ 459 private static Hashtable writeAttributeKeys; 460 private static Hashtable readAttributeKeys; 461 462 private NamedStyle styles; 463 464 /** 465 * Used for searching attributes in the pool. 466 */ 467 private transient MutableAttributeSet search = new SimpleAttributeSet(); 468 469 /** 470 * A pool of immutable AttributeSets. 471 */ 472 private transient Map attributeSetPool = 473 Collections.synchronizedMap(new WeakHashMap()); 474 475 /** 476 * Creates a new instance of the style context. Add the default style 477 * to the style table. 478 */ 479 public StyleContext() 480 { 481 styles = new NamedStyle(null); 482 addStyle(DEFAULT_STYLE, null); 483 } 484 485 protected SmallAttributeSet createSmallAttributeSet(AttributeSet a) 486 { 487 return new SmallAttributeSet(a); 488 } 489 490 protected MutableAttributeSet createLargeAttributeSet(AttributeSet a) 491 { 492 return new SimpleAttributeSet(a); 493 } 494 495 public void addChangeListener(ChangeListener listener) 496 { 497 styles.addChangeListener(listener); 498 } 499 500 public void removeChangeListener(ChangeListener listener) 501 { 502 styles.removeChangeListener(listener); 503 } 504 505 public ChangeListener[] getChangeListeners() 506 { 507 return styles.getChangeListeners(); 508 } 509 510 public Style addStyle(String name, Style parent) 511 { 512 Style newStyle = new NamedStyle(name, parent); 513 if (name != null) 514 styles.addAttribute(name, newStyle); 515 return newStyle; 516 } 517 518 public void removeStyle(String name) 519 { 520 styles.removeAttribute(name); 521 } 522 523 /** 524 * Get the style from the style table. If the passed name 525 * matches {@link #DEFAULT_STYLE}, returns the default style. 526 * Otherwise returns the previously defined style of 527 * <code>null</code> if the style with the given name is not defined. 528 * 529 * @param name the name of the style. 530 * 531 * @return the style with the given name or null if no such defined. 532 */ 533 public Style getStyle(String name) 534 { 535 return (Style) styles.getAttribute(name); 536 } 537 538 /** 539 * Get the names of the style. The returned enumeration always 540 * contains at least one member, the default style. 541 */ 542 public Enumeration<?> getStyleNames() 543 { 544 return styles.getAttributeNames(); 545 } 546 547 private void readObject(ObjectInputStream in) 548 throws ClassNotFoundException, IOException 549 { 550 search = new SimpleAttributeSet(); 551 attributeSetPool = Collections.synchronizedMap(new WeakHashMap()); 552 in.defaultReadObject(); 553 } 554 555 private void writeObject(ObjectOutputStream out) 556 throws IOException 557 { 558 cleanupPool(); 559 out.defaultWriteObject(); 560 } 561 562 // 563 // StyleContexts only understand the "simple" model of fonts present in 564 // pre-java2d systems: fonts are a family name, a size (integral number 565 // of points), and a mask of style parameters (plain, bold, italic, or 566 // bold|italic). We have an inner class here called SimpleFontSpec which 567 // holds such triples. 568 // 569 // A SimpleFontSpec can be built for *any* AttributeSet because the size, 570 // family, and style keys in an AttributeSet have default values (defined 571 // over in StyleConstants). 572 // 573 // We keep a static cache mapping SimpleFontSpecs to java.awt.Fonts, so 574 // that we reuse Fonts between styles and style contexts. 575 // 576 577 private static class SimpleFontSpec 578 { 579 String family; 580 int style; 581 int size; 582 public SimpleFontSpec(String family, 583 int style, 584 int size) 585 { 586 this.family = family; 587 this.style = style; 588 this.size = size; 589 } 590 public boolean equals(Object obj) 591 { 592 return (obj != null) 593 && (obj instanceof SimpleFontSpec) 594 && (((SimpleFontSpec)obj).family.equals(this.family)) 595 && (((SimpleFontSpec)obj).style == this.style) 596 && (((SimpleFontSpec)obj).size == this.size); 597 } 598 public int hashCode() 599 { 600 return family.hashCode() + style + size; 601 } 602 } 603 604 public Font getFont(AttributeSet attr) 605 { 606 String family = StyleConstants.getFontFamily(attr); 607 int style = Font.PLAIN; 608 if (StyleConstants.isBold(attr)) 609 style += Font.BOLD; 610 if (StyleConstants.isItalic(attr)) 611 style += Font.ITALIC; 612 int size = StyleConstants.getFontSize(attr); 613 return getFont(family, style, size); 614 } 615 616 public Font getFont(String family, int style, int size) 617 { 618 SimpleFontSpec spec = new SimpleFontSpec(family, style, size); 619 if (sharedFonts.containsKey(spec)) 620 return (Font) sharedFonts.get(spec); 621 else 622 { 623 Font tmp = new Font(family, style, size); 624 sharedFonts.put(spec, tmp); 625 return tmp; 626 } 627 } 628 629 public FontMetrics getFontMetrics(Font f) 630 { 631 return Toolkit.getDefaultToolkit().getFontMetrics(f); 632 } 633 634 public Color getForeground(AttributeSet a) 635 { 636 return StyleConstants.getForeground(a); 637 } 638 639 public Color getBackground(AttributeSet a) 640 { 641 return StyleConstants.getBackground(a); 642 } 643 644 protected int getCompressionThreshold() 645 { 646 return compressionThreshold; 647 } 648 649 public static StyleContext getDefaultStyleContext() 650 { 651 if (defaultStyleContext == null) 652 defaultStyleContext = new StyleContext(); 653 return defaultStyleContext; 654 } 655 656 public synchronized AttributeSet addAttribute(AttributeSet old, Object name, 657 Object value) 658 { 659 AttributeSet ret; 660 if (old.getAttributeCount() + 1 < getCompressionThreshold()) 661 { 662 search.removeAttributes(search); 663 search.addAttributes(old); 664 search.addAttribute(name, value); 665 reclaim(old); 666 ret = searchImmutableSet(); 667 } 668 else 669 { 670 MutableAttributeSet mas = getMutableAttributeSet(old); 671 mas.addAttribute(name, value); 672 ret = mas; 673 } 674 return ret; 675 } 676 677 public synchronized AttributeSet addAttributes(AttributeSet old, 678 AttributeSet attributes) 679 { 680 AttributeSet ret; 681 if (old.getAttributeCount() + attributes.getAttributeCount() 682 < getCompressionThreshold()) 683 { 684 search.removeAttributes(search); 685 search.addAttributes(old); 686 search.addAttributes(attributes); 687 reclaim(old); 688 ret = searchImmutableSet(); 689 } 690 else 691 { 692 MutableAttributeSet mas = getMutableAttributeSet(old); 693 mas.addAttributes(attributes); 694 ret = mas; 695 } 696 return ret; 697 } 698 699 public AttributeSet getEmptySet() 700 { 701 return SimpleAttributeSet.EMPTY; 702 } 703 704 public void reclaim(AttributeSet attributes) 705 { 706 cleanupPool(); 707 } 708 709 public synchronized AttributeSet removeAttribute(AttributeSet old, 710 Object name) 711 { 712 AttributeSet ret; 713 if (old.getAttributeCount() - 1 <= getCompressionThreshold()) 714 { 715 search.removeAttributes(search); 716 search.addAttributes(old); 717 search.removeAttribute(name); 718 reclaim(old); 719 ret = searchImmutableSet(); 720 } 721 else 722 { 723 MutableAttributeSet mas = getMutableAttributeSet(old); 724 mas.removeAttribute(name); 725 ret = mas; 726 } 727 return ret; 728 } 729 730 public synchronized AttributeSet removeAttributes(AttributeSet old, 731 AttributeSet attributes) 732 { 733 AttributeSet ret; 734 if (old.getAttributeCount() <= getCompressionThreshold()) 735 { 736 search.removeAttributes(search); 737 search.addAttributes(old); 738 search.removeAttributes(attributes); 739 reclaim(old); 740 ret = searchImmutableSet(); 741 } 742 else 743 { 744 MutableAttributeSet mas = getMutableAttributeSet(old); 745 mas.removeAttributes(attributes); 746 ret = mas; 747 } 748 return ret; 749 } 750 751 public synchronized AttributeSet removeAttributes(AttributeSet old, 752 Enumeration<?> names) 753 { 754 AttributeSet ret; 755 if (old.getAttributeCount() <= getCompressionThreshold()) 756 { 757 search.removeAttributes(search); 758 search.addAttributes(old); 759 search.removeAttributes(names); 760 reclaim(old); 761 ret = searchImmutableSet(); 762 } 763 else 764 { 765 MutableAttributeSet mas = getMutableAttributeSet(old); 766 mas.removeAttributes(names); 767 ret = mas; 768 } 769 return ret; 770 } 771 772 /** 773 * Gets the object previously registered with registerStaticAttributeKey. 774 * 775 * @param key - the key that was registered. 776 * @return the object previously registered with registerStaticAttributeKey. 777 */ 778 public static Object getStaticAttribute(Object key) 779 { 780 if (key == null) 781 return null; 782 return readAttributeKeys.get(key); 783 } 784 785 /** 786 * Returns the String that key will be registered with 787 * registerStaticAttributeKey. 788 * 789 * @param key - the key that will be registered. 790 * @return the string the key will be registered with. 791 */ 792 public static Object getStaticAttributeKey(Object key) 793 { 794 return key.getClass().getName() + "." + key.toString(); 795 } 796 797 /** 798 * Reads a set of attributes from the given object input stream. This will 799 * attempt to restore keys that were static objects by considering only the 800 * keys that have were registered with registerStaticAttributeKey. The 801 * attributes retrieved will be placed into the given set. 802 * 803 * @param in - the stream to read from 804 * @param a - the set of attributes 805 * @throws ClassNotFoundException - may be encountered when reading from 806 * stream 807 * @throws IOException - any I/O error 808 */ 809 public static void readAttributeSet(ObjectInputStream in, 810 MutableAttributeSet a) 811 throws ClassNotFoundException, IOException 812 { 813 int count = in.readInt(); 814 for (int i = 0; i < count; i++) 815 { 816 Object key = in.readObject(); 817 Object val = in.readObject(); 818 if (readAttributeKeys != null) 819 { 820 Object staticKey = readAttributeKeys.get(key); 821 if (staticKey != null) 822 key = staticKey; 823 Object staticVal = readAttributeKeys.get(val); 824 if (staticVal != null) 825 val = staticVal; 826 } 827 a.addAttribute(key, val); 828 } 829 } 830 831 /** 832 * Serialize an attribute set in a way that is compatible with it 833 * being read in again by {@link #readAttributeSet(ObjectInputStream, MutableAttributeSet)}. 834 * In particular registered static keys are transformed properly. 835 * 836 * @param out - stream to write to 837 * @param a - the attribute set 838 * @throws IOException - any I/O error 839 */ 840 public static void writeAttributeSet(ObjectOutputStream out, AttributeSet a) 841 throws IOException 842 { 843 int count = a.getAttributeCount(); 844 out.writeInt(count); 845 Enumeration e = a.getAttributeNames(); 846 while (e.hasMoreElements()) 847 { 848 Object key = e.nextElement(); 849 // Write key. 850 if (key instanceof Serializable) 851 out.writeObject(key); 852 else 853 { 854 Object io = writeAttributeKeys.get(key); 855 if (io == null) 856 throw new NotSerializableException(key.getClass().getName() 857 + ", key: " + key); 858 out.writeObject(io); 859 } 860 // Write value. 861 Object val = a.getAttribute(key); 862 Object io = writeAttributeKeys.get(val); 863 if (val instanceof Serializable) 864 out.writeObject(io != null ? io : val); 865 else 866 { 867 if (io == null) 868 throw new NotSerializableException(val.getClass().getName()); 869 out.writeObject(io); 870 } 871 } 872 } 873 874 /** 875 * Handles reading in the attributes. 876 * @see #readAttributeSet(ObjectInputStream, MutableAttributeSet) 877 * 878 * @param in - the stream to read from 879 * @param a - the set of attributes 880 * @throws ClassNotFoundException - may be encountered when reading from stream 881 * @throws IOException - any I/O error 882 */ 883 public void readAttributes(ObjectInputStream in, MutableAttributeSet a) 884 throws ClassNotFoundException, IOException 885 { 886 readAttributeSet(in, a); 887 } 888 889 /** 890 * Handles writing of the given attributes. 891 * @see #writeAttributeSet(ObjectOutputStream, AttributeSet) 892 * 893 * @param out - stream to write to 894 * @param a - the attribute set 895 * @throws IOException - any I/O error 896 */ 897 public void writeAttributes(ObjectOutputStream out, AttributeSet a) 898 throws IOException 899 { 900 writeAttributeSet(out, a); 901 } 902 903 /** 904 * Registers an attribute key as a well-known keys. When an attribute with 905 * such a key is written to a stream, a special syntax is used so that it 906 * can be recognized when it is read back in. All attribute keys defined 907 * in <code>StyleContext</code> are registered as static keys. If you define 908 * additional attribute keys that you want to exist as nonreplicated objects, 909 * then you should register them using this method. 910 * 911 * @param key the key to register as static attribute key 912 */ 913 public static void registerStaticAttributeKey(Object key) 914 { 915 String io = key.getClass().getName() + "." + key.toString(); 916 if (writeAttributeKeys == null) 917 writeAttributeKeys = new Hashtable(); 918 if (readAttributeKeys == null) 919 readAttributeKeys = new Hashtable(); 920 writeAttributeKeys.put(key, io); 921 readAttributeKeys.put(io, key); 922 } 923 924 /** 925 * Returns a string representation of this StyleContext. 926 * 927 * @return a string representation of this StyleContext 928 */ 929 public String toString() 930 { 931 cleanupPool(); 932 StringBuilder b = new StringBuilder(); 933 Iterator i = attributeSetPool.keySet().iterator(); 934 while (i.hasNext()) 935 { 936 Object att = i.next(); 937 b.append(att); 938 b.append('\n'); 939 } 940 return b.toString(); 941 } 942 943 /** 944 * Searches the AttributeSet pool and returns a pooled instance if available, 945 * or pool a new one. 946 * 947 * @return an immutable attribute set that equals the current search key 948 */ 949 private AttributeSet searchImmutableSet() 950 { 951 SmallAttributeSet k = createSmallAttributeSet(search); 952 WeakReference ref = (WeakReference) attributeSetPool.get(k); 953 SmallAttributeSet a; 954 if (ref == null || (a = (SmallAttributeSet) ref.get()) == null) 955 { 956 a = k; 957 attributeSetPool.put(a, new WeakReference(a)); 958 } 959 return a; 960 } 961 962 /** 963 * Cleans up the attribute set pool from entries that are no longer 964 * referenced. 965 */ 966 private void cleanupPool() 967 { 968 // TODO: How else can we force cleaning up the WeakHashMap? 969 attributeSetPool.size(); 970 } 971 972 /** 973 * Returns a MutableAttributeSet that holds a. If a itself is mutable, 974 * this returns a itself, otherwise it creates a new SimpleAtttributeSet 975 * via {@link #createLargeAttributeSet(AttributeSet)}. 976 * 977 * @param a the AttributeSet to create a mutable set for 978 * 979 * @return a mutable attribute set that corresponds to a 980 */ 981 private MutableAttributeSet getMutableAttributeSet(AttributeSet a) 982 { 983 MutableAttributeSet mas; 984 if (a instanceof MutableAttributeSet) 985 mas = (MutableAttributeSet) a; 986 else 987 mas = createLargeAttributeSet(a); 988 return mas; 989 } 990 }