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<Class<?>, Attribute> 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 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 }