- /*
 - * @(#)StyleContext.java 1.65 00/02/02
 - *
 - * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
 - *
 - * This software is the proprietary information of Sun Microsystems, Inc.
 - * Use is subject to license terms.
 - *
 - */
 - package javax.swing.text;
 - import java.awt.*;
 - import java.util.*;
 - import java.io.*;
 - import javax.swing.SwingUtilities;
 - import javax.swing.event.ChangeListener;
 - import javax.swing.event.EventListenerList;
 - import javax.swing.event.ChangeEvent;
 - /**
 - * A pool of styles and their associated resources. This class determines
 - * the lifetime of a group of resources by being a container that holds
 - * caches for various resources such as font and color that get reused
 - * by the various style definitions. This can be shared by multiple
 - * documents if desired to maximize the sharing of related resources.
 - * <p>
 - * This class also provides efficient support for small sets of attributes
 - * and compresses them by sharing across uses and taking advantage of
 - * their immutable nature. Since many styles are replicated, the potential
 - * for sharing is significant, and copies can be extremely cheap.
 - * Larger sets reduce the possibility of sharing, and therefore revert
 - * automatically to a less space-efficient implementation.
 - * <p>
 - * <strong>Warning:</strong>
 - * Serialized objects of this class will not be compatible with
 - * future Swing releases. The current serialization support is appropriate
 - * for short term storage or RMI between applications running the same
 - * version of Swing. A future release of Swing will provide support for
 - * long term persistence.
 - *
 - * @author Timothy Prinzing
 - * @version 1.65 02/02/00
 - */
 - public class StyleContext implements Serializable, AbstractDocument.AttributeContext {
 - /**
 - * Returns default AttributeContext shared by all documents that
 - * don't bother to define/supply their own context.
 - *
 - * @return the context
 - */
 - public static final StyleContext getDefaultStyleContext() {
 - if (defaultContext == null) {
 - defaultContext = new StyleContext();
 - }
 - return defaultContext;
 - }
 - private static StyleContext defaultContext;
 - /**
 - * Creates a new StyleContext object.
 - */
 - public StyleContext() {
 - styles = new NamedStyle(null);
 - addStyle(DEFAULT_STYLE, null);
 - }
 - /**
 - * Adds a new style into the style hierarchy. Style attributes
 - * resolve from bottom up so an attribute specified in a child
 - * will override an attribute specified in the parent.
 - *
 - * @param nm the name of the style (must be unique within the
 - * collection of named styles in the document). The name may
 - * be null if the style is unnamed, but the caller is responsible
 - * for managing the reference returned as an unnamed style can't
 - * be fetched by name. An unnamed style may be useful for things
 - * like character attribute overrides such as found in a style
 - * run.
 - * @param parent the parent style. This may be null if unspecified
 - * attributes need not be resolved in some other style.
 - * @return the created style
 - */
 - public Style addStyle(String nm, Style parent) {
 - Style style = new NamedStyle(nm, parent);
 - if (nm != null) {
 - // add a named style, a class of attributes
 - styles.addAttribute(nm, style);
 - }
 - return style;
 - }
 - /**
 - * Removes a named style previously added to the document.
 - *
 - * @param nm the name of the style to remove
 - */
 - public void removeStyle(String nm) {
 - styles.removeAttribute(nm);
 - }
 - /**
 - * Fetches a named style previously added to the document
 - *
 - * @param nm the name of the style
 - * @return the style
 - */
 - public Style getStyle(String nm) {
 - return (Style) styles.getAttribute(nm);
 - }
 - /**
 - * Fetches the names of the styles defined.
 - *
 - * @return the list of names as an enumeration
 - */
 - public Enumeration getStyleNames() {
 - return styles.getAttributeNames();
 - }
 - /**
 - * Adds a listener to track when styles are added
 - * or removed.
 - *
 - * @param l the change listener
 - */
 - public void addChangeListener(ChangeListener l) {
 - styles.addChangeListener(l);
 - }
 - /**
 - * Removes a listener that was tracking styles being
 - * added or removed.
 - *
 - * @param l the change listener
 - */
 - public void removeChangeListener(ChangeListener l) {
 - styles.removeChangeListener(l);
 - }
 - /**
 - * Gets the font from an attribute set. This is
 - * implemented to try and fetch a cached font
 - * for the given AttributeSet, and if that fails
 - * the font features are resolved and the
 - * font is fetched from the low-level font cache.
 - *
 - * @param attr the attribute set
 - * @return the font
 - */
 - public Font getFont(AttributeSet attr) {
 - // PENDING(prinz) add cache behavior
 - int style = Font.PLAIN;
 - if (StyleConstants.isBold(attr)) {
 - style |= Font.BOLD;
 - }
 - if (StyleConstants.isItalic(attr)) {
 - style |= Font.ITALIC;
 - }
 - String family = StyleConstants.getFontFamily(attr);
 - int size = StyleConstants.getFontSize(attr);
 - /**
 - * if either superscript or subscript is
 - * is set, we need to reduce the font size
 - * by 2.
 - */
 - if (StyleConstants.isSuperscript(attr) ||
 - StyleConstants.isSubscript(attr)) {
 - size -= 2;
 - }
 - return getFont(family, style, size);
 - }
 - /**
 - * Takes a set of attributes and turn it into a foreground color
 - * specification. This might be used to specify things
 - * like brighter, more hue, etc. By default it simply returns
 - * the value specified by the StyleConstants.Foreground attribute.
 - *
 - * @param attr the set of attributes
 - * @return the color
 - */
 - public Color getForeground(AttributeSet attr) {
 - return StyleConstants.getForeground(attr);
 - }
 - /**
 - * Takes a set of attributes and turn it into a background color
 - * specification. This might be used to specify things
 - * like brighter, more hue, etc. By default it simply returns
 - * the value specified by the StyleConstants.Background attribute.
 - *
 - * @param attr the set of attributes
 - * @return the color
 - */
 - public Color getBackground(AttributeSet attr) {
 - return StyleConstants.getBackground(attr);
 - }
 - /**
 - * Gets a new font. This returns a Font from a cache
 - * if a cached font exists. If not, a Font is added to
 - * the cache. This is basically a low-level cache for
 - * 1.1 font features.
 - *
 - * @param family the font family (such as "Monospaced")
 - * @param style the style of the font (such as Font.PLAIN)
 - * @param size the point size >= 1
 - * @return the new font
 - */
 - public Font getFont(String family, int style, int size) {
 - fontSearch.setValue(family, style, size);
 - Font f = (Font) fontTable.get(fontSearch);
 - if (f == null) {
 - // haven't seen this one yet.
 - f = new Font(family, style, size);
 - FontKey key = new FontKey(family, style, size);
 - fontTable.put(key, f);
 - }
 - return f;
 - }
 - /**
 - * Returns font metrics for a font.
 - *
 - * @param f the font
 - * @return the metrics
 - */
 - public FontMetrics getFontMetrics(Font f) {
 - // The Toolkit implementations cache, so we just forward
 - // to the default toolkit.
 - return Toolkit.getDefaultToolkit().getFontMetrics(f);
 - }
 - // --- AttributeContext methods --------------------
 - /**
 - * Adds an attribute to the given set, and returns
 - * the new representative set.
 - * <p>
 - * This method is thread safe, although most Swing methods
 - * are not. Please see
 - * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
 - * and Swing</A> for more information.
 - *
 - * @param old the old attribute set
 - * @param name the non-null attribute name
 - * @param value the attribute value
 - * @return the updated attribute set
 - * @see MutableAttributeSet#addAttribute
 - */
 - public synchronized AttributeSet addAttribute(AttributeSet old, Object name, Object value) {
 - if ((old.getAttributeCount() + 1) <= getCompressionThreshold()) {
 - // build a search key and find/create an immutable and unique
 - // set.
 - search.removeAttributes(search);
 - search.addAttributes(old);
 - search.addAttribute(name, value);
 - reclaim(old);
 - return getImmutableUniqueSet();
 - }
 - MutableAttributeSet ma = getMutableAttributeSet(old);
 - ma.addAttribute(name, value);
 - return ma;
 - }
 - /**
 - * Adds a set of attributes to the element.
 - * <p>
 - * This method is thread safe, although most Swing methods
 - * are not. Please see
 - * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
 - * and Swing</A> for more information.
 - *
 - * @param old the old attribute set
 - * @param attr the attributes to add
 - * @return the updated attribute set
 - * @see MutableAttributeSet#addAttribute
 - */
 - public synchronized AttributeSet addAttributes(AttributeSet old, AttributeSet attr) {
 - if ((old.getAttributeCount() + attr.getAttributeCount()) <= getCompressionThreshold()) {
 - // build a search key and find/create an immutable and unique
 - // set.
 - search.removeAttributes(search);
 - search.addAttributes(old);
 - search.addAttributes(attr);
 - reclaim(old);
 - return getImmutableUniqueSet();
 - }
 - MutableAttributeSet ma = getMutableAttributeSet(old);
 - ma.addAttributes(attr);
 - return ma;
 - }
 - /**
 - * Removes an attribute from the set.
 - * <p>
 - * This method is thread safe, although most Swing methods
 - * are not. Please see
 - * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
 - * and Swing</A> for more information.
 - *
 - * @param old the old set of attributes
 - * @param name the non-null attribute name
 - * @return the updated attribute set
 - * @see MutableAttributeSet#removeAttribute
 - */
 - public synchronized AttributeSet removeAttribute(AttributeSet old, Object name) {
 - if ((old.getAttributeCount() - 1) <= getCompressionThreshold()) {
 - // build a search key and find/create an immutable and unique
 - // set.
 - search.removeAttributes(search);
 - search.addAttributes(old);
 - search.removeAttribute(name);
 - reclaim(old);
 - return getImmutableUniqueSet();
 - }
 - MutableAttributeSet ma = getMutableAttributeSet(old);
 - ma.removeAttribute(name);
 - return ma;
 - }
 - /**
 - * Removes a set of attributes for the element.
 - * <p>
 - * This method is thread safe, although most Swing methods
 - * are not. Please see
 - * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
 - * and Swing</A> for more information.
 - *
 - * @param old the old attribute set
 - * @param names the attribute names
 - * @return the updated attribute set
 - * @see MutableAttributeSet#removeAttributes
 - */
 - public synchronized AttributeSet removeAttributes(AttributeSet old, Enumeration names) {
 - if (old.getAttributeCount() <= getCompressionThreshold()) {
 - // build a search key and find/create an immutable and unique
 - // set.
 - search.removeAttributes(search);
 - search.addAttributes(old);
 - search.removeAttributes(names);
 - reclaim(old);
 - return getImmutableUniqueSet();
 - }
 - MutableAttributeSet ma = getMutableAttributeSet(old);
 - ma.removeAttributes(names);
 - return ma;
 - }
 - /**
 - * Removes a set of attributes for the element.
 - * <p>
 - * This method is thread safe, although most Swing methods
 - * are not. Please see
 - * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
 - * and Swing</A> for more information.
 - *
 - * @param old the old attribute set
 - * @param attrs the attributes
 - * @return the updated attribute set
 - * @see MutableAttributeSet#removeAttributes
 - */
 - public synchronized AttributeSet removeAttributes(AttributeSet old, AttributeSet attrs) {
 - if (old.getAttributeCount() <= getCompressionThreshold()) {
 - // build a search key and find/create an immutable and unique
 - // set.
 - search.removeAttributes(search);
 - search.addAttributes(old);
 - search.removeAttributes(attrs);
 - reclaim(old);
 - return getImmutableUniqueSet();
 - }
 - MutableAttributeSet ma = getMutableAttributeSet(old);
 - ma.removeAttributes(attrs);
 - return ma;
 - }
 - /**
 - * Fetches an empty AttributeSet.
 - *
 - * @return the set
 - */
 - public AttributeSet getEmptySet() {
 - return SimpleAttributeSet.EMPTY;
 - }
 - /**
 - * Returns a set no longer needed by the MutableAttributeSet implmentation.
 - * This is useful for operation under 1.1 where there are no weak
 - * references. This would typically be called by the finalize method
 - * of the MutableAttributeSet implementation.
 - * <p>
 - * This method is thread safe, although most Swing methods
 - * are not. Please see
 - * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
 - * and Swing</A> for more information.
 - *
 - * @param a the set to reclaim
 - */
 - public void reclaim(AttributeSet a) {
 - if (a instanceof SmallAttributeSet) {
 - SmallAttributeSet sa = (SmallAttributeSet) a;
 - sa.nrefs -= 1;
 - if (sa.nrefs <= 0) {
 - unusedSets += 1;
 - if ((unusedSets > 10) && (unusedSets > (attributesPool.size() / 10))) {
 - if (SwingUtilities.isEventDispatchThread()) {
 - removeUnusedSets();
 - } else {
 - Runnable callRemoveUnused = new Runnable() {
 - public void run() {
 - removeUnusedSets();
 - }
 - };
 - SwingUtilities.invokeLater(callRemoveUnused);
 - }
 - }
 - }
 - }
 - }
 - // --- local methods -----------------------------------------------
 - /**
 - * Returns the maximum number of key/value pairs to try and
 - * compress into unique/immutable sets. Any sets above this
 - * limit will use hashtables and be a MutableAttributeSet.
 - *
 - * @return the threshold
 - */
 - protected int getCompressionThreshold() {
 - return THRESHOLD;
 - }
 - /**
 - * Create a compact set of attributes that might be shared.
 - * This is a hook for subclasses that want to alter the
 - * behavior of SmallAttributeSet. This can be reimplemented
 - * to return an AttributeSet that provides some sort of
 - * attribute conversion.
 - *
 - * @param a The set of attributes to be represented in the
 - * the compact form.
 - */
 - protected SmallAttributeSet createSmallAttributeSet(AttributeSet a) {
 - return new SmallAttributeSet(a);
 - }
 - /**
 - * Create a large set of attributes that should trade off
 - * space for time. This set will not be shared. This is
 - * a hook for subclasses that want to alter the behavior
 - * of the larger attribute storage format (which is
 - * SimpleAttributeSet by default). This can be reimplemented
 - * to return a MutableAttributeSet that provides some sort of
 - * attribute conversion.
 - *
 - * @param a The set of attributes to be represented in the
 - * the larger form.
 - */
 - protected MutableAttributeSet createLargeAttributeSet(AttributeSet a) {
 - return new SimpleAttributeSet(a);
 - }
 - /**
 - * Clean the unused immutable sets out of the hashtable.
 - */
 - synchronized void removeUnusedSets() {
 - Vector rmList = new Vector();
 - Enumeration sets = attributesPool.keys();
 - while (sets.hasMoreElements()) {
 - SmallAttributeSet set = (SmallAttributeSet)sets.nextElement();
 - if (set.nrefs <= 0) {
 - rmList.addElement(set);
 - }
 - }
 - sets = rmList.elements();
 - while (sets.hasMoreElements()) {
 - attributesPool.remove(sets.nextElement());
 - }
 - unusedSets = 0;
 - }
 - /**
 - * Search for an existing attribute set using the current search
 - * parameters. If a matching set is found, return it. If a match
 - * is not found, we create a new set and add it to the pool.
 - */
 - AttributeSet getImmutableUniqueSet() {
 - // PENDING(prinz) should consider finding a alternative to
 - // generating extra garbage on search key.
 - SmallAttributeSet key = createSmallAttributeSet(search);
 - SmallAttributeSet a = (SmallAttributeSet) attributesPool.get(key);
 - if (a == null) {
 - a = key;
 - attributesPool.put(a, a);
 - }
 - a.nrefs += 1;
 - return a;
 - }
 - /**
 - * Creates a mutable attribute set to hand out because the current
 - * needs are too big to try and use a shared version.
 - */
 - MutableAttributeSet getMutableAttributeSet(AttributeSet a) {
 - if (a instanceof MutableAttributeSet &&
 - a != SimpleAttributeSet.EMPTY) {
 - return (MutableAttributeSet) a;
 - }
 - return createLargeAttributeSet(a);
 - }
 - /**
 - * Converts a StyleContext to a String.
 - *
 - * @return the string
 - */
 - public String toString() {
 - removeUnusedSets();
 - String s = "";
 - Enumeration sets = attributesPool.keys();
 - while (sets.hasMoreElements()) {
 - SmallAttributeSet set = (SmallAttributeSet)sets.nextElement();
 - s = s + set + "\n";
 - }
 - return s;
 - }
 - // --- serialization ---------------------------------------------
 - /**
 - * Context-specific handling of writing out attributes
 - */
 - public void writeAttributes(ObjectOutputStream out,
 - AttributeSet a) throws IOException {
 - writeAttributeSet(out, a);
 - }
 - /**
 - * Context-specific handling of reading in attributes
 - */
 - public void readAttributes(ObjectInputStream in,
 - MutableAttributeSet a) throws ClassNotFoundException, IOException {
 - readAttributeSet(in, a);
 - }
 - /**
 - * Writes a set of attributes to the given object stream
 - * for the purpose of serialization. This will take
 - * special care to deal with static attribute keys that
 - * have been registered wit the
 - * <code>registerStaticAttributeKey</code> method.
 - * Any attribute key not regsitered as a static key
 - * will be serialized directly. All values are expected
 - * to be serializable.
 - *
 - * @param out the output stream
 - * @param a the attribute set
 - * @exception IOException on any I/O error
 - */
 - public static void writeAttributeSet(ObjectOutputStream out,
 - AttributeSet a) throws IOException {
 - int n = a.getAttributeCount();
 - out.writeInt(n);
 - Enumeration keys = a.getAttributeNames();
 - while (keys.hasMoreElements()) {
 - Object key = keys.nextElement();
 - if (key instanceof Serializable) {
 - out.writeObject(key);
 - } else {
 - Object ioFmt = freezeKeyMap.get(key);
 - if (ioFmt == null) {
 - throw new NotSerializableException(key.getClass().
 - getName() + " is not serializable as a key in an AttributeSet");
 - }
 - out.writeObject(ioFmt);
 - }
 - Object value = a.getAttribute(key);
 - if (value instanceof Serializable) {
 - out.writeObject(value);
 - } else {
 - Object ioFmt = freezeKeyMap.get(value);
 - if (ioFmt == null) {
 - throw new NotSerializableException(value.getClass().
 - getName() + " is not serializable as a value in an AttributeSet");
 - }
 - out.writeObject(ioFmt);
 - }
 - }
 - }
 - /**
 - * Reads a set of attributes from the given object input
 - * stream that have been previously written out with
 - * <code>writeAttributeSet</code>. This will try to restore
 - * keys that were static objects to the static objects in
 - * the current virtual machine considering only those keys
 - * that have been registered with the
 - * <code>registerStaticAttributeKey</code> method.
 - * The attributes retrieved from the stream will be placed
 - * into the given mutable set.
 - *
 - * @param in the object stream to read the attribute data from.
 - * @param a the attribute set to place the attribute
 - * definitions in.
 - * @exception ClassNotFoundException passed upward if encountered
 - * when reading the object stream.
 - * @exception IOException passed upward if encountered when
 - * reading the object stream.
 - */
 - public static void readAttributeSet(ObjectInputStream in,
 - MutableAttributeSet a) throws ClassNotFoundException, IOException {
 - int n = in.readInt();
 - for (int i = 0; i < n; i++) {
 - Object key = in.readObject();
 - Object value = in.readObject();
 - if (thawKeyMap != null) {
 - Object staticKey = thawKeyMap.get(key);
 - if (staticKey != null) {
 - key = staticKey;
 - }
 - Object staticValue = thawKeyMap.get(value);
 - if (staticValue != null) {
 - value = staticValue;
 - }
 - }
 - a.addAttribute(key, value);
 - }
 - }
 - /**
 - * Registers an object as a static object that is being
 - * used as a key in attribute sets. This allows the key
 - * to be treated specially for serialization.
 - * <p>
 - * For operation under a 1.1 virtual machine, this
 - * uses the value returned by <code>toString</code>
 - * concatenated to the classname. The value returned
 - * by toString should not have the class reference
 - * in it (ie it should be reimplemented from the
 - * definition in Object) in order to be the same when
 - * recomputed later.
 - *
 - * @param key the non-null object key
 - */
 - public static void registerStaticAttributeKey(Object key) {
 - String ioFmt = key.getClass().getName() + "." + key.toString();
 - if (freezeKeyMap == null) {
 - freezeKeyMap = new Hashtable();
 - thawKeyMap = new Hashtable();
 - }
 - freezeKeyMap.put(key, ioFmt);
 - thawKeyMap.put(ioFmt, key);
 - }
 - /**
 - * Returns the object previously registered with
 - * <code>registerStaticAttributeKey</code>.
 - */
 - public static Object getStaticAttribute(Object key) {
 - if (thawKeyMap == null || key == null) {
 - return null;
 - }
 - return thawKeyMap.get(key);
 - }
 - /**
 - * Returns the String that <code>key</code> will be registered with
 - * @see #getStaticAttribute
 - * @see #registerStaticAttributeKey
 - */
 - public static Object getStaticAttributeKey(Object key) {
 - return key.getClass().getName() + "." + key.toString();
 - }
 - private void writeObject(java.io.ObjectOutputStream s)
 - throws IOException
 - {
 - // clean out unused sets before saving
 - removeUnusedSets();
 - s.defaultWriteObject();
 - }
 - private void readObject(ObjectInputStream s)
 - throws ClassNotFoundException, IOException
 - {
 - fontSearch = new FontKey(null, 0, 0);
 - fontTable = new Hashtable();
 - search = new SimpleAttributeSet();
 - attributesPool = new Hashtable();
 - s.defaultReadObject();
 - }
 - // --- variables ---------------------------------------------------
 - /**
 - * The name given to the default logical style attached
 - * to paragraphs.
 - */
 - public static final String DEFAULT_STYLE = "default";
 - private static Hashtable freezeKeyMap;
 - private static Hashtable thawKeyMap;
 - private Style styles;
 - private transient FontKey fontSearch = new FontKey(null, 0, 0);
 - private transient Hashtable fontTable = new Hashtable();
 - private transient Hashtable attributesPool = new Hashtable();
 - private transient MutableAttributeSet search = new SimpleAttributeSet();
 - /**
 - * Number of immutable sets that are not currently
 - * being used. This helps indicate when the sets need
 - * to be cleaned out of the hashtable they are stored
 - * in.
 - */
 - private int unusedSets;
 - /**
 - * The threshold for no longer sharing the set of attributes
 - * in an immutable table.
 - */
 - static final int THRESHOLD = 9;
 - /**
 - * This class holds a small number of attributes in an array.
 - * The storage format is key, value, key, value, etc. The size
 - * of the set is the length of the array divided by two. By
 - * default, this is the class that will be used to store attributes
 - * when held in the compact sharable form.
 - */
 - public class SmallAttributeSet implements AttributeSet {
 - public SmallAttributeSet(Object[] attributes) {
 - this.attributes = attributes;
 - updateResolveParent();
 - }
 - public SmallAttributeSet(AttributeSet attrs) {
 - int n = attrs.getAttributeCount();
 - Object[] tbl = new Object[2 * n];
 - Enumeration names = attrs.getAttributeNames();
 - int i = 0;
 - while (names.hasMoreElements()) {
 - tbl[i] = names.nextElement();
 - tbl[i+1] = attrs.getAttribute(tbl[i]);
 - i += 2;
 - }
 - attributes = tbl;
 - updateResolveParent();
 - }
 - private void updateResolveParent() {
 - resolveParent = null;
 - Object[] tbl = attributes;
 - for (int i = 0; i < tbl.length; i += 2) {
 - if (tbl[i] == StyleConstants.ResolveAttribute) {
 - resolveParent = (AttributeSet)tbl[i + 1];
 - break;
 - }
 - }
 - }
 - Object getLocalAttribute(Object nm) {
 - if (nm == StyleConstants.ResolveAttribute) {
 - return resolveParent;
 - }
 - Object[] tbl = attributes;
 - for (int i = 0; i < tbl.length; i += 2) {
 - if (nm.equals(tbl[i])) {
 - return tbl[i+1];
 - }
 - }
 - return null;
 - }
 - // --- Object methods -------------------------
 - /**
 - * Returns a string showing the key/value pairs
 - */
 - public String toString() {
 - String s = "{";
 - Object[] tbl = attributes;
 - for (int i = 0; i < tbl.length; i += 2) {
 - if (tbl[i+1] instanceof AttributeSet) {
 - // don't recurse
 - s = s + tbl[i] + "=" + "AttributeSet" + ",";
 - } else {
 - s = s + tbl[i] + "=" + tbl[i+1] + ",";
 - }
 - }
 - s = s + "nrefs=" + nrefs + "}";
 - return s;
 - }
 - /**
 - * Returns a hashcode for this set of attributes.
 - * @return a hashcode value for this set of attributes.
 - */
 - public int hashCode() {
 - int code = 0;
 - Object[] tbl = attributes;
 - for (int i = 1; i < tbl.length; i += 2) {
 - code ^= tbl[i].hashCode();
 - }
 - return code;
 - }
 - /**
 - * Compares this object to the specifed object.
 - * The result is <code>true</code> if the object is an equivalent
 - * set of attributes.
 - * @param obj the object to compare with.
 - * @return <code>true</code> if the objects are equal;
 - * <code>false</code> otherwise.
 - */
 - public boolean equals(Object obj) {
 - if (obj instanceof AttributeSet) {
 - AttributeSet attrs = (AttributeSet) obj;
 - return ((getAttributeCount() == attrs.getAttributeCount()) &&
 - containsAttributes(attrs));
 - }
 - return false;
 - }
 - /**
 - * Clones a set of attributes. Since the set is immutable, a
 - * clone is basically the same set.
 - *
 - * @return the set of attributes
 - */
 - public Object clone() {
 - return this;
 - }
 - // --- AttributeSet methods ----------------------------
 - /**
 - * Gets the number of attributes that are defined.
 - *
 - * @return the number of attributes
 - * @see AttributeSet#getAttributeCount
 - */
 - public int getAttributeCount() {
 - return attributes.length / 2;
 - }
 - /**
 - * Checks whether a given attribute is defined.
 - *
 - * @param key the attribute key
 - * @return true if the attribute is defined
 - * @see AttributeSet#isDefined
 - */
 - public boolean isDefined(Object key) {
 - Object[] a = attributes;
 - int n = a.length;
 - for (int i = 0; i < n; i += 2) {
 - if (key.equals(a[i])) {
 - return true;
 - }
 - }
 - return false;
 - }
 - /**
 - * Checks whether two attribute sets are equal.
 - *
 - * @param attr the attribute set to check against
 - * @return true if the same
 - * @see AttributeSet#isEqual
 - */
 - public boolean isEqual(AttributeSet attr) {
 - if (attr instanceof SmallAttributeSet) {
 - return attr == this;
 - }
 - return ((getAttributeCount() == attr.getAttributeCount()) &&
 - containsAttributes(attr));
 - }
 - /**
 - * Copies a set of attributes.
 - *
 - * @return the copy
 - * @see AttributeSet#copyAttributes
 - */
 - public AttributeSet copyAttributes() {
 - return this;
 - }
 - /**
 - * Gets the value of an attribute.
 - *
 - * @param key the attribute name
 - * @return the attribute value
 - * @see AttributeSet#getAttribute
 - */
 - public Object getAttribute(Object key) {
 - Object value = getLocalAttribute(key);
 - if (value == null) {
 - AttributeSet parent = getResolveParent();
 - if (parent != null)
 - value = parent.getAttribute(key);
 - }
 - return value;
 - }
 - /**
 - * Gets the names of all attributes.
 - *
 - * @return the attribute names
 - * @see AttributeSet#getAttributeNames
 - */
 - public Enumeration getAttributeNames() {
 - return new KeyEnumeration(attributes);
 - }
 - /**
 - * Checks whether a given attribute name/value is defined.
 - *
 - * @param name the attribute name
 - * @param value the attribute value
 - * @return true if the name/value is defined
 - * @see AttributeSet#containsAttribute
 - */
 - public boolean containsAttribute(Object name, Object value) {
 - return value.equals(getAttribute(name));
 - }
 - /**
 - * Checks whether the attribute set contains all of
 - * the given attributes.
 - *
 - * @param attrs the attributes to check
 - * @return true if the element contains all the attributes
 - * @see AttributeSet#containsAttributes
 - */
 - public boolean containsAttributes(AttributeSet attrs) {
 - boolean result = true;
 - Enumeration names = attrs.getAttributeNames();
 - while (result && names.hasMoreElements()) {
 - Object name = names.nextElement();
 - result = attrs.getAttribute(name).equals(getAttribute(name));
 - }
 - return result;
 - }
 - /**
 - * If not overriden, the resolving parent defaults to
 - * the parent element.
 - *
 - * @return the attributes from the parent
 - * @see AttributeSet#getResolveParent
 - */
 - public AttributeSet getResolveParent() {
 - return resolveParent;
 - }
 - // --- variables -----------------------------------------
 - Object[] attributes;
 - int nrefs;
 - // This is also stored in attributes
 - AttributeSet resolveParent;
 - }
 - /**
 - * An enumeration of the keys in a SmallAttributeSet.
 - */
 - class KeyEnumeration implements Enumeration {
 - KeyEnumeration(Object[] attr) {
 - this.attr = attr;
 - i = 0;
 - }
 - /**
 - * Tests if this enumeration contains more elements.
 - *
 - * @return <code>true</code> if this enumeration contains more elements;
 - * <code>false</code> otherwise.
 - * @since JDK1.0
 - */
 - public boolean hasMoreElements() {
 - return i < attr.length;
 - }
 - /**
 - * Returns the next element of this enumeration.
 - *
 - * @return the next element of this enumeration.
 - * @exception NoSuchElementException if no more elements exist.
 - * @since JDK1.0
 - */
 - public Object nextElement() {
 - if (i < attr.length) {
 - Object o = attr[i];
 - i += 2;
 - return o;
 - }
 - throw new NoSuchElementException();
 - }
 - Object[] attr;
 - int i;
 - }
 - /**
 - * Sorts the key strings so that they can be very quickly compared
 - * in the attribute set searchs.
 - */
 - class KeyBuilder {
 - public void initialize(AttributeSet a) {
 - if (a instanceof SmallAttributeSet) {
 - initialize(((SmallAttributeSet)a).attributes);
 - } else {
 - keys.removeAllElements();
 - data.removeAllElements();
 - Enumeration names = a.getAttributeNames();
 - while (names.hasMoreElements()) {
 - Object name = names.nextElement();
 - addAttribute(name, a.getAttribute(name));
 - }
 - }
 - }
 - /**
 - * Initialize with a set of already sorted
 - * keys (data from an existing SmallAttributeSet).
 - */
 - private void initialize(Object[] sorted) {
 - keys.removeAllElements();
 - data.removeAllElements();
 - int n = sorted.length;
 - for (int i = 0; i < n; i += 2) {
 - keys.addElement(sorted[i]);
 - data.addElement(sorted[i+1]);
 - }
 - }
 - /**
 - * Creates a table of sorted key/value entries
 - * suitable for creation of an instance of
 - * SmallAttributeSet.
 - */
 - public Object[] createTable() {
 - int n = keys.size();
 - Object[] tbl = new Object[2 * n];
 - for (int i = 0; i < n; i ++) {
 - int offs = 2 * i;
 - tbl[offs] = keys.elementAt(i);
 - tbl[offs + 1] = data.elementAt(i);
 - }
 - return tbl;
 - }
 - /**
 - * The number of key/value pairs contained
 - * in the current key being forged.
 - */
 - int getCount() {
 - return keys.size();
 - }
 - /**
 - * Adds a key/value to the set.
 - */
 - public void addAttribute(Object key, Object value) {
 - keys.addElement(key);
 - data.addElement(value);
 - }
 - /**
 - * Adds a set of key/value pairs to the set.
 - */
 - public void addAttributes(AttributeSet attr) {
 - if (attr instanceof SmallAttributeSet) {
 - // avoid searching the keys, they are already interned.
 - Object[] tbl = ((SmallAttributeSet)attr).attributes;
 - int n = tbl.length;
 - for (int i = 0; i < n; i += 2) {
 - addAttribute(tbl[i], tbl[i+1]);
 - }
 - } else {
 - Enumeration names = attr.getAttributeNames();
 - while (names.hasMoreElements()) {
 - Object name = names.nextElement();
 - addAttribute(name, attr.getAttribute(name));
 - }
 - }
 - }
 - /**
 - * Removes the given name from the set.
 - */
 - public void removeAttribute(Object key) {
 - int n = keys.size();
 - for (int i = 0; i < n; i++) {
 - if (keys.elementAt(i).equals(key)) {
 - keys.removeElementAt(i);
 - data.removeElementAt(i);
 - return;
 - }
 - }
 - }
 - /**
 - * Removes the set of keys from the set.
 - */
 - public void removeAttributes(Enumeration names) {
 - while (names.hasMoreElements()) {
 - Object name = names.nextElement();
 - removeAttribute(name);
 - }
 - }
 - /**
 - * Removes the set of matching attributes from the set.
 - */
 - public void removeAttributes(AttributeSet attr) {
 - Enumeration names = attr.getAttributeNames();
 - while (names.hasMoreElements()) {
 - Object name = names.nextElement();
 - Object value = attr.getAttribute(name);
 - removeSearchAttribute(name, value);
 - }
 - }
 - private void removeSearchAttribute(Object ikey, Object value) {
 - int n = keys.size();
 - for (int i = 0; i < n; i++) {
 - if (keys.elementAt(i).equals(ikey)) {
 - if (data.elementAt(i).equals(value)) {
 - keys.removeElementAt(i);
 - data.removeElementAt(i);
 - }
 - return;
 - }
 - }
 - }
 - private Vector keys = new Vector();
 - private Vector data = new Vector();
 - }
 - /**
 - * key for a font table
 - */
 - static class FontKey {
 - private String family;
 - private int style;
 - private int size;
 - /**
 - * Constructs a font key.
 - */
 - public FontKey(String family, int style, int size) {
 - setValue(family, style, size);
 - }
 - public void setValue(String family, int style, int size) {
 - this.family = (family != null) ? family.intern() : null;
 - this.style = style;
 - this.size = size;
 - }
 - /**
 - * Returns a hashcode for this font.
 - * @return a hashcode value for this font.
 - */
 - public int hashCode() {
 - return family.hashCode() ^ style ^ size;
 - }
 - /**
 - * Compares this object to the specifed object.
 - * The result is <code>true</code> if and only if the argument is not
 - * <code>null</code> and is a <code>Font</code> object with the same
 - * name, style, and point size as this font.
 - * @param obj the object to compare this font with.
 - * @return <code>true</code> if the objects are equal;
 - * <code>false</code> otherwise.
 - */
 - public boolean equals(Object obj) {
 - if (obj instanceof FontKey) {
 - FontKey font = (FontKey)obj;
 - return (size == font.size) && (style == font.style) && (family == font.family);
 - }
 - return false;
 - }
 - }
 - /**
 - * A collection of attributes, typically used to represent
 - * character and paragraph styles. This is an implementation
 - * of MutableAttributeSet that can be observed if desired.
 - * These styles will take advantage of immutability while
 - * the sets are small enough, and may be substantially more
 - * efficient than something like SimpleAttributeSet.
 - * <p>
 - * <strong>Warning:</strong>
 - * Serialized objects of this class will not be compatible with
 - * future Swing releases. The current serialization support is appropriate
 - * for short term storage or RMI between applications running the same
 - * version of Swing. A future release of Swing will provide support for
 - * long term persistence.
 - */
 - public class NamedStyle implements Style, Serializable {
 - /**
 - * Creates a new named style.
 - *
 - * @param name the style name, null for unnamed
 - * @param parent the parent style, null if none
 - */
 - public NamedStyle(String name, Style parent) {
 - attributes = getEmptySet();
 - if (name != null) {
 - setName(name);
 - }
 - if (parent != null) {
 - setResolveParent(parent);
 - }
 - }
 - /**
 - * Creates a new named style.
 - *
 - * @param parent the parent style, null if none
 - */
 - public NamedStyle(Style parent) {
 - this(null, parent);
 - }
 - /**
 - * Creates a new named style, with a null name and parent.
 - */
 - public NamedStyle() {
 - attributes = getEmptySet();
 - }
 - /**
 - * Converts the style to a string.
 - *
 - * @return the string
 - */
 - public String toString() {
 - return "NamedStyle:" + getName() + " " + attributes;
 - }
 - /**
 - * Fetches the name of the style. A style is not required to be named,
 - * so null is returned if there is no name associated with the style.
 - *
 - * @return the name
 - */
 - public String getName() {
 - if (isDefined(StyleConstants.NameAttribute)) {
 - return (String) getAttribute(StyleConstants.NameAttribute);
 - }
 - return null;
 - }
 - /**
 - * Changes the name of the style. Does nothing with a null name.
 - *
 - * @param name the new name
 - */
 - public void setName(String name) {
 - if (name != null) {
 - this.addAttribute(StyleConstants.NameAttribute, name);
 - }
 - }
 - /**
 - * Adds a change listener.
 - *
 - * @param l the change listener
 - */
 - public void addChangeListener(ChangeListener l) {
 - listenerList.add(ChangeListener.class, l);
 - }
 - /**
 - * Removes a change listener.
 - *
 - * @param l the change listener
 - */
 - public void removeChangeListener(ChangeListener l) {
 - listenerList.remove(ChangeListener.class, l);
 - }
 - /**
 - * Notifies all listeners that have registered interest for
 - * notification on this event type. The event instance
 - * is lazily created using the parameters passed into
 - * the fire method.
 - *
 - * @see EventListenerList
 - */
 - protected void fireStateChanged() {
 - // Guaranteed to return a non-null array
 - Object[] listeners = listenerList.getListenerList();
 - // Process the listeners last to first, notifying
 - // those that are interested in this event
 - for (int i = listeners.length-2; i>=0; i-=2) {
 - if (listeners[i]==ChangeListener.class) {
 - // Lazily create the event:
 - if (changeEvent == null)
 - changeEvent = new ChangeEvent(this);
 - ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
 - }
 - }
 - }
 - /**
 - * Return an array of all the listeners of the given type that
 - * were added to this model.
 - *
 - * @returns all of the objects recieving <em>listenerType</em> notifications
 - * from this model
 - *
 - * @since 1.3
 - */
 - public EventListener[] getListeners(Class listenerType) {
 - return listenerList.getListeners(listenerType);
 - }
 - // --- AttributeSet ----------------------------
 - // delegated to the immutable field "attributes"
 - /**
 - * Gets the number of attributes that are defined.
 - *
 - * @return the number of attributes >= 0
 - * @see AttributeSet#getAttributeCount
 - */
 - public int getAttributeCount() {
 - return attributes.getAttributeCount();
 - }
 - /**
 - * Checks whether a given attribute is defined.
 - *
 - * @param attrName the non-null attribute name
 - * @return true if the attribute is defined
 - * @see AttributeSet#isDefined
 - */
 - public boolean isDefined(Object attrName) {
 - return attributes.isDefined(attrName);
 - }
 - /**
 - * Checks whether two attribute sets are equal.
 - *
 - * @param attr the attribute set to check against
 - * @return true if the same
 - * @see AttributeSet#isEqual
 - */
 - public boolean isEqual(AttributeSet attr) {
 - return attributes.isEqual(attr);
 - }
 - /**
 - * Copies a set of attributes.
 - *
 - * @return the copy
 - * @see AttributeSet#copyAttributes
 - */
 - public AttributeSet copyAttributes() {
 - NamedStyle a = new NamedStyle();
 - a.attributes = attributes.copyAttributes();
 - return a;
 - }
 - /**
 - * Gets the value of an attribute.
 - *
 - * @param attrName the non-null attribute name
 - * @return the attribute value
 - * @see AttributeSet#getAttribute
 - */
 - public Object getAttribute(Object attrName) {
 - return attributes.getAttribute(attrName);
 - }
 - /**
 - * Gets the names of all attributes.
 - *
 - * @return the attribute names as an enumeration
 - * @see AttributeSet#getAttributeNames
 - */
 - public Enumeration getAttributeNames() {
 - return attributes.getAttributeNames();
 - }
 - /**
 - * Checks whether a given attribute name/value is defined.
 - *
 - * @param name the non-null attribute name
 - * @param value the attribute value
 - * @return true if the name/value is defined
 - * @see AttributeSet#containsAttribute
 - */
 - public boolean containsAttribute(Object name, Object value) {
 - return attributes.containsAttribute(name, value);
 - }
 - /**
 - * Checks whether the element contains all the attributes.
 - *
 - * @param attrs the attributes to check
 - * @return true if the element contains all the attributes
 - * @see AttributeSet#containsAttributes
 - */
 - public boolean containsAttributes(AttributeSet attrs) {
 - return attributes.containsAttributes(attrs);
 - }
 - /**
 - * Gets attributes from the parent.
 - * If not overriden, the resolving parent defaults to
 - * the parent element.
 - *
 - * @return the attributes from the parent
 - * @see AttributeSet#getResolveParent
 - */
 - public AttributeSet getResolveParent() {
 - return attributes.getResolveParent();
 - }
 - // --- MutableAttributeSet ----------------------------------
 - // should fetch a new immutable record for the field
 - // "attributes".
 - /**
 - * Adds an attribute.
 - *
 - * @param name the non-null attribute name
 - * @param value the attribute value
 - * @see MutableAttributeSet#addAttribute
 - */
 - public void addAttribute(Object name, Object value) {
 - StyleContext context = StyleContext.this;
 - attributes = context.addAttribute(attributes, name, value);
 - fireStateChanged();
 - }
 - /**
 - * Adds a set of attributes to the element.
 - *
 - * @param attr the attributes to add
 - * @see MutableAttributeSet#addAttribute
 - */
 - public void addAttributes(AttributeSet attr) {
 - StyleContext context = StyleContext.this;
 - attributes = context.addAttributes(attributes, attr);
 - fireStateChanged();
 - }
 - /**
 - * Removes an attribute from the set.
 - *
 - * @param name the non-null attribute name
 - * @see MutableAttributeSet#removeAttribute
 - */
 - public void removeAttribute(Object name) {
 - StyleContext context = StyleContext.this;
 - attributes = context.removeAttribute(attributes, name);
 - fireStateChanged();
 - }
 - /**
 - * Removes a set of attributes for the element.
 - *
 - * @param names the attribute names
 - * @see MutableAttributeSet#removeAttributes
 - */
 - public void removeAttributes(Enumeration names) {
 - StyleContext context = StyleContext.this;
 - attributes = context.removeAttributes(attributes, names);
 - fireStateChanged();
 - }
 - /**
 - * Removes a set of attributes for the element.
 - *
 - * @param attrs the attributes
 - * @see MutableAttributeSet#removeAttributes
 - */
 - public void removeAttributes(AttributeSet attrs) {
 - StyleContext context = StyleContext.this;
 - if (attrs == this) {
 - attributes = context.getEmptySet();
 - } else {
 - attributes = context.removeAttributes(attributes, attrs);
 - }
 - fireStateChanged();
 - }
 - /**
 - * Sets the resolving parent.
 - *
 - * @param parent the parent, null if none
 - * @see MutableAttributeSet#setResolveParent
 - */
 - public void setResolveParent(AttributeSet parent) {
 - if (parent != null) {
 - addAttribute(StyleConstants.ResolveAttribute, parent);
 - } else {
 - removeAttribute(StyleConstants.ResolveAttribute);
 - }
 - }
 - // --- serialization ---------------------------------------------
 - private void writeObject(ObjectOutputStream s) throws IOException {
 - s.defaultWriteObject();
 - writeAttributeSet(s, attributes);
 - }
 - private void readObject(ObjectInputStream s)
 - throws ClassNotFoundException, IOException
 - {
 - s.defaultReadObject();
 - attributes = SimpleAttributeSet.EMPTY;
 - readAttributeSet(s, this);
 - }
 - // --- member variables -----------------------------------------------
 - /**
 - * The change listeners for the model.
 - */
 - protected EventListenerList listenerList = new EventListenerList();
 - /**
 - * Only one ChangeEvent is needed per model instance since the
 - * event's only (read-only) state is the source property. The source
 - * of events generated here is always "this".
 - */
 - protected transient ChangeEvent changeEvent = null;
 - /**
 - * Inner AttributeSet implementation, which may be an
 - * immutable unique set being shared.
 - */
 - private transient AttributeSet attributes;
 - }
 - static {
 - // initialize the static key registry with the StyleConstants keys
 - try {
 - int n = StyleConstants.keys.length;
 - for (int i = 0; i < n; i++) {
 - StyleContext.registerStaticAttributeKey(StyleConstants.keys[i]);
 - }
 - } catch (Throwable e) {
 - e.printStackTrace();
 - }
 - }
 - }