- /*
- * @(#)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();
- }
- }
-
-
- }