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 }