| /* 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; |
| } |
| } |