mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			991 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			991 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Java
		
	
	
	
| /* 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;
 | |
|   }
 | |
| }
 |