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  * <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.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 }