001/* HashAttributeSet.java -- 
002   Copyright (C) 2003, 2004, 2005, 2006  Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038package javax.print.attribute;
039
040import java.io.IOException;
041import java.io.ObjectInputStream;
042import java.io.ObjectOutputStream;
043import java.io.Serializable;
044import java.util.HashMap;
045import java.util.Iterator;
046
047/**
048 * <code>HashAttributeSet</code> provides an implementation of
049 * {@link javax.print.attribute.AttributeSet}.
050 */
051public class HashAttributeSet implements AttributeSet, Serializable
052{
053  private static final long serialVersionUID = 5311560590283707917L;
054  
055  private Class myInterface;
056  private transient HashMap attributeMap = new HashMap();
057
058  /**
059   * Creates an empty <code>HashAttributeSet</code> object.
060   */
061  public HashAttributeSet()
062  {
063    this(Attribute.class);
064  }
065
066  /**
067   * Creates a <code>HashAttributeSet</code> object with the given
068   * attribute in it.
069   *
070   * @param attribute the attribute to put into the set
071   *
072   * @exception NullPointerException if attribute is null
073   */
074  public HashAttributeSet(Attribute attribute)
075  {
076    this(attribute, Attribute.class);
077  }
078
079  /**
080   * Creates a <code>HashAttributeSet</code> object with the given
081   * attributes in it.
082   *
083   * @param attributes the array of attributes to put into the set. If
084   * <code>null</code> an empty set is created.
085   *
086   * @exception NullPointerException if one of the attributes of the given
087   * array is null.
088   */
089  public HashAttributeSet(Attribute[] attributes)
090  {
091    this(attributes, Attribute.class);
092  }
093
094  /**
095   * Creates a <code>HashAttributeSet</code> object with attributes
096   * of the given attributes set in it.
097   *
098   * @param attributes the attributes set to put into the set. If 
099   * <code>null</code> an empty set is created.
100   */
101  public HashAttributeSet(AttributeSet attributes)
102  {
103    this(attributes, Attribute.class);
104  }
105
106  /**
107   * Creates an empty <code>HashAttributeSet</code> object.
108   *
109   * @param interfaceName the interface that all members must implement
110   *
111   * @exception NullPointerException if interfaceName is null
112   */
113  protected HashAttributeSet(Class<?> interfaceName)
114  {
115    if (interfaceName == null)
116      throw new NullPointerException("interfaceName may not be null");
117    
118    myInterface = interfaceName;
119  }
120  
121  /**
122   * Creates a <code>HashAttributeSet</code> object with the given
123   * attribute in it.
124   * 
125   * @param attribute the attribute to put into the set.
126   * @param interfaceName the interface that all members must implement.
127   *
128   * @exception ClassCastException if attribute is not an interface of
129   * interfaceName
130   * @exception NullPointerException if attribute or interfaceName is null
131   */
132  protected HashAttributeSet(Attribute attribute, Class<?> interfaceName)
133  {
134    this(interfaceName);
135    
136    if (attribute == null)
137      throw new NullPointerException();
138    
139    addInternal(attribute, interfaceName);
140  }
141
142  /**
143   * Creates a <code>HashAttributeSet</code> object with the given
144   * attributes in it.
145   *
146   * @param attributes the array of attributes to put into the set. If
147   * <code>null</code> an empty set is created.
148   * @param interfaceName the interface that all members must implement.
149   *
150   * @exception ClassCastException if any element of attributes is not an
151   * interface of interfaceName
152   * @exception NullPointerException if attributes or interfaceName is null
153   */
154  protected HashAttributeSet(Attribute[] attributes, Class<?> interfaceName)
155  {
156    this(interfaceName);
157    
158    if (attributes != null)
159      {
160        for (int index = 0; index < attributes.length; index++)
161          addInternal(attributes[index], interfaceName);
162      }
163  }
164
165  /**
166   * Creates a <code>HashAttributeSet</code> object with attributes
167   * of the given attributes set in it.
168   *
169   * @param attributes the attributes set to put into the set. If 
170   * <code>null</code> an empty set is created.
171   * @param interfaceName the interface that all members must implement.
172   *
173   * @exception ClassCastException if any element of attributes is not an
174   * interface of interfaceName
175   */
176  protected HashAttributeSet(AttributeSet attributes, Class<?> interfaceName)
177  {
178    this(interfaceName);
179    
180    if (attributes != null)
181      addAllInternal(attributes, interfaceName);
182  }
183
184  /**
185   * Adds the specified attribute value to this attribute set 
186   * if it is not already present.
187   * 
188   * This operation removes any existing attribute of the same category 
189   * before adding the given attribute to the set. 
190   * 
191   * @param attribute the attribute to add.
192   * @return <code>true</code> if the set is changed, false otherwise.
193   * @throws NullPointerException if the attribute is <code>null</code>.
194   * @throws UnmodifiableSetException if the set does not support modification.
195   */
196  public boolean add(Attribute attribute)
197  {
198    return addInternal(attribute, myInterface);
199  }
200
201  private boolean addInternal(Attribute attribute, Class interfaceName)
202  {
203    if (attribute == null)
204      throw new NullPointerException("attribute may not be null");
205
206    AttributeSetUtilities.verifyAttributeCategory(interfaceName,
207                                                  myInterface);
208
209    Object old = attributeMap.put
210      (attribute.getCategory(), AttributeSetUtilities.verifyAttributeValue
211                                  (attribute, interfaceName));
212    return !attribute.equals(old);
213  }
214
215  /**
216   * Adds all of the elements in the specified set to this attribute set.
217   * 
218   * @param attributes the set of attributes to add.
219   * @return <code>true</code> if the set is changed, false otherwise.
220   * @throws UnmodifiableSetException if the set does not support modification.
221   * 
222   * @see #add(Attribute)
223   */
224  public boolean addAll(AttributeSet attributes)
225  {
226    return addAllInternal(attributes, myInterface);
227  }
228
229  private boolean addAllInternal(AttributeSet attributes, Class interfaceName)
230  {
231    boolean modified = false;
232    Attribute[] array = attributes.toArray();
233
234    for (int index = 0; index < array.length; index++)
235      if (addInternal(array[index], interfaceName))
236        modified = true;
237
238    return modified;
239  }
240
241  /**
242   * Removes all attributes from this attribute set.
243   * 
244   * @throws UnmodifiableSetException if the set does not support modification.
245   */
246  public void clear()
247  {
248    attributeMap.clear();
249  }
250
251  /**
252   * Checks if this attributes set contains an attribute with the given 
253   * category.
254   * 
255   * @param category the category to test for.
256   * @return <code>true</code> if an attribute of the category is contained
257   * in the set, <code>false</code> otherwise.
258   */
259  public boolean containsKey(Class<?> category)
260  {
261    return attributeMap.containsKey(category);
262  }
263
264  /**
265   * Checks if this attribute set contains the given attribute.
266   * 
267   * @param attribute the attribute to test for.
268   * @return <code>true</code> if the attribute is contained in the set,
269   * <code>false</code> otherwise.
270   */
271  public boolean containsValue(Attribute attribute)
272  {
273    return attributeMap.containsValue(attribute);
274  }
275
276  /**
277   * Tests this set for equality with the given object. <code>true</code> is
278   * returned, if the given object is also of type <code>AttributeSet</code>
279   * and the contained attributes are the same as in this set.
280   * 
281   * @param obj the Object to test.
282   * @return <code>true</code> if equal, false otherwise.
283   */
284  public boolean equals(Object obj)
285  {
286    if (! (obj instanceof HashAttributeSet))
287      return false;
288
289    return attributeMap.equals(((HashAttributeSet) obj).attributeMap);
290  }
291
292  /**
293   * Returns the attribute object contained in this set for the given attribute
294   * category. 
295   * 
296   * @param category the category of the attribute. A <code>Class</code> 
297   * instance of a class implementing the <code>Attribute</code> interface. 
298   * @return The attribute for this category or <code>null</code> if no 
299   * attribute is contained for the given category. 
300   * @throws NullPointerException if category is null.
301   * @throws ClassCastException if category is not implementing 
302   * <code>Attribute</code>.
303   */
304  public Attribute get(Class<?> category)
305  {
306    if (category == null)
307      throw new NullPointerException("category may not be null");
308    
309    return (Attribute) attributeMap.get(category);
310  }
311  
312  /**
313   * Returns the hashcode value. The hashcode value is the sum of all hashcodes
314   * of the attributes contained in this set.
315   * 
316   * @return The hashcode for this attribute set.
317   */
318  public int hashCode()
319  {
320    int hashcode = 0;
321    Iterator it = attributeMap.values().iterator();
322    while (it.hasNext())
323      hashcode = hashcode + it.next().hashCode();
324          
325    return hashcode;
326  }
327
328  /**
329   * Checks if the attribute set is empty.
330   *
331   * @return <code>true</code> if the attribute set is empty, false otherwise.
332   */
333  public boolean isEmpty()
334  {
335    return attributeMap.isEmpty();
336  }
337
338  /**
339   * Removes the given attribute from the set. If the given attribute is <code>null</code>
340   * nothing is done and <code>false</code> is returned.
341   * 
342   * @param attribute the attribute to remove.  
343   * @return <code>true</code> if removed, false in all other cases. 
344   * @throws UnmodifiableSetException if the set does not support modification.
345   */
346  public boolean remove(Attribute attribute)
347  {
348    if (attribute == null)
349      return false;
350
351    return attributeMap.remove(attribute.getCategory()) != null;
352  }
353
354  /**
355   * Removes the attribute entry of the given category from the set. If the given
356   * category is <code>null</code> nothing is done and <code>false</code> is returned.
357   * 
358   * @param category the category of the entry to be removed.
359   * @return <code>true</code> if an attribute is removed, false in all other cases. 
360   * @throws UnmodifiableSetException if the set does not support modification.
361   */
362  public boolean remove(Class<?> category)
363  {
364    if (category == null)
365      return false;
366
367    return attributeMap.remove(category) != null;
368  }
369
370  /**
371   * Returns the number of elements in this attribute set.
372   *
373   * @return The number of elements.
374   */
375  public int size()
376  {
377    return attributeMap.size();
378  }
379
380  /**
381   * Returns the content of the attribute set as an array
382   *
383   * @return An array of attributes.
384   */
385  public Attribute[] toArray()
386  {
387    int index = 0;
388    Iterator it = attributeMap.values().iterator();
389    Attribute[] array = new Attribute[size()];
390
391    while (it.hasNext())
392      {
393        array[index] = (Attribute) it.next();
394        index++;
395      }
396    
397    return array;
398  }
399  
400  // Implemented as specified in serialized form
401  private void readObject(ObjectInputStream s)
402    throws ClassNotFoundException, IOException
403  {
404    myInterface = (Class) s.readObject();
405    int size = s.readInt();
406    attributeMap = new HashMap(size);
407    for (int i=0; i < size; i++)
408      add((Attribute) s.readObject());
409  }
410         
411  private void writeObject(ObjectOutputStream s) throws IOException
412  {
413    s.writeObject(myInterface);
414    s.writeInt(size());
415    Iterator it = attributeMap.values().iterator();
416    while (it.hasNext())
417      s.writeObject(it.next());    
418  }
419}