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 }