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} 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<Class<?>, Attribute> 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} 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} 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} 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} is null. 175 * @exception NullPointerException if interfaceName is null. 176 * @exception ClassCastException 177 * (unchecked exception) Thrown if {@code attribute} is not an 178 * instance of {@code interfaceName}. 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} 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} is null. 207 * @exception NullPointerException if interfaceName is null. 208 * @exception ClassCastException 209 * (unchecked exception) Thrown if any element of 210 * {@code attributes} is not an instance of 211 * {@code interfaceName}. 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} is not an instance of 239 * {@code interfaceName}. 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 {@code null} 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 {@code null} 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} is null. 270 * @throws ClassCastException 271 * (unchecked exception) Thrown if the {@code category} 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 attrMap.get(AttributeSetUtilities. 277 verifyAttributeCategory(category, 278 Attribute.class)); 279 } 280 281 /** 282 * Adds the specified attribute to this attribute set if it is not 283 * already present, first removing any existing in the same 284 * attribute category as the specified attribute value. 285 * 286 * @param attribute Attribute value to be added to this attribute set. 287 * 288 * @return {@code true} if this attribute set changed as a result of the 289 * call, i.e., the given attribute value was not already a 290 * member of this attribute set. 291 * 292 * @throws NullPointerException 293 * (unchecked exception) Thrown if the {@code attribute} is null. 294 * @throws UnmodifiableSetException 295 * (unchecked exception) Thrown if this attribute set does not support 296 * the {@code add()} operation. 297 */ 298 public boolean add(Attribute attribute) { 299 Object oldAttribute = 300 attrMap.put(attribute.getCategory(), 301 AttributeSetUtilities. 302 verifyAttributeValue(attribute, myInterface)); 303 return (!attribute.equals(oldAttribute)); 304 } 305 306 /** 307 * Removes any attribute for this category from this attribute set if 308 * present. If {@code category} is null, then 309 * {@code remove()} does nothing and returns {@code false}. 310 * 311 * @param category Attribute category to be removed from this 312 * attribute set. 313 * 314 * @return {@code true} if this attribute set changed as a result of the 315 * call, i.e., the given attribute category had been a member of 316 * this attribute set. 317 * 318 * @throws UnmodifiableSetException 319 * (unchecked exception) Thrown if this attribute set does not 320 * support the {@code remove()} operation. 321 */ 322 public boolean remove(Class<?> category) { 323 return 324 category != null && 325 AttributeSetUtilities. 326 verifyAttributeCategory(category, Attribute.class) != null && 327 attrMap.remove(category) != null; 328 } 329 330 /** 331 * Removes the specified attribute from this attribute set if 332 * present. If {@code attribute} is null, then 333 * {@code remove()} does nothing and returns {@code false}. 334 * 335 * @param attribute Attribute value to be removed from this attribute set. 336 * 337 * @return {@code true} if this attribute set changed as a result of the 338 * call, i.e., the given attribute value had been a member of 339 * this attribute set. 340 * 341 * @throws UnmodifiableSetException 342 * (unchecked exception) Thrown if this attribute set does not 343 * support the {@code remove()} operation. 344 */ 345 public boolean remove(Attribute attribute) { 346 return 347 attribute != null && 348 attrMap.remove(attribute.getCategory()) != null; 349 } 350 351 /** 352 * Returns {@code true} if this attribute set contains an 353 * attribute for the specified category. 354 * 355 * @param category whose presence in this attribute set is 356 * to be tested. 357 * 358 * @return {@code true} if this attribute set contains an attribute 359 * value for the specified category. 360 */ 361 public boolean containsKey(Class<?> category) { 362 return 363 category != null && 364 AttributeSetUtilities. 365 verifyAttributeCategory(category, Attribute.class) != null && 366 attrMap.get(category) != null; 367 } 368 369 /** 370 * Returns {@code true} if this attribute set contains the given 371 * attribute. 372 * 373 * @param attribute value whose presence in this attribute set is 374 * to be tested. 375 * 376 * @return {@code true} if this attribute set contains the given 377 * attribute value. 378 */ 379 public boolean containsValue(Attribute attribute) { 380 return 381 attribute != null && 382 attribute instanceof Attribute && 383 attribute.equals(attrMap.get(attribute.getCategory())); 384 } 385 386 /** 387 * Adds all of the elements in the specified set to this attribute. 388 * The outcome is the same as if the 389 * {@link #add(Attribute) add(Attribute)} 390 * operation had been applied to this attribute set successively with 391 * each element from the specified set. 392 * The behavior of the {@code addAll(AttributeSet)} 393 * operation is unspecified if the specified set is modified while 394 * the operation is in progress. 395 * <P> 396 * If the {@code addAll(AttributeSet)} operation throws an exception, 397 * the effect on this attribute set's state is implementation dependent; 398 * elements from the specified set before the point of the exception may 399 * or may not have been added to this attribute set. 400 * 401 * @param attributes whose elements are to be added to this attribute 402 * set. 403 * 404 * @return {@code true} if this attribute set changed as a result of the 405 * call. 406 * 407 * @throws UnmodifiableSetException 408 * (Unchecked exception) Thrown if this attribute set does not 409 * support the {@code addAll(AttributeSet)} method. 410 * @throws NullPointerException 411 * (Unchecked exception) Thrown if some element in the specified 412 * set is null, or the set is null. 413 * 414 * @see #add(Attribute) 415 */ 416 public boolean addAll(AttributeSet attributes) { 417 418 Attribute []attrs = attributes.toArray(); 419 boolean result = false; 420 for (int i=0; i<attrs.length; i++) { 421 Attribute newValue = 422 AttributeSetUtilities.verifyAttributeValue(attrs[i], 423 myInterface); 424 Object oldValue = attrMap.put(newValue.getCategory(), newValue); 425 result = (! newValue.equals(oldValue)) || result; 426 } 427 return result; 428 } 429 430 /** 431 * Returns the number of attributes in this attribute set. If this 432 * attribute set contains more than {@code Integer.MAX_VALUE} elements, 433 * returns {@code Integer.MAX_VALUE}. 434 * 435 * @return The number of attributes in this attribute set. 436 */ 437 public int size() { 438 return attrMap.size(); 439 } 440 441 /** 442 * 443 * @return the Attributes contained in this set as an array, zero length 444 * if the AttributeSet is empty. 445 */ 446 public Attribute[] toArray() { 447 Attribute []attrs = new Attribute[size()]; 448 attrMap.values().toArray(attrs); 449 return attrs; 450 } 451 452 453 /** 454 * Removes all attributes from this attribute set. 455 * 456 * @throws UnmodifiableSetException 457 * (unchecked exception) Thrown if this attribute set does not support 458 * the {@code clear()} operation. 459 */ 460 public void clear() { 461 attrMap.clear(); 462 } 463 464 /** 465 * Returns true if this attribute set contains no attributes. 466 * 467 * @return true if this attribute set contains no attributes. 468 */ 469 public boolean isEmpty() { 470 return attrMap.isEmpty(); 471 } 472 473 /** 474 * Compares the specified object with this attribute set for equality. 475 * Returns {@code true} if the given object is also an attribute set and 476 * the two attribute sets contain the same attribute category-attribute 477 * value mappings. This ensures that the 478 * {@code equals()} method works properly across different 479 * implementations of the AttributeSet interface. 480 * 481 * @param object to be compared for equality with this attribute set. 482 * 483 * @return {@code true} if the specified object is equal to this 484 * attribute set. 485 */ 486 487 public boolean equals(Object object) { 488 if (object == null || !(object instanceof AttributeSet)) { 489 return false; 490 } 491 492 AttributeSet aset = (AttributeSet)object; 493 if (aset.size() != size()) { 494 return false; 495 } 496 497 Attribute[] attrs = toArray(); 498 for (int i=0;i<attrs.length; i++) { 499 if (!aset.containsValue(attrs[i])) { 500 return false; 501 } 502 } 503 return true; 504 } 505 506 /** 507 * Returns the hash code value for this attribute set. 508 * The hash code of an attribute set is defined to be the sum 509 * of the hash codes of each entry in the AttributeSet. 510 * This ensures that {@code t1.equals(t2)} implies that 511 * {@code t1.hashCode()==t2.hashCode()} for any two attribute sets 512 * {@code t1} and {@code t2}, as required by the general contract of 513 * {@link java.lang.Object#hashCode() Object.hashCode()}. 514 * 515 * @return The hash code value for this attribute set. 516 */ 517 public int hashCode() { 518 int hcode = 0; 519 Attribute[] attrs = toArray(); 520 for (int i=0;i<attrs.length; i++) { 521 hcode += attrs[i].hashCode(); 522 } 523 return hcode; 524 } 525 526 }