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

    1   /*
    2    * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   package javax.swing.text;
   26   
   27   import java.awt;
   28   import java.util;
   29   import java.io;
   30   
   31   import javax.swing.SwingUtilities;
   32   import javax.swing.event.ChangeListener;
   33   import javax.swing.event.EventListenerList;
   34   import javax.swing.event.ChangeEvent;
   35   import java.lang.ref.WeakReference;
   36   import java.util.WeakHashMap;
   37   
   38   import sun.font.FontUtilities;
   39   
   40   /**
   41    * A pool of styles and their associated resources.  This class determines
   42    * the lifetime of a group of resources by being a container that holds
   43    * caches for various resources such as font and color that get reused
   44    * by the various style definitions.  This can be shared by multiple
   45    * documents if desired to maximize the sharing of related resources.
   46    * <p>
   47    * This class also provides efficient support for small sets of attributes
   48    * and compresses them by sharing across uses and taking advantage of
   49    * their immutable nature.  Since many styles are replicated, the potential
   50    * for sharing is significant, and copies can be extremely cheap.
   51    * Larger sets reduce the possibility of sharing, and therefore revert
   52    * automatically to a less space-efficient implementation.
   53    * <p>
   54    * <strong>Warning:</strong>
   55    * Serialized objects of this class will not be compatible with
   56    * future Swing releases. The current serialization support is
   57    * appropriate for short term storage or RMI between applications running
   58    * the same version of Swing.  As of 1.4, support for long term storage
   59    * of all JavaBeans<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 = 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 (! FontUtilities.fontSupportsDefaultEncoding(f)) {
  267                   f = FontUtilities.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<SmallAttributeSet> reference = attributesPool.get(key);
  521           SmallAttributeSet a;
  522           if (reference == null || (a = reference.get()) == null) {
  523               a = key;
  524               attributesPool.put(a, new WeakReference<SmallAttributeSet>(a));
  525           }
  526           return a;
  527       }
  528   
  529       /**
  530        * Creates a mutable attribute set to hand out because the current
  531        * needs are too big to try and use a shared version.
  532        */
  533       MutableAttributeSet getMutableAttributeSet(AttributeSet a) {
  534           if (a instanceof MutableAttributeSet &&
  535               a != SimpleAttributeSet.EMPTY) {
  536               return (MutableAttributeSet) a;
  537           }
  538           return createLargeAttributeSet(a);
  539       }
  540   
  541       /**
  542        * Converts a StyleContext to a String.
  543        *
  544        * @return the string
  545        */
  546       public String toString() {
  547           removeUnusedSets();
  548           String s = "";
  549           for (SmallAttributeSet set : attributesPool.keySet()) {
  550               s = s + set + "\n";
  551           }
  552           return s;
  553       }
  554   
  555       // --- serialization ---------------------------------------------
  556   
  557       /**
  558        * Context-specific handling of writing out attributes
  559        */
  560       public void writeAttributes(ObjectOutputStream out,
  561                                     AttributeSet a) throws IOException {
  562           writeAttributeSet(out, a);
  563       }
  564   
  565       /**
  566        * Context-specific handling of reading in attributes
  567        */
  568       public void readAttributes(ObjectInputStream in,
  569                                  MutableAttributeSet a) throws ClassNotFoundException, IOException {
  570           readAttributeSet(in, a);
  571       }
  572   
  573       /**
  574        * Writes a set of attributes to the given object stream
  575        * for the purpose of serialization.  This will take
  576        * special care to deal with static attribute keys that
  577        * have been registered wit the
  578        * <code>registerStaticAttributeKey</code> method.
  579        * Any attribute key not regsitered as a static key
  580        * will be serialized directly.  All values are expected
  581        * to be serializable.
  582        *
  583        * @param out the output stream
  584        * @param a the attribute set
  585        * @exception IOException on any I/O error
  586        */
  587       public static void writeAttributeSet(ObjectOutputStream out,
  588                                            AttributeSet a) throws IOException {
  589           int n = a.getAttributeCount();
  590           out.writeInt(n);
  591           Enumeration keys = a.getAttributeNames();
  592           while (keys.hasMoreElements()) {
  593               Object key = keys.nextElement();
  594               if (key instanceof Serializable) {
  595                   out.writeObject(key);
  596               } else {
  597                   Object ioFmt = freezeKeyMap.get(key);
  598                   if (ioFmt == null) {
  599                       throw new NotSerializableException(key.getClass().
  600                                    getName() + " is not serializable as a key in an AttributeSet");
  601                   }
  602                   out.writeObject(ioFmt);
  603               }
  604               Object value = a.getAttribute(key);
  605               Object ioFmt = freezeKeyMap.get(value);
  606               if (value instanceof Serializable) {
  607                   out.writeObject((ioFmt != null) ? ioFmt : value);
  608               } else {
  609                   if (ioFmt == null) {
  610                       throw new NotSerializableException(value.getClass().
  611                                    getName() + " is not serializable as a value in an AttributeSet");
  612                   }
  613                   out.writeObject(ioFmt);
  614               }
  615           }
  616       }
  617   
  618       /**
  619        * Reads a set of attributes from the given object input
  620        * stream that have been previously written out with
  621        * <code>writeAttributeSet</code>.  This will try to restore
  622        * keys that were static objects to the static objects in
  623        * the current virtual machine considering only those keys
  624        * that have been registered with the
  625        * <code>registerStaticAttributeKey</code> method.
  626        * The attributes retrieved from the stream will be placed
  627        * into the given mutable set.
  628        *
  629        * @param in the object stream to read the attribute data from.
  630        * @param a  the attribute set to place the attribute
  631        *   definitions in.
  632        * @exception ClassNotFoundException passed upward if encountered
  633        *  when reading the object stream.
  634        * @exception IOException passed upward if encountered when
  635        *  reading the object stream.
  636        */
  637       public static void readAttributeSet(ObjectInputStream in,
  638           MutableAttributeSet a) throws ClassNotFoundException, IOException {
  639   
  640           int n = in.readInt();
  641           for (int i = 0; i < n; i++) {
  642               Object key = in.readObject();
  643               Object value = in.readObject();
  644               if (thawKeyMap != null) {
  645                   Object staticKey = thawKeyMap.get(key);
  646                   if (staticKey != null) {
  647                       key = staticKey;
  648                   }
  649                   Object staticValue = thawKeyMap.get(value);
  650                   if (staticValue != null) {
  651                       value = staticValue;
  652                   }
  653               }
  654               a.addAttribute(key, value);
  655           }
  656       }
  657   
  658       /**
  659        * Registers an object as a static object that is being
  660        * used as a key in attribute sets.  This allows the key
  661        * to be treated specially for serialization.
  662        * <p>
  663        * For operation under a 1.1 virtual machine, this
  664        * uses the value returned by <code>toString</code>
  665        * concatenated to the classname.  The value returned
  666        * by toString should not have the class reference
  667        * in it (ie it should be reimplemented from the
  668        * definition in Object) in order to be the same when
  669        * recomputed later.
  670        *
  671        * @param key the non-null object key
  672        */
  673       public static void registerStaticAttributeKey(Object key) {
  674           String ioFmt = key.getClass().getName() + "." + key.toString();
  675           if (freezeKeyMap == null) {
  676               freezeKeyMap = new Hashtable<Object, String>();
  677               thawKeyMap = new Hashtable<String, Object>();
  678           }
  679           freezeKeyMap.put(key, ioFmt);
  680           thawKeyMap.put(ioFmt, key);
  681       }
  682   
  683       /**
  684        * Returns the object previously registered with
  685        * <code>registerStaticAttributeKey</code>.
  686        */
  687       public static Object getStaticAttribute(Object key) {
  688           if (thawKeyMap == null || key == null) {
  689               return null;
  690           }
  691           return thawKeyMap.get(key);
  692       }
  693   
  694       /**
  695        * Returns the String that <code>key</code> will be registered with
  696        * @see #getStaticAttribute
  697        * @see #registerStaticAttributeKey
  698        */
  699       public static Object getStaticAttributeKey(Object key) {
  700           return key.getClass().getName() + "." + key.toString();
  701       }
  702   
  703       private void writeObject(java.io.ObjectOutputStream s)
  704           throws IOException
  705       {
  706           // clean out unused sets before saving
  707           removeUnusedSets();
  708   
  709           s.defaultWriteObject();
  710       }
  711   
  712       private void readObject(ObjectInputStream s)
  713         throws ClassNotFoundException, IOException
  714       {
  715           fontSearch = new FontKey(null, 0, 0);
  716           fontTable = new Hashtable<FontKey, Font>();
  717           search = new SimpleAttributeSet();
  718           attributesPool = Collections.
  719                   synchronizedMap(new WeakHashMap<SmallAttributeSet, WeakReference<SmallAttributeSet>>());
  720           s.defaultReadObject();
  721       }
  722   
  723       // --- variables ---------------------------------------------------
  724   
  725       /**
  726        * The name given to the default logical style attached
  727        * to paragraphs.
  728        */
  729       public static final String DEFAULT_STYLE = "default";
  730   
  731       private static Hashtable<Object, String> freezeKeyMap;
  732       private static Hashtable<String, Object> thawKeyMap;
  733   
  734       private Style styles;
  735       private transient FontKey fontSearch = new FontKey(null, 0, 0);
  736       private transient Hashtable<FontKey, Font> fontTable = new Hashtable<FontKey, Font>();
  737   
  738       private transient Map<SmallAttributeSet, WeakReference<SmallAttributeSet>> attributesPool = Collections.
  739               synchronizedMap(new WeakHashMap<SmallAttributeSet, WeakReference<SmallAttributeSet>>());
  740       private transient MutableAttributeSet search = new SimpleAttributeSet();
  741   
  742       /**
  743        * Number of immutable sets that are not currently
  744        * being used.  This helps indicate when the sets need
  745        * to be cleaned out of the hashtable they are stored
  746        * in.
  747        */
  748       private int unusedSets;
  749   
  750       /**
  751        * The threshold for no longer sharing the set of attributes
  752        * in an immutable table.
  753        */
  754       static final int THRESHOLD = 9;
  755   
  756       /**
  757        * This class holds a small number of attributes in an array.
  758        * The storage format is key, value, key, value, etc.  The size
  759        * of the set is the length of the array divided by two.  By
  760        * default, this is the class that will be used to store attributes
  761        * when held in the compact sharable form.
  762        */
  763       public class SmallAttributeSet implements AttributeSet {
  764   
  765           public SmallAttributeSet(Object[] attributes) {
  766               this.attributes = attributes;
  767               updateResolveParent();
  768           }
  769   
  770           public SmallAttributeSet(AttributeSet attrs) {
  771               int n = attrs.getAttributeCount();
  772               Object[] tbl = new Object[2 * n];
  773               Enumeration names = attrs.getAttributeNames();
  774               int i = 0;
  775               while (names.hasMoreElements()) {
  776                   tbl[i] = names.nextElement();
  777                   tbl[i+1] = attrs.getAttribute(tbl[i]);
  778                   i += 2;
  779               }
  780               attributes = tbl;
  781               updateResolveParent();
  782           }
  783   
  784           private void updateResolveParent() {
  785               resolveParent = null;
  786               Object[] tbl = attributes;
  787               for (int i = 0; i < tbl.length; i += 2) {
  788                   if (tbl[i] == StyleConstants.ResolveAttribute) {
  789                       resolveParent = (AttributeSet)tbl[i + 1];
  790                       break;
  791                   }
  792               }
  793           }
  794   
  795           Object getLocalAttribute(Object nm) {
  796               if (nm == StyleConstants.ResolveAttribute) {
  797                   return resolveParent;
  798               }
  799               Object[] tbl = attributes;
  800               for (int i = 0; i < tbl.length; i += 2) {
  801                   if (nm.equals(tbl[i])) {
  802                       return tbl[i+1];
  803                   }
  804               }
  805               return null;
  806           }
  807   
  808           // --- Object methods -------------------------
  809   
  810           /**
  811            * Returns a string showing the key/value pairs
  812            */
  813           public String toString() {
  814               String s = "{";
  815               Object[] tbl = attributes;
  816               for (int i = 0; i < tbl.length; i += 2) {
  817                   if (tbl[i+1] instanceof AttributeSet) {
  818                       // don't recurse
  819                       s = s + tbl[i] + "=" + "AttributeSet" + ",";
  820                   } else {
  821                       s = s + tbl[i] + "=" + tbl[i+1] + ",";
  822                   }
  823               }
  824               s = s + "}";
  825               return s;
  826           }
  827   
  828           /**
  829            * Returns a hashcode for this set of attributes.
  830            * @return     a hashcode value for this set of attributes.
  831            */
  832           public int hashCode() {
  833               int code = 0;
  834               Object[] tbl = attributes;
  835               for (int i = 1; i < tbl.length; i += 2) {
  836                   code ^= tbl[i].hashCode();
  837               }
  838               return code;
  839           }
  840   
  841           /**
  842            * Compares this object to the specifed object.
  843            * The result is <code>true</code> if the object is an equivalent
  844            * set of attributes.
  845            * @param     obj   the object to compare with.
  846            * @return    <code>true</code> if the objects are equal;
  847            *            <code>false</code> otherwise.
  848            */
  849           public boolean equals(Object obj) {
  850               if (obj instanceof AttributeSet) {
  851                   AttributeSet attrs = (AttributeSet) obj;
  852                   return ((getAttributeCount() == attrs.getAttributeCount()) &&
  853                           containsAttributes(attrs));
  854               }
  855               return false;
  856           }
  857   
  858           /**
  859            * Clones a set of attributes.  Since the set is immutable, a
  860            * clone is basically the same set.
  861            *
  862            * @return the set of attributes
  863            */
  864           public Object clone() {
  865               return this;
  866           }
  867   
  868           //  --- AttributeSet methods ----------------------------
  869   
  870           /**
  871            * Gets the number of attributes that are defined.
  872            *
  873            * @return the number of attributes
  874            * @see AttributeSet#getAttributeCount
  875            */
  876           public int getAttributeCount() {
  877               return attributes.length / 2;
  878           }
  879   
  880           /**
  881            * Checks whether a given attribute is defined.
  882            *
  883            * @param key the attribute key
  884            * @return true if the attribute is defined
  885            * @see AttributeSet#isDefined
  886            */
  887           public boolean isDefined(Object key) {
  888               Object[] a = attributes;
  889               int n = a.length;
  890               for (int i = 0; i < n; i += 2) {
  891                   if (key.equals(a[i])) {
  892                       return true;
  893                   }
  894               }
  895               return false;
  896           }
  897   
  898           /**
  899            * Checks whether two attribute sets are equal.
  900            *
  901            * @param attr the attribute set to check against
  902            * @return true if the same
  903            * @see AttributeSet#isEqual
  904            */
  905           public boolean isEqual(AttributeSet attr) {
  906               if (attr instanceof SmallAttributeSet) {
  907                   return attr == this;
  908               }
  909               return ((getAttributeCount() == attr.getAttributeCount()) &&
  910                       containsAttributes(attr));
  911           }
  912   
  913           /**
  914            * Copies a set of attributes.
  915            *
  916            * @return the copy
  917            * @see AttributeSet#copyAttributes
  918            */
  919           public AttributeSet copyAttributes() {
  920               return this;
  921           }
  922   
  923           /**
  924            * Gets the value of an attribute.
  925            *
  926            * @param key the attribute name
  927            * @return the attribute value
  928            * @see AttributeSet#getAttribute
  929            */
  930           public Object getAttribute(Object key) {
  931               Object value = getLocalAttribute(key);
  932               if (value == null) {
  933                   AttributeSet parent = getResolveParent();
  934                   if (parent != null)
  935                       value = parent.getAttribute(key);
  936               }
  937               return value;
  938           }
  939   
  940           /**
  941            * Gets the names of all attributes.
  942            *
  943            * @return the attribute names
  944            * @see AttributeSet#getAttributeNames
  945            */
  946           public Enumeration<?> getAttributeNames() {
  947               return new KeyEnumeration(attributes);
  948           }
  949   
  950           /**
  951            * Checks whether a given attribute name/value is defined.
  952            *
  953            * @param name the attribute name
  954            * @param value the attribute value
  955            * @return true if the name/value is defined
  956            * @see AttributeSet#containsAttribute
  957            */
  958           public boolean containsAttribute(Object name, Object value) {
  959               return value.equals(getAttribute(name));
  960           }
  961   
  962           /**
  963            * Checks whether the attribute set contains all of
  964            * the given attributes.
  965            *
  966            * @param attrs the attributes to check
  967            * @return true if the element contains all the attributes
  968            * @see AttributeSet#containsAttributes
  969            */
  970           public boolean containsAttributes(AttributeSet attrs) {
  971               boolean result = true;
  972   
  973               Enumeration names = attrs.getAttributeNames();
  974               while (result && names.hasMoreElements()) {
  975                   Object name = names.nextElement();
  976                   result = attrs.getAttribute(name).equals(getAttribute(name));
  977               }
  978   
  979               return result;
  980           }
  981   
  982           /**
  983            * If not overriden, the resolving parent defaults to
  984            * the parent element.
  985            *
  986            * @return the attributes from the parent
  987            * @see AttributeSet#getResolveParent
  988            */
  989           public AttributeSet getResolveParent() {
  990               return resolveParent;
  991           }
  992   
  993           // --- variables -----------------------------------------
  994   
  995           Object[] attributes;
  996           // This is also stored in attributes
  997           AttributeSet resolveParent;
  998       }
  999   
 1000       /**
 1001        * An enumeration of the keys in a SmallAttributeSet.
 1002        */
 1003       class KeyEnumeration implements Enumeration<Object> {
 1004   
 1005           KeyEnumeration(Object[] attr) {
 1006               this.attr = attr;
 1007               i = 0;
 1008           }
 1009   
 1010           /**
 1011            * Tests if this enumeration contains more elements.
 1012            *
 1013            * @return  <code>true</code> if this enumeration contains more elements;
 1014            *          <code>false</code> otherwise.
 1015            * @since   JDK1.0
 1016            */
 1017           public boolean hasMoreElements() {
 1018               return i < attr.length;
 1019           }
 1020   
 1021           /**
 1022            * Returns the next element of this enumeration.
 1023            *
 1024            * @return     the next element of this enumeration.
 1025            * @exception  NoSuchElementException  if no more elements exist.
 1026            * @since      JDK1.0
 1027            */
 1028           public Object nextElement() {
 1029               if (i < attr.length) {
 1030                   Object o = attr[i];
 1031                   i += 2;
 1032                   return o;
 1033               }
 1034               throw new NoSuchElementException();
 1035           }
 1036   
 1037           Object[] attr;
 1038           int i;
 1039       }
 1040   
 1041       /**
 1042        * Sorts the key strings so that they can be very quickly compared
 1043        * in the attribute set searchs.
 1044        */
 1045       class KeyBuilder {
 1046   
 1047           public void initialize(AttributeSet a) {
 1048               if (a instanceof SmallAttributeSet) {
 1049                   initialize(((SmallAttributeSet)a).attributes);
 1050               } else {
 1051                   keys.removeAllElements();
 1052                   data.removeAllElements();
 1053                   Enumeration names = a.getAttributeNames();
 1054                   while (names.hasMoreElements()) {
 1055                       Object name = names.nextElement();
 1056                       addAttribute(name, a.getAttribute(name));
 1057                   }
 1058               }
 1059           }
 1060   
 1061           /**
 1062            * Initialize with a set of already sorted
 1063            * keys (data from an existing SmallAttributeSet).
 1064            */
 1065           private void initialize(Object[] sorted) {
 1066               keys.removeAllElements();
 1067               data.removeAllElements();
 1068               int n = sorted.length;
 1069               for (int i = 0; i < n; i += 2) {
 1070                   keys.addElement(sorted[i]);
 1071                   data.addElement(sorted[i+1]);
 1072               }
 1073           }
 1074   
 1075           /**
 1076            * Creates a table of sorted key/value entries
 1077            * suitable for creation of an instance of
 1078            * SmallAttributeSet.
 1079            */
 1080           public Object[] createTable() {
 1081               int n = keys.size();
 1082               Object[] tbl = new Object[2 * n];
 1083               for (int i = 0; i < n; i ++) {
 1084                   int offs = 2 * i;
 1085                   tbl[offs] = keys.elementAt(i);
 1086                   tbl[offs + 1] = data.elementAt(i);
 1087               }
 1088               return tbl;
 1089           }
 1090   
 1091           /**
 1092            * The number of key/value pairs contained
 1093            * in the current key being forged.
 1094            */
 1095           int getCount() {
 1096               return keys.size();
 1097           }
 1098   
 1099           /**
 1100            * Adds a key/value to the set.
 1101            */
 1102           public void addAttribute(Object key, Object value) {
 1103               keys.addElement(key);
 1104               data.addElement(value);
 1105           }
 1106   
 1107           /**
 1108            * Adds a set of key/value pairs to the set.
 1109            */
 1110           public void addAttributes(AttributeSet attr) {
 1111               if (attr instanceof SmallAttributeSet) {
 1112                   // avoid searching the keys, they are already interned.
 1113                   Object[] tbl = ((SmallAttributeSet)attr).attributes;
 1114                   int n = tbl.length;
 1115                   for (int i = 0; i < n; i += 2) {
 1116                       addAttribute(tbl[i], tbl[i+1]);
 1117                   }
 1118               } else {
 1119                   Enumeration names = attr.getAttributeNames();
 1120                   while (names.hasMoreElements()) {
 1121                       Object name = names.nextElement();
 1122                       addAttribute(name, attr.getAttribute(name));
 1123                   }
 1124               }
 1125           }
 1126   
 1127           /**
 1128            * Removes the given name from the set.
 1129            */
 1130           public void removeAttribute(Object key) {
 1131               int n = keys.size();
 1132               for (int i = 0; i < n; i++) {
 1133                   if (keys.elementAt(i).equals(key)) {
 1134                       keys.removeElementAt(i);
 1135                       data.removeElementAt(i);
 1136                       return;
 1137                   }
 1138               }
 1139           }
 1140   
 1141           /**
 1142            * Removes the set of keys from the set.
 1143            */
 1144           public void removeAttributes(Enumeration names) {
 1145               while (names.hasMoreElements()) {
 1146                   Object name = names.nextElement();
 1147                   removeAttribute(name);
 1148               }
 1149           }
 1150   
 1151           /**
 1152            * Removes the set of matching attributes from the set.
 1153            */
 1154           public void removeAttributes(AttributeSet attr) {
 1155               Enumeration names = attr.getAttributeNames();
 1156               while (names.hasMoreElements()) {
 1157                   Object name = names.nextElement();
 1158                   Object value = attr.getAttribute(name);
 1159                   removeSearchAttribute(name, value);
 1160               }
 1161           }
 1162   
 1163           private void removeSearchAttribute(Object ikey, Object value) {
 1164               int n = keys.size();
 1165               for (int i = 0; i < n; i++) {
 1166                   if (keys.elementAt(i).equals(ikey)) {
 1167                       if (data.elementAt(i).equals(value)) {
 1168                           keys.removeElementAt(i);
 1169                           data.removeElementAt(i);
 1170                       }
 1171                       return;
 1172                   }
 1173               }
 1174           }
 1175   
 1176           private Vector<Object> keys = new Vector<Object>();
 1177           private Vector<Object> data = new Vector<Object>();
 1178       }
 1179   
 1180       /**
 1181        * key for a font table
 1182        */
 1183       static class FontKey {
 1184   
 1185           private String family;
 1186           private int style;
 1187           private int size;
 1188   
 1189           /**
 1190            * Constructs a font key.
 1191            */
 1192           public FontKey(String family, int style, int size) {
 1193               setValue(family, style, size);
 1194           }
 1195   
 1196           public void setValue(String family, int style, int size) {
 1197               this.family = (family != null) ? family.intern() : null;
 1198               this.style = style;
 1199               this.size = size;
 1200           }
 1201   
 1202           /**
 1203            * Returns a hashcode for this font.
 1204            * @return     a hashcode value for this font.
 1205            */
 1206           public int hashCode() {
 1207               int fhash = (family != null) ? family.hashCode() : 0;
 1208               return fhash ^ style ^ size;
 1209           }
 1210   
 1211           /**
 1212            * Compares this object to the specifed object.
 1213            * The result is <code>true</code> if and only if the argument is not
 1214            * <code>null</code> and is a <code>Font</code> object with the same
 1215            * name, style, and point size as this font.
 1216            * @param     obj   the object to compare this font with.
 1217            * @return    <code>true</code> if the objects are equal;
 1218            *            <code>false</code> otherwise.
 1219            */
 1220           public boolean equals(Object obj) {
 1221               if (obj instanceof FontKey) {
 1222                   FontKey font = (FontKey)obj;
 1223                   return (size == font.size) && (style == font.style) && (family == font.family);
 1224               }
 1225               return false;
 1226           }
 1227   
 1228       }
 1229   
 1230       /**
 1231        * A collection of attributes, typically used to represent
 1232        * character and paragraph styles.  This is an implementation
 1233        * of MutableAttributeSet that can be observed if desired.
 1234        * These styles will take advantage of immutability while
 1235        * the sets are small enough, and may be substantially more
 1236        * efficient than something like SimpleAttributeSet.
 1237        * <p>
 1238        * <strong>Warning:</strong>
 1239        * Serialized objects of this class will not be compatible with
 1240        * future Swing releases. The current serialization support is
 1241        * appropriate for short term storage or RMI between applications running
 1242        * the same version of Swing.  As of 1.4, support for long term storage
 1243        * of all JavaBeans<sup><font size="-2">TM</font></sup>
 1244        * has been added to the <code>java.beans</code> package.
 1245        * Please see {@link java.beans.XMLEncoder}.
 1246        */
 1247       public class NamedStyle implements Style, Serializable {
 1248   
 1249           /**
 1250            * Creates a new named style.
 1251            *
 1252            * @param name the style name, null for unnamed
 1253            * @param parent the parent style, null if none
 1254            * @since 1.4
 1255            */
 1256           public NamedStyle(String name, Style parent) {
 1257               attributes = getEmptySet();
 1258               if (name != null) {
 1259                   setName(name);
 1260               }
 1261               if (parent != null) {
 1262                   setResolveParent(parent);
 1263               }
 1264           }
 1265   
 1266           /**
 1267            * Creates a new named style.
 1268            *
 1269            * @param parent the parent style, null if none
 1270            * @since 1.4
 1271            */
 1272           public NamedStyle(Style parent) {
 1273               this(null, parent);
 1274           }
 1275   
 1276           /**
 1277            * Creates a new named style, with a null name and parent.
 1278            */
 1279           public NamedStyle() {
 1280               attributes = getEmptySet();
 1281           }
 1282   
 1283           /**
 1284            * Converts the style to a string.
 1285            *
 1286            * @return the string
 1287            */
 1288           public String toString() {
 1289               return "NamedStyle:" + getName() + " " + attributes;
 1290           }
 1291   
 1292           /**
 1293            * Fetches the name of the style.   A style is not required to be named,
 1294            * so null is returned if there is no name associated with the style.
 1295            *
 1296            * @return the name
 1297            */
 1298           public String getName() {
 1299               if (isDefined(StyleConstants.NameAttribute)) {
 1300                   return getAttribute(StyleConstants.NameAttribute).toString();
 1301               }
 1302               return null;
 1303           }
 1304   
 1305           /**
 1306            * Changes the name of the style.  Does nothing with a null name.
 1307            *
 1308            * @param name the new name
 1309            */
 1310           public void setName(String name) {
 1311               if (name != null) {
 1312                   this.addAttribute(StyleConstants.NameAttribute, name);
 1313               }
 1314           }
 1315   
 1316           /**
 1317            * Adds a change listener.
 1318            *
 1319            * @param l the change listener
 1320            */
 1321           public void addChangeListener(ChangeListener l) {
 1322               listenerList.add(ChangeListener.class, l);
 1323           }
 1324   
 1325           /**
 1326            * Removes a change listener.
 1327            *
 1328            * @param l the change listener
 1329            */
 1330           public void removeChangeListener(ChangeListener l) {
 1331               listenerList.remove(ChangeListener.class, l);
 1332           }
 1333   
 1334   
 1335           /**
 1336            * Returns an array of all the <code>ChangeListener</code>s added
 1337            * to this NamedStyle with addChangeListener().
 1338            *
 1339            * @return all of the <code>ChangeListener</code>s added or an empty
 1340            *         array if no listeners have been added
 1341            * @since 1.4
 1342            */
 1343           public ChangeListener[] getChangeListeners() {
 1344               return listenerList.getListeners(ChangeListener.class);
 1345           }
 1346   
 1347   
 1348           /**
 1349            * Notifies all listeners that have registered interest for
 1350            * notification on this event type.  The event instance
 1351            * is lazily created using the parameters passed into
 1352            * the fire method.
 1353            *
 1354            * @see EventListenerList
 1355            */
 1356           protected void fireStateChanged() {
 1357               // Guaranteed to return a non-null array
 1358               Object[] listeners = listenerList.getListenerList();
 1359               // Process the listeners last to first, notifying
 1360               // those that are interested in this event
 1361               for (int i = listeners.length-2; i>=0; i-=2) {
 1362                   if (listeners[i]==ChangeListener.class) {
 1363                       // Lazily create the event:
 1364                       if (changeEvent == null)
 1365                           changeEvent = new ChangeEvent(this);
 1366                       ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
 1367                   }
 1368               }
 1369           }
 1370   
 1371           /**
 1372            * Return an array of all the listeners of the given type that
 1373            * were added to this model.
 1374            *
 1375            * @return all of the objects receiving <em>listenerType</em> notifications
 1376            *          from this model
 1377            *
 1378            * @since 1.3
 1379            */
 1380           public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
 1381               return listenerList.getListeners(listenerType);
 1382           }
 1383   
 1384           // --- AttributeSet ----------------------------
 1385           // delegated to the immutable field "attributes"
 1386   
 1387           /**
 1388            * Gets the number of attributes that are defined.
 1389            *
 1390            * @return the number of attributes >= 0
 1391            * @see AttributeSet#getAttributeCount
 1392            */
 1393           public int getAttributeCount() {
 1394               return attributes.getAttributeCount();
 1395           }
 1396   
 1397           /**
 1398            * Checks whether a given attribute is defined.
 1399            *
 1400            * @param attrName the non-null attribute name
 1401            * @return true if the attribute is defined
 1402            * @see AttributeSet#isDefined
 1403            */
 1404           public boolean isDefined(Object attrName) {
 1405               return attributes.isDefined(attrName);
 1406           }
 1407   
 1408           /**
 1409            * Checks whether two attribute sets are equal.
 1410            *
 1411            * @param attr the attribute set to check against
 1412            * @return true if the same
 1413            * @see AttributeSet#isEqual
 1414            */
 1415           public boolean isEqual(AttributeSet attr) {
 1416               return attributes.isEqual(attr);
 1417           }
 1418   
 1419           /**
 1420            * Copies a set of attributes.
 1421            *
 1422            * @return the copy
 1423            * @see AttributeSet#copyAttributes
 1424            */
 1425           public AttributeSet copyAttributes() {
 1426               NamedStyle a = new NamedStyle();
 1427               a.attributes = attributes.copyAttributes();
 1428               return a;
 1429           }
 1430   
 1431           /**
 1432            * Gets the value of an attribute.
 1433            *
 1434            * @param attrName the non-null attribute name
 1435            * @return the attribute value
 1436            * @see AttributeSet#getAttribute
 1437            */
 1438           public Object getAttribute(Object attrName) {
 1439               return attributes.getAttribute(attrName);
 1440           }
 1441   
 1442           /**
 1443            * Gets the names of all attributes.
 1444            *
 1445            * @return the attribute names as an enumeration
 1446            * @see AttributeSet#getAttributeNames
 1447            */
 1448           public Enumeration<?> getAttributeNames() {
 1449               return attributes.getAttributeNames();
 1450           }
 1451   
 1452           /**
 1453            * Checks whether a given attribute name/value is defined.
 1454            *
 1455            * @param name the non-null attribute name
 1456            * @param value the attribute value
 1457            * @return true if the name/value is defined
 1458            * @see AttributeSet#containsAttribute
 1459            */
 1460           public boolean containsAttribute(Object name, Object value) {
 1461               return attributes.containsAttribute(name, value);
 1462           }
 1463   
 1464   
 1465           /**
 1466            * Checks whether the element contains all the attributes.
 1467            *
 1468            * @param attrs the attributes to check
 1469            * @return true if the element contains all the attributes
 1470            * @see AttributeSet#containsAttributes
 1471            */
 1472           public boolean containsAttributes(AttributeSet attrs) {
 1473               return attributes.containsAttributes(attrs);
 1474           }
 1475   
 1476           /**
 1477            * Gets attributes from the parent.
 1478            * If not overriden, the resolving parent defaults to
 1479            * the parent element.
 1480            *
 1481            * @return the attributes from the parent
 1482            * @see AttributeSet#getResolveParent
 1483            */
 1484           public AttributeSet getResolveParent() {
 1485               return attributes.getResolveParent();
 1486           }
 1487   
 1488           // --- MutableAttributeSet ----------------------------------
 1489           // should fetch a new immutable record for the field
 1490           // "attributes".
 1491   
 1492           /**
 1493            * Adds an attribute.
 1494            *
 1495            * @param name the non-null attribute name
 1496            * @param value the attribute value
 1497            * @see MutableAttributeSet#addAttribute
 1498            */
 1499           public void addAttribute(Object name, Object value) {
 1500               StyleContext context = StyleContext.this;
 1501               attributes = context.addAttribute(attributes, name, value);
 1502               fireStateChanged();
 1503           }
 1504   
 1505           /**
 1506            * Adds a set of attributes to the element.
 1507            *
 1508            * @param attr the attributes to add
 1509            * @see MutableAttributeSet#addAttribute
 1510            */
 1511           public void addAttributes(AttributeSet attr) {
 1512               StyleContext context = StyleContext.this;
 1513               attributes = context.addAttributes(attributes, attr);
 1514               fireStateChanged();
 1515           }
 1516   
 1517           /**
 1518            * Removes an attribute from the set.
 1519            *
 1520            * @param name the non-null attribute name
 1521            * @see MutableAttributeSet#removeAttribute
 1522            */
 1523           public void removeAttribute(Object name) {
 1524               StyleContext context = StyleContext.this;
 1525               attributes = context.removeAttribute(attributes, name);
 1526               fireStateChanged();
 1527           }
 1528   
 1529           /**
 1530            * Removes a set of attributes for the element.
 1531            *
 1532            * @param names the attribute names
 1533            * @see MutableAttributeSet#removeAttributes
 1534            */
 1535           public void removeAttributes(Enumeration<?> names) {
 1536               StyleContext context = StyleContext.this;
 1537               attributes = context.removeAttributes(attributes, names);
 1538               fireStateChanged();
 1539           }
 1540   
 1541           /**
 1542            * Removes a set of attributes for the element.
 1543            *
 1544            * @param attrs the attributes
 1545            * @see MutableAttributeSet#removeAttributes
 1546            */
 1547           public void removeAttributes(AttributeSet attrs) {
 1548               StyleContext context = StyleContext.this;
 1549               if (attrs == this) {
 1550                   attributes = context.getEmptySet();
 1551               } else {
 1552                   attributes = context.removeAttributes(attributes, attrs);
 1553               }
 1554               fireStateChanged();
 1555           }
 1556   
 1557           /**
 1558            * Sets the resolving parent.
 1559            *
 1560            * @param parent the parent, null if none
 1561            * @see MutableAttributeSet#setResolveParent
 1562            */
 1563           public void setResolveParent(AttributeSet parent) {
 1564               if (parent != null) {
 1565                   addAttribute(StyleConstants.ResolveAttribute, parent);
 1566               } else {
 1567                   removeAttribute(StyleConstants.ResolveAttribute);
 1568               }
 1569           }
 1570   
 1571           // --- serialization ---------------------------------------------
 1572   
 1573           private void writeObject(ObjectOutputStream s) throws IOException {
 1574               s.defaultWriteObject();
 1575               writeAttributeSet(s, attributes);
 1576           }
 1577   
 1578           private void readObject(ObjectInputStream s)
 1579               throws ClassNotFoundException, IOException
 1580           {
 1581               s.defaultReadObject();
 1582               attributes = SimpleAttributeSet.EMPTY;
 1583               readAttributeSet(s, this);
 1584           }
 1585   
 1586           // --- member variables -----------------------------------------------
 1587   
 1588           /**
 1589            * The change listeners for the model.
 1590            */
 1591           protected EventListenerList listenerList = new EventListenerList();
 1592   
 1593           /**
 1594            * Only one ChangeEvent is needed per model instance since the
 1595            * event's only (read-only) state is the source property.  The source
 1596            * of events generated here is always "this".
 1597            */
 1598           protected transient ChangeEvent changeEvent = null;
 1599   
 1600           /**
 1601            * Inner AttributeSet implementation, which may be an
 1602            * immutable unique set being shared.
 1603            */
 1604           private transient AttributeSet attributes;
 1605   
 1606       }
 1607   
 1608       static {
 1609           // initialize the static key registry with the StyleConstants keys
 1610           try {
 1611               int n = StyleConstants.keys.length;
 1612               for (int i = 0; i < n; i++) {
 1613                   StyleContext.registerStaticAttributeKey(StyleConstants.keys[i]);
 1614               }
 1615           } catch (Throwable e) {
 1616               e.printStackTrace();
 1617           }
 1618       }
 1619   
 1620   
 1621   }

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