jdk/src/share/classes/javax/swing/text/StyleContext.java
changeset 2 90ce3da70b43
child 1287 a04aca99c77a
equal deleted inserted replaced
0:fd16c54261b3 2:90ce3da70b43
       
     1 /*
       
     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  */
       
    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.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  */
       
    65 public 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 }