View Javadoc
1   /*
2    * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package javax.print.attribute;
27  
28  import java.io.IOException;
29  import java.io.ObjectInputStream;
30  import java.io.ObjectOutputStream;
31  import java.io.Serializable;
32  import java.util.HashMap;
33  
34  /**
35   * Class HashAttributeSet provides an <code>AttributeSet</code>
36   * implementation with characteristics of a hash map.
37   * <P>
38   *
39   * @author  Alan Kaminsky
40   */
41  public class HashAttributeSet implements AttributeSet, Serializable {
42  
43      private static final long serialVersionUID = 5311560590283707917L;
44  
45      /**
46       * The interface of which all members of this attribute set must be an
47       * instance. It is assumed to be interface {@link Attribute Attribute}
48       * or a subinterface thereof.
49       * @serial
50       */
51      private Class myInterface;
52  
53      /*
54       * A HashMap used by the implementation.
55       * The serialised form doesn't include this instance variable.
56       */
57      private transient HashMap attrMap = new HashMap();
58  
59      /**
60       * Write the instance to a stream (ie serialize the object)
61       *
62       * @serialData
63       * The serialized form of an attribute set explicitly writes the
64       * number of attributes in the set, and each of the attributes.
65       * This does not guarantee equality of serialized forms since
66       * the order in which the attributes are written is not defined.
67       */
68      private void writeObject(ObjectOutputStream s) throws IOException {
69  
70          s.defaultWriteObject();
71          Attribute [] attrs = toArray();
72          s.writeInt(attrs.length);
73          for (int i = 0; i < attrs.length; i++) {
74              s.writeObject(attrs[i]);
75          }
76      }
77  
78      /**
79       * Reconstitute an instance from a stream that is, deserialize it).
80       */
81      private void readObject(ObjectInputStream s)
82          throws ClassNotFoundException, IOException {
83  
84          s.defaultReadObject();
85          attrMap = new HashMap();
86          int count = s.readInt();
87          Attribute attr;
88          for (int i = 0; i < count; i++) {
89              attr = (Attribute)s.readObject();
90              add(attr);
91          }
92      }
93  
94      /**
95       * Construct a new, empty attribute set.
96       */
97      public HashAttributeSet() {
98          this(Attribute.class);
99      }
100 
101     /**
102      * Construct a new attribute set,
103      * initially populated with the given attribute.
104      *
105      * @param  attribute  Attribute value to add to the set.
106      *
107      * @exception  NullPointerException
108      *     (unchecked exception) Thrown if <CODE>attribute</CODE> is null.
109      */
110     public HashAttributeSet(Attribute attribute) {
111         this (attribute, Attribute.class);
112     }
113 
114     /**
115      * Construct a new attribute set,
116      * initially populated with the values from the
117      * given array. The new attribute set is populated by
118      * adding the elements of <CODE>attributes</CODE> array to the set in
119      * sequence, starting at index 0. Thus, later array elements may replace
120      * earlier array elements if the array contains duplicate attribute
121      * values or attribute categories.
122      *
123      * @param  attributes  Array of attribute values to add to the set.
124      *                    If null, an empty attribute set is constructed.
125      *
126      * @exception  NullPointerException
127      *     (unchecked exception) Thrown if any element of
128      *     <CODE>attributes</CODE> is null.
129      */
130     public HashAttributeSet(Attribute[] attributes) {
131         this (attributes, Attribute.class);
132     }
133 
134     /**
135      * Construct a new attribute set,
136      * initially populated with the values from the  given set.
137      *
138      * @param  attributes Set of attributes from which to initialise this set.
139      *                 If null, an empty attribute set is constructed.
140      *
141      */
142     public HashAttributeSet(AttributeSet attributes) {
143         this (attributes, Attribute.class);
144     }
145 
146     /**
147      * Construct a new, empty attribute set, where the members of
148      * the attribute set are restricted to the given interface.
149      *
150      * @param  interfaceName  The interface of which all members of this
151      *                     attribute set must be an instance. It is assumed to
152      *                     be interface {@link Attribute Attribute} or a
153      *                     subinterface thereof.
154      * @exception NullPointerException if interfaceName is null.
155      */
156     protected HashAttributeSet(Class<?> interfaceName) {
157         if (interfaceName == null) {
158             throw new NullPointerException("null interface");
159         }
160         myInterface = interfaceName;
161     }
162 
163     /**
164      * Construct a new attribute set, initially populated with the given
165      * attribute, where the members of the attribute set are restricted to the
166      * given interface.
167      *
168      * @param  attribute      Attribute value to add to the set.
169      * @param  interfaceName  The interface of which all members of this
170      *                    attribute set must be an instance. It is assumed to
171      *                    be interface {@link Attribute Attribute} or a
172      *                    subinterface thereof.
173      *
174      * @exception  NullPointerException
175      *     (unchecked exception) Thrown if <CODE>attribute</CODE> is null.
176      * @exception NullPointerException if interfaceName is null.
177      * @exception  ClassCastException
178      *     (unchecked exception) Thrown if <CODE>attribute</CODE> is not an
179      *     instance of <CODE>interfaceName</CODE>.
180      */
181     protected HashAttributeSet(Attribute attribute, Class<?> interfaceName) {
182         if (interfaceName == null) {
183             throw new NullPointerException("null interface");
184         }
185         myInterface = interfaceName;
186         add (attribute);
187     }
188 
189     /**
190      * Construct a new attribute set, where the members of the attribute
191      * set are restricted to the given interface.
192      * The new attribute set is populated
193      * by adding the elements of <CODE>attributes</CODE> array to the set in
194      * sequence, starting at index 0. Thus, later array elements may replace
195      * earlier array elements if the array contains duplicate attribute
196      * values or attribute categories.
197      *
198      * @param  attributes Array of attribute values to add to the set. If
199      *                    null, an empty attribute set is constructed.
200      * @param  interfaceName  The interface of which all members of this
201      *                    attribute set must be an instance. It is assumed to
202      *                    be interface {@link Attribute Attribute} or a
203      *                    subinterface thereof.
204      *
205      * @exception  NullPointerException
206      *     (unchecked exception) Thrown if any element of
207      * <CODE>attributes</CODE> is null.
208      * @exception NullPointerException if interfaceName is null.
209      * @exception  ClassCastException
210      *     (unchecked exception) Thrown if any element of
211      * <CODE>attributes</CODE> is not an instance of
212      * <CODE>interfaceName</CODE>.
213      */
214     protected HashAttributeSet(Attribute[] attributes, Class<?> interfaceName) {
215         if (interfaceName == null) {
216             throw new NullPointerException("null interface");
217         }
218         myInterface = interfaceName;
219         int n = attributes == null ? 0 : attributes.length;
220         for (int i = 0; i < n; ++ i) {
221             add (attributes[i]);
222         }
223     }
224 
225     /**
226      * Construct a new attribute set, initially populated with the
227      * values from the  given set where the members of the attribute
228      * set are restricted to the given interface.
229      *
230      * @param  attributes set of attribute values to initialise the set. If
231      *                    null, an empty attribute set is constructed.
232      * @param  interfaceName  The interface of which all members of this
233      *                    attribute set must be an instance. It is assumed to
234      *                    be interface {@link Attribute Attribute} or a
235      *                    subinterface thereof.
236      *
237      * @exception  ClassCastException
238      *     (unchecked exception) Thrown if any element of
239      * <CODE>attributes</CODE> is not an instance of
240      * <CODE>interfaceName</CODE>.
241      */
242     protected HashAttributeSet(AttributeSet attributes, Class<?> interfaceName) {
243       myInterface = interfaceName;
244       if (attributes != null) {
245         Attribute[] attribArray = attributes.toArray();
246         int n = attribArray == null ? 0 : attribArray.length;
247         for (int i = 0; i < n; ++ i) {
248           add (attribArray[i]);
249         }
250       }
251     }
252 
253     /**
254      * Returns the attribute value which this attribute set contains in the
255      * given attribute category. Returns <tt>null</tt> if this attribute set
256      * does not contain any attribute value in the given attribute category.
257      *
258      * @param  category  Attribute category whose associated attribute value
259      *                   is to be returned. It must be a
260      *                   {@link java.lang.Class Class}
261      *                   that implements interface {@link Attribute
262      *                   Attribute}.
263      *
264      * @return  The attribute value in the given attribute category contained
265      *          in this attribute set, or <tt>null</tt> if this attribute set
266      *          does not contain any attribute value in the given attribute
267      *          category.
268      *
269      * @throws  NullPointerException
270      *     (unchecked exception) Thrown if the <CODE>category</CODE> is null.
271      * @throws  ClassCastException
272      *     (unchecked exception) Thrown if the <CODE>category</CODE> is not a
273      *     {@link java.lang.Class Class} that implements interface {@link
274      *     Attribute Attribute}.
275      */
276     public Attribute get(Class<?> category) {
277         return (Attribute)
278             attrMap.get(AttributeSetUtilities.
279                         verifyAttributeCategory(category,
280                                                 Attribute.class));
281     }
282 
283     /**
284      * Adds the specified attribute to this attribute set if it is not
285      * already present, first removing any existing in the same
286      * attribute category as the specified attribute value.
287      *
288      * @param  attribute  Attribute value to be added to this attribute set.
289      *
290      * @return  <tt>true</tt> if this attribute set changed as a result of the
291      *          call, i.e., the given attribute value was not already a
292      *          member of this attribute set.
293      *
294      * @throws  NullPointerException
295      *    (unchecked exception) Thrown if the <CODE>attribute</CODE> is null.
296      * @throws  UnmodifiableSetException
297      *    (unchecked exception) Thrown if this attribute set does not support
298      *     the <CODE>add()</CODE> operation.
299      */
300     public boolean add(Attribute attribute) {
301         Object oldAttribute =
302             attrMap.put(attribute.getCategory(),
303                         AttributeSetUtilities.
304                         verifyAttributeValue(attribute, myInterface));
305         return (!attribute.equals(oldAttribute));
306     }
307 
308     /**
309      * Removes any attribute for this category from this attribute set if
310      * present. If <CODE>category</CODE> is null, then
311      * <CODE>remove()</CODE> does nothing and returns <tt>false</tt>.
312      *
313      * @param  category Attribute category to be removed from this
314      *                  attribute set.
315      *
316      * @return  <tt>true</tt> if this attribute set changed as a result of the
317      *         call, i.e., the given attribute category had been a member of
318      *         this attribute set.
319      *
320      * @throws  UnmodifiableSetException
321      *     (unchecked exception) Thrown if this attribute set does not
322      *     support the <CODE>remove()</CODE> operation.
323      */
324     public boolean remove(Class<?> category) {
325         return
326             category != null &&
327             AttributeSetUtilities.
328             verifyAttributeCategory(category, Attribute.class) != null &&
329             attrMap.remove(category) != null;
330     }
331 
332     /**
333      * Removes the specified attribute from this attribute set if
334      * present. If <CODE>attribute</CODE> is null, then
335      * <CODE>remove()</CODE> does nothing and returns <tt>false</tt>.
336      *
337      * @param attribute Attribute value to be removed from this attribute set.
338      *
339      * @return  <tt>true</tt> if this attribute set changed as a result of the
340      *         call, i.e., the given attribute value had been a member of
341      *         this attribute set.
342      *
343      * @throws  UnmodifiableSetException
344      *     (unchecked exception) Thrown if this attribute set does not
345      *     support the <CODE>remove()</CODE> operation.
346      */
347     public boolean remove(Attribute attribute) {
348         return
349             attribute != null &&
350             attrMap.remove(attribute.getCategory()) != null;
351     }
352 
353     /**
354      * Returns <tt>true</tt> if this attribute set contains an
355      * attribute for the specified category.
356      *
357      * @param  category whose presence in this attribute set is
358      *            to be tested.
359      *
360      * @return  <tt>true</tt> if this attribute set contains an attribute
361      *         value for the specified category.
362      */
363     public boolean containsKey(Class<?> category) {
364         return
365             category != null &&
366             AttributeSetUtilities.
367             verifyAttributeCategory(category, Attribute.class) != null &&
368             attrMap.get(category) != null;
369     }
370 
371     /**
372      * Returns <tt>true</tt> if this attribute set contains the given
373      * attribute.
374      *
375      * @param  attribute  value whose presence in this attribute set is
376      *            to be tested.
377      *
378      * @return  <tt>true</tt> if this attribute set contains the given
379      *      attribute    value.
380      */
381     public boolean containsValue(Attribute attribute) {
382         return
383            attribute != null &&
384            attribute instanceof Attribute &&
385            attribute.equals(attrMap.get(((Attribute)attribute).getCategory()));
386     }
387 
388     /**
389      * Adds all of the elements in the specified set to this attribute.
390      * The outcome is the same as if the
391      * {@link #add(Attribute) add(Attribute)}
392      * operation had been applied to this attribute set successively with
393      * each element from the specified set.
394      * The behavior of the <CODE>addAll(AttributeSet)</CODE>
395      * operation is unspecified if the specified set is modified while
396      * the operation is in progress.
397      * <P>
398      * If the <CODE>addAll(AttributeSet)</CODE> operation throws an exception,
399      * the effect on this attribute set's state is implementation dependent;
400      * elements from the specified set before the point of the exception may
401      * or may not have been added to this attribute set.
402      *
403      * @param  attributes  whose elements are to be added to this attribute
404      *            set.
405      *
406      * @return  <tt>true</tt> if this attribute set changed as a result of the
407      *          call.
408      *
409      * @throws  UnmodifiableSetException
410      *    (Unchecked exception) Thrown if this attribute set does not
411      *     support the <tt>addAll(AttributeSet)</tt> method.
412      * @throws  NullPointerException
413      *     (Unchecked exception) Thrown if some element in the specified
414      *     set is null, or the set is null.
415      *
416      * @see #add(Attribute)
417      */
418     public boolean addAll(AttributeSet attributes) {
419 
420         Attribute []attrs = attributes.toArray();
421         boolean result = false;
422         for (int i=0; i<attrs.length; i++) {
423             Attribute newValue =
424                 AttributeSetUtilities.verifyAttributeValue(attrs[i],
425                                                            myInterface);
426             Object oldValue = attrMap.put(newValue.getCategory(), newValue);
427             result = (! newValue.equals(oldValue)) || result;
428         }
429         return result;
430     }
431 
432     /**
433      * Returns the number of attributes in this attribute set. If this
434      * attribute set contains more than <tt>Integer.MAX_VALUE</tt> elements,
435      * returns  <tt>Integer.MAX_VALUE</tt>.
436      *
437      * @return  The number of attributes in this attribute set.
438      */
439     public int size() {
440         return attrMap.size();
441     }
442 
443     /**
444      *
445      * @return the Attributes contained in this set as an array, zero length
446      * if the AttributeSet is empty.
447      */
448     public Attribute[] toArray() {
449         Attribute []attrs = new Attribute[size()];
450         attrMap.values().toArray(attrs);
451         return attrs;
452     }
453 
454 
455     /**
456      * Removes all attributes from this attribute set.
457      *
458      * @throws  UnmodifiableSetException
459      *   (unchecked exception) Thrown if this attribute set does not support
460      *     the <CODE>clear()</CODE> operation.
461      */
462     public void clear() {
463         attrMap.clear();
464     }
465 
466    /**
467      * Returns true if this attribute set contains no attributes.
468      *
469      * @return true if this attribute set contains no attributes.
470      */
471     public boolean isEmpty() {
472         return attrMap.isEmpty();
473     }
474 
475     /**
476      * Compares the specified object with this attribute set for equality.
477      * Returns <tt>true</tt> if the given object is also an attribute set and
478      * the two attribute sets contain the same attribute category-attribute
479      * value mappings. This ensures that the
480      * <tt>equals()</tt> method works properly across different
481      * implementations of the AttributeSet interface.
482      *
483      * @param  object to be compared for equality with this attribute set.
484      *
485      * @return  <tt>true</tt> if the specified object is equal to this
486      *       attribute   set.
487      */
488 
489     public boolean equals(Object object) {
490         if (object == null || !(object instanceof AttributeSet)) {
491             return false;
492         }
493 
494         AttributeSet aset = (AttributeSet)object;
495         if (aset.size() != size()) {
496             return false;
497         }
498 
499         Attribute[] attrs = toArray();
500         for (int i=0;i<attrs.length; i++) {
501             if (!aset.containsValue(attrs[i])) {
502                 return false;
503             }
504         }
505         return true;
506     }
507 
508     /**
509      * Returns the hash code value for this attribute set.
510      * The hash code of an attribute set is defined to be the sum
511      * of the hash codes of each entry in the AttributeSet.
512      * This ensures that <tt>t1.equals(t2)</tt> implies that
513      * <tt>t1.hashCode()==t2.hashCode()</tt> for any two attribute sets
514      * <tt>t1</tt> and <tt>t2</tt>, as required by the general contract of
515      * {@link java.lang.Object#hashCode() Object.hashCode()}.
516      *
517      * @return  The hash code value for this attribute set.
518      */
519     public int hashCode() {
520         int hcode = 0;
521         Attribute[] attrs = toArray();
522         for (int i=0;i<attrs.length; i++) {
523             hcode += attrs[i].hashCode();
524         }
525         return hcode;
526     }
527 
528 }