001 /*
002 * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package javax.print.attribute;
027
028 import java.io.IOException;
029 import java.io.ObjectInputStream;
030 import java.io.ObjectOutputStream;
031 import java.io.Serializable;
032 import java.util.HashMap;
033
034 /**
035 * Class HashAttributeSet provides an <code>AttributeSet</code>
036 * implementation with characteristics of a hash map.
037 * <P>
038 *
039 * @author Alan Kaminsky
040 */
041 public class HashAttributeSet implements AttributeSet, Serializable {
042
043 private static final long serialVersionUID = 5311560590283707917L;
044
045 /**
046 * The interface of which all members of this attribute set must be an
047 * instance. It is assumed to be interface {@link Attribute Attribute}
048 * or a subinterface thereof.
049 * @serial
050 */
051 private Class myInterface;
052
053 /*
054 * A HashMap used by the implementation.
055 * The serialised form doesn't include this instance variable.
056 */
057 private transient HashMap attrMap = new HashMap();
058
059 /**
060 * Write the instance to a stream (ie serialize the object)
061 *
062 * @serialData
063 * The serialized form of an attribute set explicitly writes the
064 * number of attributes in the set, and each of the attributes.
065 * This does not guarantee equality of serialized forms since
066 * the order in which the attributes are written is not defined.
067 */
068 private void writeObject(ObjectOutputStream s) throws IOException {
069
070 s.defaultWriteObject();
071 Attribute[] attrs = toArray();
072 s.writeInt(attrs.length);
073 for (int i = 0; i < attrs.length; i++) {
074 s.writeObject(attrs[i]);
075 }
076 }
077
078 /**
079 * Reconstitute an instance from a stream that is, deserialize it).
080 */
081 private void readObject(ObjectInputStream s)
082 throws ClassNotFoundException, IOException {
083
084 s.defaultReadObject();
085 attrMap = new HashMap();
086 int count = s.readInt();
087 Attribute attr;
088 for (int i = 0; i < count; i++) {
089 attr = (Attribute) s.readObject();
090 add(attr);
091 }
092 }
093
094 /**
095 * Construct a new, empty attribute set.
096 */
097 public HashAttributeSet() {
098 this (Attribute.class);
099 }
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,
182 Class<?> interfaceName) {
183 if (interfaceName == null) {
184 throw new NullPointerException("null interface");
185 }
186 myInterface = interfaceName;
187 add(attribute);
188 }
189
190 /**
191 * Construct a new attribute set, where the members of the attribute
192 * set are restricted to the given interface.
193 * The new attribute set is populated
194 * by adding the elements of <CODE>attributes</CODE> array to the set in
195 * sequence, starting at index 0. Thus, later array elements may replace
196 * earlier array elements if the array contains duplicate attribute
197 * values or attribute categories.
198 *
199 * @param attributes Array of attribute values to add to the set. If
200 * null, an empty attribute set is constructed.
201 * @param interfaceName The interface of which all members of this
202 * attribute set must be an instance. It is assumed to
203 * be interface {@link Attribute Attribute} or a
204 * subinterface thereof.
205 *
206 * @exception NullPointerException
207 * (unchecked exception) Thrown if any element of
208 * <CODE>attributes</CODE> is null.
209 * @exception NullPointerException if interfaceName is null.
210 * @exception ClassCastException
211 * (unchecked exception) Thrown if any element of
212 * <CODE>attributes</CODE> is not an instance of
213 * <CODE>interfaceName</CODE>.
214 */
215 protected HashAttributeSet(Attribute[] attributes,
216 Class<?> interfaceName) {
217 if (interfaceName == null) {
218 throw new NullPointerException("null interface");
219 }
220 myInterface = interfaceName;
221 int n = attributes == null ? 0 : attributes.length;
222 for (int i = 0; i < n; ++i) {
223 add(attributes[i]);
224 }
225 }
226
227 /**
228 * Construct a new attribute set, initially populated with the
229 * values from the given set where the members of the attribute
230 * set are restricted to the given interface.
231 *
232 * @param attributes set of attribute values to initialise the set. If
233 * null, an empty attribute set is constructed.
234 * @param interfaceName The interface of which all members of this
235 * attribute set must be an instance. It is assumed to
236 * be interface {@link Attribute Attribute} or a
237 * subinterface thereof.
238 *
239 * @exception ClassCastException
240 * (unchecked exception) Thrown if any element of
241 * <CODE>attributes</CODE> is not an instance of
242 * <CODE>interfaceName</CODE>.
243 */
244 protected HashAttributeSet(AttributeSet attributes,
245 Class<?> interfaceName) {
246 myInterface = interfaceName;
247 if (attributes != null) {
248 Attribute[] attribArray = attributes.toArray();
249 int n = attribArray == null ? 0 : attribArray.length;
250 for (int i = 0; i < n; ++i) {
251 add(attribArray[i]);
252 }
253 }
254 }
255
256 /**
257 * Returns the attribute value which this attribute set contains in the
258 * given attribute category. Returns <tt>null</tt> if this attribute set
259 * does not contain any attribute value in the given attribute category.
260 *
261 * @param category Attribute category whose associated attribute value
262 * is to be returned. It must be a
263 * {@link java.lang.Class Class}
264 * that implements interface {@link Attribute
265 * Attribute}.
266 *
267 * @return The attribute value in the given attribute category contained
268 * in this attribute set, or <tt>null</tt> if this attribute set
269 * does not contain any attribute value in the given attribute
270 * category.
271 *
272 * @throws NullPointerException
273 * (unchecked exception) Thrown if the <CODE>category</CODE> is null.
274 * @throws ClassCastException
275 * (unchecked exception) Thrown if the <CODE>category</CODE> is not a
276 * {@link java.lang.Class Class} that implements interface {@link
277 * Attribute Attribute}.
278 */
279 public Attribute get(Class<?> category) {
280 return (Attribute) attrMap.get(AttributeSetUtilities
281 .verifyAttributeCategory(category, Attribute.class));
282 }
283
284 /**
285 * Adds the specified attribute to this attribute set if it is not
286 * already present, first removing any existing in the same
287 * attribute category as the specified attribute value.
288 *
289 * @param attribute Attribute value to be added to this attribute set.
290 *
291 * @return <tt>true</tt> if this attribute set changed as a result of the
292 * call, i.e., the given attribute value was not already a
293 * member of this attribute set.
294 *
295 * @throws NullPointerException
296 * (unchecked exception) Thrown if the <CODE>attribute</CODE> is null.
297 * @throws UnmodifiableSetException
298 * (unchecked exception) Thrown if this attribute set does not support
299 * the <CODE>add()</CODE> operation.
300 */
301 public boolean add(Attribute attribute) {
302 Object oldAttribute = attrMap.put(attribute.getCategory(),
303 AttributeSetUtilities.verifyAttributeValue(attribute,
304 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 category != null
326 && AttributeSetUtilities.verifyAttributeCategory(
327 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 attribute != null
348 && attrMap.remove(attribute.getCategory()) != null;
349 }
350
351 /**
352 * Returns <tt>true</tt> 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 <tt>true</tt> if this attribute set contains an attribute
359 * value for the specified category.
360 */
361 public boolean containsKey(Class<?> category) {
362 return category != null
363 && AttributeSetUtilities.verifyAttributeCategory(
364 category, Attribute.class) != null
365 && attrMap.get(category) != null;
366 }
367
368 /**
369 * Returns <tt>true</tt> if this attribute set contains the given
370 * attribute.
371 *
372 * @param attribute value whose presence in this attribute set is
373 * to be tested.
374 *
375 * @return <tt>true</tt> if this attribute set contains the given
376 * attribute value.
377 */
378 public boolean containsValue(Attribute attribute) {
379 return attribute != null
380 && attribute instanceof Attribute
381 && attribute.equals(attrMap.get(((Attribute) attribute)
382 .getCategory()));
383 }
384
385 /**
386 * Adds all of the elements in the specified set to this attribute.
387 * The outcome is the same as if the
388 * {@link #add(Attribute) <CODE>add(Attribute)</CODE>}
389 * operation had been applied to this attribute set successively with
390 * each element from the specified set.
391 * The behavior of the <CODE>addAll(AttributeSet)</CODE>
392 * operation is unspecified if the specified set is modified while
393 * the operation is in progress.
394 * <P>
395 * If the <CODE>addAll(AttributeSet)</CODE> operation throws an exception,
396 * the effect on this attribute set's state is implementation dependent;
397 * elements from the specified set before the point of the exception may
398 * or may not have been added to this attribute set.
399 *
400 * @param attributes whose elements are to be added to this attribute
401 * set.
402 *
403 * @return <tt>true</tt> if this attribute set changed as a result of the
404 * call.
405 *
406 * @throws UnmodifiableSetException
407 * (Unchecked exception) Thrown if this attribute set does not
408 * support the <tt>addAll(AttributeSet)</tt> method.
409 * @throws NullPointerException
410 * (Unchecked exception) Thrown if some element in the specified
411 * set is null, or the set is null.
412 *
413 * @see #add(Attribute)
414 */
415 public boolean addAll(AttributeSet attributes) {
416
417 Attribute[] attrs = attributes.toArray();
418 boolean result = false;
419 for (int i = 0; i < attrs.length; i++) {
420 Attribute newValue = AttributeSetUtilities
421 .verifyAttributeValue(attrs[i], myInterface);
422 Object oldValue = attrMap.put(newValue.getCategory(),
423 newValue);
424 result = (!newValue.equals(oldValue)) || result;
425 }
426 return result;
427 }
428
429 /**
430 * Returns the number of attributes in this attribute set. If this
431 * attribute set contains more than <tt>Integer.MAX_VALUE</tt> elements,
432 * returns <tt>Integer.MAX_VALUE</tt>.
433 *
434 * @return The number of attributes in this attribute set.
435 */
436 public int size() {
437 return attrMap.size();
438 }
439
440 /**
441 *
442 * @return the Attributes contained in this set as an array, zero length
443 * if the AttributeSet is empty.
444 */
445 public Attribute[] toArray() {
446 Attribute[] attrs = new Attribute[size()];
447 attrMap.values().toArray(attrs);
448 return attrs;
449 }
450
451 /**
452 * Removes all attributes from this attribute set.
453 *
454 * @throws UnmodifiableSetException
455 * (unchecked exception) Thrown if this attribute set does not support
456 * the <CODE>clear()</CODE> operation.
457 */
458 public void clear() {
459 attrMap.clear();
460 }
461
462 /**
463 * Returns true if this attribute set contains no attributes.
464 *
465 * @return true if this attribute set contains no attributes.
466 */
467 public boolean isEmpty() {
468 return attrMap.isEmpty();
469 }
470
471 /**
472 * Compares the specified object with this attribute set for equality.
473 * Returns <tt>true</tt> if the given object is also an attribute set and
474 * the two attribute sets contain the same attribute category-attribute
475 * value mappings. This ensures that the
476 * <tt>equals()</tt> method works properly across different
477 * implementations of the AttributeSet interface.
478 *
479 * @param object to be compared for equality with this attribute set.
480 *
481 * @return <tt>true</tt> if the specified object is equal to this
482 * attribute set.
483 */
484
485 public boolean equals(Object object) {
486 if (object == null || !(object instanceof AttributeSet)) {
487 return false;
488 }
489
490 AttributeSet aset = (AttributeSet) object;
491 if (aset.size() != size()) {
492 return false;
493 }
494
495 Attribute[] attrs = toArray();
496 for (int i = 0; i < attrs.length; i++) {
497 if (!aset.containsValue(attrs[i])) {
498 return false;
499 }
500 }
501 return true;
502 }
503
504 /**
505 * Returns the hash code value for this attribute set.
506 * The hash code of an attribute set is defined to be the sum
507 * of the hash codes of each entry in the AttributeSet.
508 * This ensures that <tt>t1.equals(t2)</tt> implies that
509 * <tt>t1.hashCode()==t2.hashCode()</tt> for any two attribute sets
510 * <tt>t1</tt> and <tt>t2</tt>, as required by the general contract of
511 * {@link java.lang.Object#hashCode() <CODE>Object.hashCode()</CODE>}.
512 *
513 * @return The hash code value for this attribute set.
514 */
515 public int hashCode() {
516 int hcode = 0;
517 Attribute[] attrs = toArray();
518 for (int i = 0; i < attrs.length; i++) {
519 hcode += attrs[i].hashCode();
520 }
521 return hcode;
522 }
523
524 }
|