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