Save This Page
Home » openjdk-7 » javax » swing » text » [javadoc | source]
    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   }

Save This Page
Home » openjdk-7 » javax » swing » text » [javadoc | source]