summaryrefslogtreecommitdiff
path: root/libjava/classpath/javax/swing/text/StyleContext.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/javax/swing/text/StyleContext.java')
-rw-r--r--libjava/classpath/javax/swing/text/StyleContext.java990
1 files changed, 990 insertions, 0 deletions
diff --git a/libjava/classpath/javax/swing/text/StyleContext.java b/libjava/classpath/javax/swing/text/StyleContext.java
new file mode 100644
index 000000000..295398835
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/StyleContext.java
@@ -0,0 +1,990 @@
+/* StyleContext.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Toolkit;
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.ref.WeakReference;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.EventListener;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.EventListenerList;
+
+public class StyleContext
+ implements Serializable, AbstractDocument.AttributeContext
+{
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = 8042858831190784241L;
+
+ public class NamedStyle
+ implements Serializable, Style
+ {
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = -6690628971806226374L;
+
+ protected transient ChangeEvent changeEvent;
+ protected EventListenerList listenerList;
+
+ private transient AttributeSet attributes;
+
+ public NamedStyle()
+ {
+ this(null, null);
+ }
+
+ public NamedStyle(Style parent)
+ {
+ this(null, parent);
+ }
+
+ public NamedStyle(String name, Style parent)
+ {
+ attributes = getEmptySet();
+ listenerList = new EventListenerList();
+ if (name != null)
+ setName(name);
+ if (parent != null)
+ setResolveParent(parent);
+ }
+
+ public String getName()
+ {
+ String name = null;
+ if (isDefined(StyleConstants.NameAttribute))
+ name = getAttribute(StyleConstants.NameAttribute).toString();
+ return name;
+ }
+
+ public void setName(String n)
+ {
+ if (n != null)
+ addAttribute(StyleConstants.NameAttribute, n);
+ }
+
+ public void addChangeListener(ChangeListener l)
+ {
+ listenerList.add(ChangeListener.class, l);
+ }
+
+ public void removeChangeListener(ChangeListener l)
+ {
+ listenerList.remove(ChangeListener.class, l);
+ }
+
+ public <T extends EventListener> T[] getListeners(Class<T> listenerType)
+ {
+ return listenerList.getListeners(listenerType);
+ }
+
+ public ChangeListener[] getChangeListeners()
+ {
+ return (ChangeListener[]) getListeners(ChangeListener.class);
+ }
+
+ protected void fireStateChanged()
+ {
+ ChangeListener[] listeners = getChangeListeners();
+ for (int i = 0; i < listeners.length; ++i)
+ {
+ // Lazily create event.
+ if (changeEvent == null)
+ changeEvent = new ChangeEvent(this);
+ listeners[i].stateChanged(changeEvent);
+ }
+ }
+
+ public void addAttribute(Object name, Object value)
+ {
+ attributes = StyleContext.this.addAttribute(attributes, name, value);
+ fireStateChanged();
+ }
+
+ public void addAttributes(AttributeSet attr)
+ {
+ attributes = StyleContext.this.addAttributes(attributes, attr);
+ fireStateChanged();
+ }
+
+ public boolean containsAttribute(Object name, Object value)
+ {
+ return attributes.containsAttribute(name, value);
+ }
+
+ public boolean containsAttributes(AttributeSet attrs)
+ {
+ return attributes.containsAttributes(attrs);
+ }
+
+ public AttributeSet copyAttributes()
+ {
+ // The RI returns a NamedStyle as copy, so do we.
+ NamedStyle copy = new NamedStyle();
+ copy.attributes = attributes.copyAttributes();
+ return copy;
+ }
+
+ public Object getAttribute(Object attrName)
+ {
+ return attributes.getAttribute(attrName);
+ }
+
+ public int getAttributeCount()
+ {
+ return attributes.getAttributeCount();
+ }
+
+ public Enumeration<?> getAttributeNames()
+ {
+ return attributes.getAttributeNames();
+ }
+
+ public boolean isDefined(Object attrName)
+ {
+ return attributes.isDefined(attrName);
+ }
+
+ public boolean isEqual(AttributeSet attr)
+ {
+ return attributes.isEqual(attr);
+ }
+
+ public void removeAttribute(Object name)
+ {
+ attributes = StyleContext.this.removeAttribute(attributes, name);
+ fireStateChanged();
+ }
+
+ public void removeAttributes(AttributeSet attrs)
+ {
+ attributes = StyleContext.this.removeAttributes(attributes, attrs);
+ fireStateChanged();
+ }
+
+ public void removeAttributes(Enumeration<?> names)
+ {
+ attributes = StyleContext.this.removeAttributes(attributes, names);
+ fireStateChanged();
+ }
+
+
+ public AttributeSet getResolveParent()
+ {
+ return attributes.getResolveParent();
+ }
+
+ public void setResolveParent(AttributeSet parent)
+ {
+ if (parent != null)
+ addAttribute(StyleConstants.ResolveAttribute, parent);
+ else
+ removeAttribute(StyleConstants.ResolveAttribute);
+ }
+
+ public String toString()
+ {
+ return "NamedStyle:" + getName() + " " + attributes;
+ }
+
+ 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);
+ }
+ }
+
+ public class SmallAttributeSet
+ implements AttributeSet
+ {
+ final Object [] attrs;
+ private AttributeSet resolveParent;
+ public SmallAttributeSet(AttributeSet a)
+ {
+ int n = a.getAttributeCount();
+ int i = 0;
+ attrs = new Object[n * 2];
+ Enumeration e = a.getAttributeNames();
+ while (e.hasMoreElements())
+ {
+ Object name = e.nextElement();
+ Object value = a.getAttribute(name);
+ if (name == ResolveAttribute)
+ resolveParent = (AttributeSet) value;
+ attrs[i++] = name;
+ attrs[i++] = value;
+ }
+ }
+
+ public SmallAttributeSet(Object [] a)
+ {
+ attrs = a;
+ for (int i = 0; i < attrs.length; i += 2)
+ {
+ if (attrs[i] == ResolveAttribute)
+ resolveParent = (AttributeSet) attrs[i + 1];
+ }
+ }
+
+ public Object clone()
+ {
+ return this;
+ }
+
+ public boolean containsAttribute(Object name, Object value)
+ {
+ return value.equals(getAttribute(name));
+ }
+
+ public boolean containsAttributes(AttributeSet a)
+ {
+ boolean res = true;
+ Enumeration e = a.getAttributeNames();
+ while (e.hasMoreElements() && res)
+ {
+ Object name = e.nextElement();
+ res = a.getAttribute(name).equals(getAttribute(name));
+ }
+ return res;
+ }
+
+ public AttributeSet copyAttributes()
+ {
+ return this;
+ }
+
+ public boolean equals(Object obj)
+ {
+ boolean eq = false;
+ if (obj instanceof AttributeSet)
+ {
+ AttributeSet atts = (AttributeSet) obj;
+ eq = getAttributeCount() == atts.getAttributeCount()
+ && containsAttributes(atts);
+ }
+ return eq;
+ }
+
+ public Object getAttribute(Object key)
+ {
+ Object att = null;
+ if (key == StyleConstants.ResolveAttribute)
+ att = resolveParent;
+
+ for (int i = 0; i < attrs.length && att == null; i += 2)
+ {
+ if (attrs[i].equals(key))
+ att = attrs[i + 1];
+ }
+
+ // Check the resolve parent, unless we're looking for the
+ // ResolveAttribute, which must not be looked up
+ if (att == null)
+ {
+ AttributeSet parent = getResolveParent();
+ if (parent != null)
+ att = parent.getAttribute(key);
+ }
+
+ return att;
+ }
+
+ public int getAttributeCount()
+ {
+ return attrs.length / 2;
+ }
+
+ public Enumeration<?> getAttributeNames()
+ {
+ return new Enumeration()
+ {
+ int i = 0;
+ public boolean hasMoreElements()
+ {
+ return i < attrs.length;
+ }
+ public Object nextElement()
+ {
+ i += 2;
+ return attrs[i-2];
+ }
+ };
+ }
+
+ public AttributeSet getResolveParent()
+ {
+ return resolveParent;
+ }
+
+ public int hashCode()
+ {
+ return java.util.Arrays.asList(attrs).hashCode();
+ }
+
+ public boolean isDefined(Object key)
+ {
+ for (int i = 0; i < attrs.length; i += 2)
+ {
+ if (attrs[i].equals(key))
+ return true;
+ }
+ return false;
+ }
+
+ public boolean isEqual(AttributeSet attr)
+ {
+ boolean eq;
+ // If the other one is also a SmallAttributeSet, it is only considered
+ // equal if it's the same instance.
+ if (attr instanceof SmallAttributeSet)
+ eq = attr == this;
+ else
+ eq = getAttributeCount() == attr.getAttributeCount()
+ && this.containsAttributes(attr);
+ return eq;
+ }
+
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append('{');
+ for (int i = 0; i < attrs.length; i += 2)
+ {
+ if (attrs[i + 1] instanceof AttributeSet)
+ {
+ sb.append(attrs[i]);
+ sb.append("=AttributeSet,");
+ }
+ else
+ {
+ sb.append(attrs[i]);
+ sb.append('=');
+ sb.append(attrs[i + 1]);
+ sb.append(',');
+ }
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+ }
+
+ /**
+ * Register StyleConstant keys as static attribute keys for serialization.
+ */
+ static
+ {
+ // Don't let problems while doing this prevent class loading.
+ try
+ {
+ for (Iterator i = StyleConstants.keys.iterator(); i.hasNext();)
+ registerStaticAttributeKey(i.next());
+ }
+ catch (Throwable t)
+ {
+ t.printStackTrace();
+ }
+ }
+
+ /**
+ * The name of the default style.
+ */
+ public static final String DEFAULT_STYLE = "default";
+
+ static Hashtable sharedAttributeSets = new Hashtable();
+ static Hashtable sharedFonts = new Hashtable();
+
+ static StyleContext defaultStyleContext;
+ static final int compressionThreshold = 9;
+
+ /**
+ * These attribute keys are handled specially in serialization.
+ */
+ private static Hashtable writeAttributeKeys;
+ private static Hashtable readAttributeKeys;
+
+ private NamedStyle styles;
+
+ /**
+ * Used for searching attributes in the pool.
+ */
+ private transient MutableAttributeSet search = new SimpleAttributeSet();
+
+ /**
+ * A pool of immutable AttributeSets.
+ */
+ private transient Map attributeSetPool =
+ Collections.synchronizedMap(new WeakHashMap());
+
+ /**
+ * Creates a new instance of the style context. Add the default style
+ * to the style table.
+ */
+ public StyleContext()
+ {
+ styles = new NamedStyle(null);
+ addStyle(DEFAULT_STYLE, null);
+ }
+
+ protected SmallAttributeSet createSmallAttributeSet(AttributeSet a)
+ {
+ return new SmallAttributeSet(a);
+ }
+
+ protected MutableAttributeSet createLargeAttributeSet(AttributeSet a)
+ {
+ return new SimpleAttributeSet(a);
+ }
+
+ public void addChangeListener(ChangeListener listener)
+ {
+ styles.addChangeListener(listener);
+ }
+
+ public void removeChangeListener(ChangeListener listener)
+ {
+ styles.removeChangeListener(listener);
+ }
+
+ public ChangeListener[] getChangeListeners()
+ {
+ return styles.getChangeListeners();
+ }
+
+ public Style addStyle(String name, Style parent)
+ {
+ Style newStyle = new NamedStyle(name, parent);
+ if (name != null)
+ styles.addAttribute(name, newStyle);
+ return newStyle;
+ }
+
+ public void removeStyle(String name)
+ {
+ styles.removeAttribute(name);
+ }
+
+ /**
+ * Get the style from the style table. If the passed name
+ * matches {@link #DEFAULT_STYLE}, returns the default style.
+ * Otherwise returns the previously defined style of
+ * <code>null</code> if the style with the given name is not defined.
+ *
+ * @param name the name of the style.
+ *
+ * @return the style with the given name or null if no such defined.
+ */
+ public Style getStyle(String name)
+ {
+ return (Style) styles.getAttribute(name);
+ }
+
+ /**
+ * Get the names of the style. The returned enumeration always
+ * contains at least one member, the default style.
+ */
+ public Enumeration<?> getStyleNames()
+ {
+ return styles.getAttributeNames();
+ }
+
+ private void readObject(ObjectInputStream in)
+ throws ClassNotFoundException, IOException
+ {
+ search = new SimpleAttributeSet();
+ attributeSetPool = Collections.synchronizedMap(new WeakHashMap());
+ in.defaultReadObject();
+ }
+
+ private void writeObject(ObjectOutputStream out)
+ throws IOException
+ {
+ cleanupPool();
+ out.defaultWriteObject();
+ }
+
+ //
+ // StyleContexts only understand the "simple" model of fonts present in
+ // pre-java2d systems: fonts are a family name, a size (integral number
+ // of points), and a mask of style parameters (plain, bold, italic, or
+ // bold|italic). We have an inner class here called SimpleFontSpec which
+ // holds such triples.
+ //
+ // A SimpleFontSpec can be built for *any* AttributeSet because the size,
+ // family, and style keys in an AttributeSet have default values (defined
+ // over in StyleConstants).
+ //
+ // We keep a static cache mapping SimpleFontSpecs to java.awt.Fonts, so
+ // that we reuse Fonts between styles and style contexts.
+ //
+
+ private static class SimpleFontSpec
+ {
+ String family;
+ int style;
+ int size;
+ public SimpleFontSpec(String family,
+ int style,
+ int size)
+ {
+ this.family = family;
+ this.style = style;
+ this.size = size;
+ }
+ public boolean equals(Object obj)
+ {
+ return (obj != null)
+ && (obj instanceof SimpleFontSpec)
+ && (((SimpleFontSpec)obj).family.equals(this.family))
+ && (((SimpleFontSpec)obj).style == this.style)
+ && (((SimpleFontSpec)obj).size == this.size);
+ }
+ public int hashCode()
+ {
+ return family.hashCode() + style + size;
+ }
+ }
+
+ public Font getFont(AttributeSet attr)
+ {
+ String family = StyleConstants.getFontFamily(attr);
+ int style = Font.PLAIN;
+ if (StyleConstants.isBold(attr))
+ style += Font.BOLD;
+ if (StyleConstants.isItalic(attr))
+ style += Font.ITALIC;
+ int size = StyleConstants.getFontSize(attr);
+ return getFont(family, style, size);
+ }
+
+ public Font getFont(String family, int style, int size)
+ {
+ SimpleFontSpec spec = new SimpleFontSpec(family, style, size);
+ if (sharedFonts.containsKey(spec))
+ return (Font) sharedFonts.get(spec);
+ else
+ {
+ Font tmp = new Font(family, style, size);
+ sharedFonts.put(spec, tmp);
+ return tmp;
+ }
+ }
+
+ public FontMetrics getFontMetrics(Font f)
+ {
+ return Toolkit.getDefaultToolkit().getFontMetrics(f);
+ }
+
+ public Color getForeground(AttributeSet a)
+ {
+ return StyleConstants.getForeground(a);
+ }
+
+ public Color getBackground(AttributeSet a)
+ {
+ return StyleConstants.getBackground(a);
+ }
+
+ protected int getCompressionThreshold()
+ {
+ return compressionThreshold;
+ }
+
+ public static StyleContext getDefaultStyleContext()
+ {
+ if (defaultStyleContext == null)
+ defaultStyleContext = new StyleContext();
+ return defaultStyleContext;
+ }
+
+ public synchronized AttributeSet addAttribute(AttributeSet old, Object name,
+ Object value)
+ {
+ AttributeSet ret;
+ if (old.getAttributeCount() + 1 < getCompressionThreshold())
+ {
+ search.removeAttributes(search);
+ search.addAttributes(old);
+ search.addAttribute(name, value);
+ reclaim(old);
+ ret = searchImmutableSet();
+ }
+ else
+ {
+ MutableAttributeSet mas = getMutableAttributeSet(old);
+ mas.addAttribute(name, value);
+ ret = mas;
+ }
+ return ret;
+ }
+
+ public synchronized AttributeSet addAttributes(AttributeSet old,
+ AttributeSet attributes)
+ {
+ AttributeSet ret;
+ if (old.getAttributeCount() + attributes.getAttributeCount()
+ < getCompressionThreshold())
+ {
+ search.removeAttributes(search);
+ search.addAttributes(old);
+ search.addAttributes(attributes);
+ reclaim(old);
+ ret = searchImmutableSet();
+ }
+ else
+ {
+ MutableAttributeSet mas = getMutableAttributeSet(old);
+ mas.addAttributes(attributes);
+ ret = mas;
+ }
+ return ret;
+ }
+
+ public AttributeSet getEmptySet()
+ {
+ return SimpleAttributeSet.EMPTY;
+ }
+
+ public void reclaim(AttributeSet attributes)
+ {
+ cleanupPool();
+ }
+
+ public synchronized AttributeSet removeAttribute(AttributeSet old,
+ Object name)
+ {
+ AttributeSet ret;
+ if (old.getAttributeCount() - 1 <= getCompressionThreshold())
+ {
+ search.removeAttributes(search);
+ search.addAttributes(old);
+ search.removeAttribute(name);
+ reclaim(old);
+ ret = searchImmutableSet();
+ }
+ else
+ {
+ MutableAttributeSet mas = getMutableAttributeSet(old);
+ mas.removeAttribute(name);
+ ret = mas;
+ }
+ return ret;
+ }
+
+ public synchronized AttributeSet removeAttributes(AttributeSet old,
+ AttributeSet attributes)
+ {
+ AttributeSet ret;
+ if (old.getAttributeCount() <= getCompressionThreshold())
+ {
+ search.removeAttributes(search);
+ search.addAttributes(old);
+ search.removeAttributes(attributes);
+ reclaim(old);
+ ret = searchImmutableSet();
+ }
+ else
+ {
+ MutableAttributeSet mas = getMutableAttributeSet(old);
+ mas.removeAttributes(attributes);
+ ret = mas;
+ }
+ return ret;
+ }
+
+ public synchronized AttributeSet removeAttributes(AttributeSet old,
+ Enumeration<?> names)
+ {
+ AttributeSet ret;
+ if (old.getAttributeCount() <= getCompressionThreshold())
+ {
+ search.removeAttributes(search);
+ search.addAttributes(old);
+ search.removeAttributes(names);
+ reclaim(old);
+ ret = searchImmutableSet();
+ }
+ else
+ {
+ MutableAttributeSet mas = getMutableAttributeSet(old);
+ mas.removeAttributes(names);
+ ret = mas;
+ }
+ return ret;
+ }
+
+ /**
+ * Gets the object previously registered with registerStaticAttributeKey.
+ *
+ * @param key - the key that was registered.
+ * @return the object previously registered with registerStaticAttributeKey.
+ */
+ public static Object getStaticAttribute(Object key)
+ {
+ if (key == null)
+ return null;
+ return readAttributeKeys.get(key);
+ }
+
+ /**
+ * Returns the String that key will be registered with
+ * registerStaticAttributeKey.
+ *
+ * @param key - the key that will be registered.
+ * @return the string the key will be registered with.
+ */
+ public static Object getStaticAttributeKey(Object key)
+ {
+ return key.getClass().getName() + "." + key.toString();
+ }
+
+ /**
+ * Reads a set of attributes from the given object input stream. This will
+ * attempt to restore keys that were static objects by considering only the
+ * keys that have were registered with registerStaticAttributeKey. The
+ * attributes retrieved will be placed into the given set.
+ *
+ * @param in - the stream to read from
+ * @param a - the set of attributes
+ * @throws ClassNotFoundException - may be encountered when reading from
+ * stream
+ * @throws IOException - any I/O error
+ */
+ public static void readAttributeSet(ObjectInputStream in,
+ MutableAttributeSet a)
+ throws ClassNotFoundException, IOException
+ {
+ int count = in.readInt();
+ for (int i = 0; i < count; i++)
+ {
+ Object key = in.readObject();
+ Object val = in.readObject();
+ if (readAttributeKeys != null)
+ {
+ Object staticKey = readAttributeKeys.get(key);
+ if (staticKey != null)
+ key = staticKey;
+ Object staticVal = readAttributeKeys.get(val);
+ if (staticVal != null)
+ val = staticVal;
+ }
+ a.addAttribute(key, val);
+ }
+ }
+
+ /**
+ * Serialize an attribute set in a way that is compatible with it
+ * being read in again by {@link #readAttributeSet(ObjectInputStream, MutableAttributeSet)}.
+ * In particular registered static keys are transformed properly.
+ *
+ * @param out - stream to write to
+ * @param a - the attribute set
+ * @throws IOException - any I/O error
+ */
+ public static void writeAttributeSet(ObjectOutputStream out, AttributeSet a)
+ throws IOException
+ {
+ int count = a.getAttributeCount();
+ out.writeInt(count);
+ Enumeration e = a.getAttributeNames();
+ while (e.hasMoreElements())
+ {
+ Object key = e.nextElement();
+ // Write key.
+ if (key instanceof Serializable)
+ out.writeObject(key);
+ else
+ {
+ Object io = writeAttributeKeys.get(key);
+ if (io == null)
+ throw new NotSerializableException(key.getClass().getName()
+ + ", key: " + key);
+ out.writeObject(io);
+ }
+ // Write value.
+ Object val = a.getAttribute(key);
+ Object io = writeAttributeKeys.get(val);
+ if (val instanceof Serializable)
+ out.writeObject(io != null ? io : val);
+ else
+ {
+ if (io == null)
+ throw new NotSerializableException(val.getClass().getName());
+ out.writeObject(io);
+ }
+ }
+ }
+
+ /**
+ * Handles reading in the attributes.
+ * @see #readAttributeSet(ObjectInputStream, MutableAttributeSet)
+ *
+ * @param in - the stream to read from
+ * @param a - the set of attributes
+ * @throws ClassNotFoundException - may be encountered when reading from stream
+ * @throws IOException - any I/O error
+ */
+ public void readAttributes(ObjectInputStream in, MutableAttributeSet a)
+ throws ClassNotFoundException, IOException
+ {
+ readAttributeSet(in, a);
+ }
+
+ /**
+ * Handles writing of the given attributes.
+ * @see #writeAttributeSet(ObjectOutputStream, AttributeSet)
+ *
+ * @param out - stream to write to
+ * @param a - the attribute set
+ * @throws IOException - any I/O error
+ */
+ public void writeAttributes(ObjectOutputStream out, AttributeSet a)
+ throws IOException
+ {
+ writeAttributeSet(out, a);
+ }
+
+ /**
+ * Registers an attribute key as a well-known keys. When an attribute with
+ * such a key is written to a stream, a special syntax is used so that it
+ * can be recognized when it is read back in. All attribute keys defined
+ * in <code>StyleContext</code> are registered as static keys. If you define
+ * additional attribute keys that you want to exist as nonreplicated objects,
+ * then you should register them using this method.
+ *
+ * @param key the key to register as static attribute key
+ */
+ public static void registerStaticAttributeKey(Object key)
+ {
+ String io = key.getClass().getName() + "." + key.toString();
+ if (writeAttributeKeys == null)
+ writeAttributeKeys = new Hashtable();
+ if (readAttributeKeys == null)
+ readAttributeKeys = new Hashtable();
+ writeAttributeKeys.put(key, io);
+ readAttributeKeys.put(io, key);
+ }
+
+ /**
+ * Returns a string representation of this StyleContext.
+ *
+ * @return a string representation of this StyleContext
+ */
+ public String toString()
+ {
+ cleanupPool();
+ StringBuilder b = new StringBuilder();
+ Iterator i = attributeSetPool.keySet().iterator();
+ while (i.hasNext())
+ {
+ Object att = i.next();
+ b.append(att);
+ b.append('\n');
+ }
+ return b.toString();
+ }
+
+ /**
+ * Searches the AttributeSet pool and returns a pooled instance if available,
+ * or pool a new one.
+ *
+ * @return an immutable attribute set that equals the current search key
+ */
+ private AttributeSet searchImmutableSet()
+ {
+ SmallAttributeSet k = createSmallAttributeSet(search);
+ WeakReference ref = (WeakReference) attributeSetPool.get(k);
+ SmallAttributeSet a;
+ if (ref == null || (a = (SmallAttributeSet) ref.get()) == null)
+ {
+ a = k;
+ attributeSetPool.put(a, new WeakReference(a));
+ }
+ return a;
+ }
+
+ /**
+ * Cleans up the attribute set pool from entries that are no longer
+ * referenced.
+ */
+ private void cleanupPool()
+ {
+ // TODO: How else can we force cleaning up the WeakHashMap?
+ attributeSetPool.size();
+ }
+
+ /**
+ * Returns a MutableAttributeSet that holds a. If a itself is mutable,
+ * this returns a itself, otherwise it creates a new SimpleAtttributeSet
+ * via {@link #createLargeAttributeSet(AttributeSet)}.
+ *
+ * @param a the AttributeSet to create a mutable set for
+ *
+ * @return a mutable attribute set that corresponds to a
+ */
+ private MutableAttributeSet getMutableAttributeSet(AttributeSet a)
+ {
+ MutableAttributeSet mas;
+ if (a instanceof MutableAttributeSet)
+ mas = (MutableAttributeSet) a;
+ else
+ mas = createLargeAttributeSet(a);
+ return mas;
+ }
+}