001    /* SimpleAttributeSet.java --
002       Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.swing.text;
040    
041    import java.io.Serializable;
042    import java.util.Enumeration;
043    import java.util.Hashtable;
044    
045    /**
046     * A set of attributes.
047     */
048    public class SimpleAttributeSet
049      implements MutableAttributeSet, Serializable, Cloneable
050    {
051      /** The serialization UID (compatible with JDK1.5). */
052      private static final long serialVersionUID = 8267656273837665219L;
053    
054      /**
055       * An empty attribute set.
056       */
057      public static final AttributeSet EMPTY = new EmptyAttributeSet();
058    
059      /** Storage for the attributes. */
060      Hashtable tab;
061    
062      /**
063       * Creates a new attribute set that is initially empty.
064       */
065      public SimpleAttributeSet()
066      {
067        tab = new Hashtable();
068      }
069    
070      /**
071       * Creates a new <code>SimpleAttributeSet</code> with the same attributes
072       * and resolve parent as the specified set.
073       *
074       * @param a  the attributes (<code>null</code> not permitted).
075       *
076       * @throws NullPointerException if <code>a</code> is <code>null</code>.
077       */
078      public SimpleAttributeSet(AttributeSet a)
079      {
080        tab = new Hashtable();
081        addAttributes(a);
082      }
083    
084      /**
085       * Adds an attribute with the given <code>name</code> and <code>value</code>
086       * to the set.  If the set already contains an attribute with the given
087       * <code>name</code>, the attribute value is updated.
088       *
089       * @param name  the attribute name (<code>null</code> not permitted).
090       * @param value  the value (<code>null</code> not permitted).
091       *
092       * @throws NullPointerException if either argument is <code>null</code>.
093       */
094      public void addAttribute(Object name, Object value)
095      {
096        tab.put(name, value);
097      }
098    
099      /**
100       * Adds all the attributes from <code>attributes</code> to this set.
101       *
102       * @param attributes  the set of attributes to add (<code>null</code> not
103       *                    permitted).
104       *
105       * @throws NullPointerException if <code>attributes</code> is
106       *         <code>null</code>.
107       */
108      public void addAttributes(AttributeSet attributes)
109      {
110        Enumeration e = attributes.getAttributeNames();
111        while (e.hasMoreElements())
112          {
113            Object name = e.nextElement();
114            Object val = attributes.getAttribute(name);
115            tab.put(name, val);
116          }
117      }
118    
119      /**
120       * Returns a clone of the attribute set.
121       *
122       * @return A clone of the attribute set.
123       */
124      public Object clone()
125      {
126        SimpleAttributeSet attr = null;
127        try
128          {
129            attr = (SimpleAttributeSet) super.clone();
130            attr.tab = (Hashtable) tab.clone();
131          }
132        catch (CloneNotSupportedException ex)
133          {
134            assert false;
135          }
136        return attr;
137      }
138    
139      /**
140       * Returns true if the given name and value represent an attribute
141       * found either in this AttributeSet or in its resolve parent hierarchy.
142       * @param name the key for the attribute
143       * @param value the value for the attribute
144       * @return true if the attribute is found here or in this set's resolve
145       * parent hierarchy
146       */
147      public boolean containsAttribute(Object name, Object value)
148      {
149        if (value == null)
150          throw new NullPointerException("Null 'value' argument.");
151        if (tab.containsKey(name))
152          return tab.get(name).equals(value);
153        else
154          {
155            AttributeSet p = getResolveParent();
156            if (p != null)
157              return p.containsAttribute(name, value);
158            else
159              return false;
160          }
161      }
162    
163      /**
164       * Returns true if the given name and value are found in this AttributeSet.
165       * Does not check the resolve parent.
166       * @param name the key for the attribute
167       * @param value the value for the attribute
168       * @return true if the attribute is found in this AttributeSet
169       */
170      boolean containsAttributeLocally(Object name, Object value)
171      {
172        return tab.containsKey(name)
173          && tab.get(name).equals(value);
174      }
175    
176      /**
177       * Returns <code>true</code> of this <code>AttributeSet</code> contains all
178       * of the specified <code>attributes</code>.
179       *
180       * @param attributes the requested attributes
181       *
182       * @return <code>true</code> of this <code>AttributeSet</code> contains all
183       *         of the specified <code>attributes</code>
184       */
185      public boolean containsAttributes(AttributeSet attributes)
186      {
187        Enumeration e = attributes.getAttributeNames();
188        while (e.hasMoreElements())
189          {
190            Object name = e.nextElement();
191            Object val = attributes.getAttribute(name);
192            if (! containsAttribute(name, val))
193              return false;
194          }
195        return true;
196      }
197    
198      /**
199       * Creates and returns a copy of this <code>AttributeSet</code>.
200       *
201       * @return a copy of this <code>AttributeSet</code>
202       */
203      public AttributeSet copyAttributes()
204      {
205        return (AttributeSet) clone();
206      }
207    
208      /**
209       * Checks this set for equality with an arbitrary object.
210       *
211       * @param obj  the object (<code>null</code> permitted).
212       *
213       * @return <code>true</code> if this set is equal to <code>obj</code>, and
214       *         <code>false</code> otherwise.
215       */
216      public boolean equals(Object obj)
217      {
218        return
219          (obj instanceof AttributeSet)
220          && this.isEqual((AttributeSet) obj);
221      }
222    
223      /**
224       * Returns the value of the specified attribute, or <code>null</code> if
225       * there is no attribute with that name.  If the attribute is not defined
226       * directly in this set, the parent hierarchy (if there is one) will be
227       * used.
228       *
229       * @param name  the attribute (<code>null</code> not permitted).
230       *
231       * @throws NullPointerException if <code>name</code> is <code>null</code>.
232       */
233      public Object getAttribute(Object name)
234      {
235        Object val = tab.get(name);
236        if (val != null)
237          return val;
238    
239        AttributeSet p = getResolveParent();
240        if (p != null)
241          return p.getAttribute(name);
242    
243        return null;
244      }
245    
246      /**
247       * Returns the number of attributes stored in this set, plus 1 if a parent
248       * has been specified (the reference to the parent is stored as a special
249       * attribute).  The attributes stored in the parent do NOT contribute
250       * to the count.
251       *
252       * @return The attribute count.
253       */
254      public int getAttributeCount()
255      {
256        return tab.size();
257      }
258    
259      /**
260       * Returns an enumeration of the attribute names.
261       *
262       * @return An enumeration of the attribute names.
263       */
264      public Enumeration<?> getAttributeNames()
265      {
266        return tab.keys();
267      }
268    
269      /**
270       * Returns the resolving parent.
271       *
272       * @return The resolving parent (possibly <code>null</code>).
273       *
274       * @see #setResolveParent(AttributeSet)
275       */
276      public AttributeSet getResolveParent()
277      {
278        return (AttributeSet) tab.get(ResolveAttribute);
279      }
280    
281      /**
282       * Returns a hash code for this instance.
283       *
284       * @return A hash code.
285       */
286      public int hashCode()
287      {
288        return tab.hashCode();
289      }
290    
291      /**
292       * Returns <code>true</code> if the given attribute is defined in this set,
293       * and <code>false</code> otherwise.  The parent attribute set is not
294       * checked.
295       *
296       * @param attrName  the attribute name (<code>null</code> not permitted).
297       */
298      public boolean isDefined(Object attrName)
299      {
300        return tab.containsKey(attrName);
301      }
302    
303      /**
304       * Returns <code>true</code> if the set contains no attributes, and
305       * <code>false</code> otherwise.  Note that the resolving parent is
306       * stored as an attribute, so this method will return <code>false</code> if
307       * a resolving parent is set.
308       *
309       * @return <code>true</code> if the set contains no attributes, and
310       * <code>false</code> otherwise.
311       */
312      public boolean isEmpty()
313      {
314        return tab.isEmpty();
315      }
316    
317      /**
318       * Returns true if the given set has the same number of attributes
319       * as this set and <code>containsAttributes(attr)</code> returns
320       * <code>true</code>.
321       *
322       * @param attr  the attribute set (<code>null</code> not permitted).
323       *
324       * @return A boolean.
325       *
326       * @throws NullPointerException if <code>attr</code> is <code>null</code>.
327       */
328      public boolean isEqual(AttributeSet attr)
329      {
330        return getAttributeCount() == attr.getAttributeCount()
331          && this.containsAttributes(attr);
332      }
333    
334      /**
335       * Removes the attribute with the specified <code>name</code>, if this
336       * attribute is defined.  This method will only remove an attribute from
337       * this set, not from the resolving parent.
338       *
339       * @param name  the attribute name (<code>null</code> not permitted).
340       *
341       * @throws NullPointerException if <code>name</code> is <code>null</code>.
342       */
343      public void removeAttribute(Object name)
344      {
345        tab.remove(name);
346      }
347    
348      /**
349       * Removes attributes from this set if they are found in the
350       * given set.  Only attributes whose key AND value are removed.
351       * Removes attributes only from this set, not from the resolving parent.
352       * Since the resolving parent is stored as an attribute, if
353       * <code>attributes</code> has the same resolving parent as this set, the
354       * parent will be removed from this set.
355       *
356       * @param attributes  the attributes (<code>null</code> not permitted).
357       */
358      public void removeAttributes(AttributeSet attributes)
359      {
360        Enumeration e = attributes.getAttributeNames();
361        while (e.hasMoreElements())
362          {
363            Object name = e.nextElement();
364            Object val = attributes.getAttribute(name);
365            if (containsAttributeLocally(name, val))
366              removeAttribute(name);
367          }
368      }
369    
370      /**
371       * Removes the attributes listed in <code>names</code>.
372       *
373       * @param names  the attribute names (<code>null</code> not permitted).
374       *
375       * @throws NullPointerException if <code>names</code> is <code>null</code>
376       *         or contains any <code>null</code> values.
377       */
378      public void removeAttributes(Enumeration<?> names)
379      {
380        while (names.hasMoreElements())
381          {
382            removeAttribute(names.nextElement());
383          }
384      }
385    
386      /**
387       * Sets the reolving parent for this set.  When looking up an attribute, if
388       * it is not found in this set, then the resolving parent is also used for
389       * the lookup.
390       * <p>
391       * Note that the parent is stored as an attribute, and will contribute 1 to
392       * the count returned by {@link #getAttributeCount()}.
393       *
394       * @param parent  the parent attribute set (<code>null</code> not permitted).
395       *
396       * @throws NullPointerException if <code>parent</code> is <code>null</code>.
397       *
398       * @see #setResolveParent(AttributeSet)
399       */
400      public void setResolveParent(AttributeSet parent)
401      {
402        addAttribute(ResolveAttribute, parent);
403      }
404    
405      /**
406       * Returns a string representation of this instance, typically used for
407       * debugging purposes.
408       *
409       * @return A string representation of this instance.
410       */
411      public String toString()
412      {
413        return tab.toString();
414      }
415    }