1. /*
  2. * @(#)HashAttributeSet.java 1.12 04/05/05
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.print.attribute;
  8. import java.io.IOException;
  9. import java.io.ObjectInputStream;
  10. import java.io.ObjectOutputStream;
  11. import java.io.Serializable;
  12. import java.util.HashMap;
  13. /**
  14. * Class HashAttributeSet provides an <code>AttributeSet</code>
  15. * implementation with characteristics of a hash map.
  16. * <P>
  17. *
  18. * @author Alan Kaminsky
  19. */
  20. public class HashAttributeSet implements AttributeSet, Serializable {
  21. private static final long serialVersionUID = 5311560590283707917L;
  22. /**
  23. * The interface of which all members of this attribute set must be an
  24. * instance. It is assumed to be interface {@link Attribute Attribute}
  25. * or a subinterface thereof.
  26. * @serial
  27. */
  28. private Class myInterface;
  29. /*
  30. * A HashMap used by the implementation.
  31. * The serialised form doesn't include this instance variable.
  32. */
  33. private transient HashMap attrMap = new HashMap();
  34. /**
  35. * Write the instance to a stream (ie serialize the object)
  36. *
  37. * @serialData
  38. * The serialized form of an attribute set explicitly writes the
  39. * number of attributes in the set, and each of the attributes.
  40. * This does not guarantee equality of serialized forms since
  41. * the order in which the attributes are written is not defined.
  42. */
  43. private void writeObject(ObjectOutputStream s) throws IOException {
  44. s.defaultWriteObject();
  45. Attribute [] attrs = toArray();
  46. s.writeInt(attrs.length);
  47. for (int i = 0; i < attrs.length; i++) {
  48. s.writeObject(attrs[i]);
  49. }
  50. }
  51. /**
  52. * Reconstitute an instance from a stream that is, deserialize it).
  53. */
  54. private void readObject(ObjectInputStream s)
  55. throws ClassNotFoundException, IOException {
  56. s.defaultReadObject();
  57. attrMap = new HashMap();
  58. int count = s.readInt();
  59. Attribute attr;
  60. for (int i = 0; i < count; i++) {
  61. attr = (Attribute)s.readObject();
  62. add(attr);
  63. }
  64. }
  65. /**
  66. * Construct a new, empty attribute set.
  67. */
  68. public HashAttributeSet() {
  69. this(Attribute.class);
  70. }
  71. /**
  72. * Construct a new attribute set,
  73. * initially populated with the given attribute.
  74. *
  75. * @param attribute Attribute value to add to the set.
  76. *
  77. * @exception NullPointerException
  78. * (unchecked exception) Thrown if <CODE>attribute</CODE> is null.
  79. */
  80. public HashAttributeSet(Attribute attribute) {
  81. this (attribute, Attribute.class);
  82. }
  83. /**
  84. * Construct a new attribute set,
  85. * initially populated with the values from the
  86. * given array. The new attribute set is populated by
  87. * adding the elements of <CODE>attributes</CODE> array to the set in
  88. * sequence, starting at index 0. Thus, later array elements may replace
  89. * earlier array elements if the array contains duplicate attribute
  90. * values or attribute categories.
  91. *
  92. * @param attributes Array of attribute values to add to the set.
  93. * If null, an empty attribute set is constructed.
  94. *
  95. * @exception NullPointerException
  96. * (unchecked exception) Thrown if any element of
  97. * <CODE>attributes</CODE> is null.
  98. */
  99. public HashAttributeSet(Attribute[] attributes) {
  100. this (attributes, Attribute.class);
  101. }
  102. /**
  103. * Construct a new attribute set,
  104. * initially populated with the values from the given set.
  105. *
  106. * @param attributes Set of attributes from which to initialise this set.
  107. * If null, an empty attribute set is constructed.
  108. *
  109. */
  110. public HashAttributeSet(AttributeSet attributes) {
  111. this (attributes, Attribute.class);
  112. }
  113. /**
  114. * Construct a new, empty attribute set, where the members of
  115. * the attribute set are restricted to the given interface.
  116. *
  117. * @param interfaceName The interface of which all members of this
  118. * attribute set must be an instance. It is assumed to
  119. * be interface {@link Attribute Attribute} or a
  120. * subinterface thereof.
  121. * @exception NullPointerException if interfaceName is null.
  122. */
  123. protected HashAttributeSet(Class<?> interfaceName) {
  124. if (interfaceName == null) {
  125. throw new NullPointerException("null interface");
  126. }
  127. myInterface = interfaceName;
  128. }
  129. /**
  130. * Construct a new attribute set, initially populated with the given
  131. * attribute, where the members of the attribute set are restricted to the
  132. * given interface.
  133. *
  134. * @param attribute Attribute value to add to the set.
  135. * @param interfaceName The interface of which all members of this
  136. * attribute set must be an instance. It is assumed to
  137. * be interface {@link Attribute Attribute} or a
  138. * subinterface thereof.
  139. *
  140. * @exception NullPointerException
  141. * (unchecked exception) Thrown if <CODE>attribute</CODE> is null.
  142. * @exception NullPointerException if interfaceName is null.
  143. * @exception ClassCastException
  144. * (unchecked exception) Thrown if <CODE>attribute</CODE> is not an
  145. * instance of <CODE>interfaceName</CODE>.
  146. */
  147. protected HashAttributeSet(Attribute attribute, Class<?> interfaceName) {
  148. if (interfaceName == null) {
  149. throw new NullPointerException("null interface");
  150. }
  151. myInterface = interfaceName;
  152. add (attribute);
  153. }
  154. /**
  155. * Construct a new attribute set, where the members of the attribute
  156. * set are restricted to the given interface.
  157. * The new attribute set is populated
  158. * by adding the elements of <CODE>attributes</CODE> array to the set in
  159. * sequence, starting at index 0. Thus, later array elements may replace
  160. * earlier array elements if the array contains duplicate attribute
  161. * values or attribute categories.
  162. *
  163. * @param attributes Array of attribute values to add to the set. If
  164. * null, an empty attribute set is constructed.
  165. * @param interfaceName The interface of which all members of this
  166. * attribute set must be an instance. It is assumed to
  167. * be interface {@link Attribute Attribute} or a
  168. * subinterface thereof.
  169. *
  170. * @exception NullPointerException
  171. * (unchecked exception) Thrown if any element of
  172. * <CODE>attributes</CODE> is null.
  173. * @exception NullPointerException if interfaceName is null.
  174. * @exception ClassCastException
  175. * (unchecked exception) Thrown if any element of
  176. * <CODE>attributes</CODE> is not an instance of
  177. * <CODE>interfaceName</CODE>.
  178. */
  179. protected HashAttributeSet(Attribute[] attributes, Class<?> interfaceName) {
  180. if (interfaceName == null) {
  181. throw new NullPointerException("null interface");
  182. }
  183. myInterface = interfaceName;
  184. int n = attributes == null ? 0 : attributes.length;
  185. for (int i = 0; i < n; ++ i) {
  186. add (attributes[i]);
  187. }
  188. }
  189. /**
  190. * Construct a new attribute set, initially populated with the
  191. * values from the given set where the members of the attribute
  192. * set are restricted to the given interface.
  193. *
  194. * @param attributes set of attribute values to initialise the set. If
  195. * null, an empty attribute set is constructed.
  196. * @param interfaceName The interface of which all members of this
  197. * attribute set must be an instance. It is assumed to
  198. * be interface {@link Attribute Attribute} or a
  199. * subinterface thereof.
  200. *
  201. * @exception ClassCastException
  202. * (unchecked exception) Thrown if any element of
  203. * <CODE>attributes</CODE> is not an instance of
  204. * <CODE>interfaceName</CODE>.
  205. */
  206. protected HashAttributeSet(AttributeSet attributes, Class<?> interfaceName) {
  207. myInterface = interfaceName;
  208. if (attributes != null) {
  209. Attribute[] attribArray = attributes.toArray();
  210. int n = attribArray == null ? 0 : attribArray.length;
  211. for (int i = 0; i < n; ++ i) {
  212. add (attribArray[i]);
  213. }
  214. }
  215. }
  216. /**
  217. * Returns the attribute value which this attribute set contains in the
  218. * given attribute category. Returns <tt>null</tt> if this attribute set
  219. * does not contain any attribute value in the given attribute category.
  220. *
  221. * @param category Attribute category whose associated attribute value
  222. * is to be returned. It must be a
  223. * {@link java.lang.Class Class}
  224. * that implements interface {@link Attribute
  225. * Attribute}.
  226. *
  227. * @return The attribute value in the given attribute category contained
  228. * in this attribute set, or <tt>null</tt> if this attribute set
  229. * does not contain any attribute value in the given attribute
  230. * category.
  231. *
  232. * @throws NullPointerException
  233. * (unchecked exception) Thrown if the <CODE>category</CODE> is null.
  234. * @throws ClassCastException
  235. * (unchecked exception) Thrown if the <CODE>category</CODE> is not a
  236. * {@link java.lang.Class Class} that implements interface {@link
  237. * Attribute Attribute}.
  238. */
  239. public Attribute get(Class<?> category) {
  240. return (Attribute)
  241. attrMap.get(AttributeSetUtilities.
  242. verifyAttributeCategory(category,
  243. Attribute.class));
  244. }
  245. /**
  246. * Adds the specified attribute to this attribute set if it is not
  247. * already present, first removing any existing in the same
  248. * attribute category as the specified attribute value.
  249. *
  250. * @param attribute Attribute value to be added to this attribute set.
  251. *
  252. * @return <tt>true</tt> if this attribute set changed as a result of the
  253. * call, i.e., the given attribute value was not already a
  254. * member of this attribute set.
  255. *
  256. * @throws NullPointerException
  257. * (unchecked exception) Thrown if the <CODE>attribute</CODE> is null.
  258. * @throws UnmodifiableSetException
  259. * (unchecked exception) Thrown if this attribute set does not support
  260. * the <CODE>add()</CODE> operation.
  261. */
  262. public boolean add(Attribute attribute) {
  263. Object oldAttribute =
  264. attrMap.put(attribute.getCategory(),
  265. AttributeSetUtilities.
  266. verifyAttributeValue(attribute, myInterface));
  267. return (!attribute.equals(oldAttribute));
  268. }
  269. /**
  270. * Removes any attribute for this category from this attribute set if
  271. * present. If <CODE>category</CODE> is null, then
  272. * <CODE>remove()</CODE> does nothing and returns <tt>false</tt>.
  273. *
  274. * @param category Attribute category to be removed from this
  275. * attribute set.
  276. *
  277. * @return <tt>true</tt> if this attribute set changed as a result of the
  278. * call, i.e., the given attribute category had been a member of
  279. * this attribute set.
  280. *
  281. * @throws UnmodifiableSetException
  282. * (unchecked exception) Thrown if this attribute set does not
  283. * support the <CODE>remove()</CODE> operation.
  284. */
  285. public boolean remove(Class<?> category) {
  286. return
  287. category != null &&
  288. AttributeSetUtilities.
  289. verifyAttributeCategory(category, Attribute.class) != null &&
  290. attrMap.remove(category) != null;
  291. }
  292. /**
  293. * Removes the specified attribute from this attribute set if
  294. * present. If <CODE>attribute</CODE> is null, then
  295. * <CODE>remove()</CODE> does nothing and returns <tt>false</tt>.
  296. *
  297. * @param attribute Attribute value to be removed from this attribute set.
  298. *
  299. * @return <tt>true</tt> if this attribute set changed as a result of the
  300. * call, i.e., the given attribute value had been a member of
  301. * this attribute set.
  302. *
  303. * @throws UnmodifiableSetException
  304. * (unchecked exception) Thrown if this attribute set does not
  305. * support the <CODE>remove()</CODE> operation.
  306. */
  307. public boolean remove(Attribute attribute) {
  308. return
  309. attribute != null &&
  310. attrMap.remove(attribute.getCategory()) != null;
  311. }
  312. /**
  313. * Returns <tt>true</tt> if this attribute set contains an
  314. * attribute for the specified category.
  315. *
  316. * @param category whose presence in this attribute set is
  317. * to be tested.
  318. *
  319. * @return <tt>true</tt> if this attribute set contains an attribute
  320. * value for the specified category.
  321. */
  322. public boolean containsKey(Class<?> category) {
  323. return
  324. category != null &&
  325. AttributeSetUtilities.
  326. verifyAttributeCategory(category, Attribute.class) != null &&
  327. attrMap.get(category) != null;
  328. }
  329. /**
  330. * Returns <tt>true</tt> if this attribute set contains the given
  331. * attribute.
  332. *
  333. * @param attribute value whose presence in this attribute set is
  334. * to be tested.
  335. *
  336. * @return <tt>true</tt> if this attribute set contains the given
  337. * attribute value.
  338. */
  339. public boolean containsValue(Attribute attribute) {
  340. return
  341. attribute != null &&
  342. attribute instanceof Attribute &&
  343. attribute.equals(attrMap.get(((Attribute)attribute).getCategory()));
  344. }
  345. /**
  346. * Adds all of the elements in the specified set to this attribute.
  347. * The outcome is the same as if the
  348. * {@link #add(Attribute) <CODE>add(Attribute)</CODE>}
  349. * operation had been applied to this attribute set successively with
  350. * each element from the specified set.
  351. * The behavior of the <CODE>addAll(AttributeSet)</CODE>
  352. * operation is unspecified if the specified set is modified while
  353. * the operation is in progress.
  354. * <P>
  355. * If the <CODE>addAll(AttributeSet)</CODE> operation throws an exception,
  356. * the effect on this attribute set's state is implementation dependent;
  357. * elements from the specified set before the point of the exception may
  358. * or may not have been added to this attribute set.
  359. *
  360. * @param attributes whose elements are to be added to this attribute
  361. * set.
  362. *
  363. * @return <tt>true</tt> if this attribute set changed as a result of the
  364. * call.
  365. *
  366. * @throws UnmodifiableSetException
  367. * (Unchecked exception) Thrown if this attribute set does not
  368. * support the <tt>addAll(AttributeSet)</tt> method.
  369. * @throws NullPointerException
  370. * (Unchecked exception) Thrown if some element in the specified
  371. * set is null, or the set is null.
  372. *
  373. * @see #add(Attribute)
  374. */
  375. public boolean addAll(AttributeSet attributes) {
  376. Attribute []attrs = attributes.toArray();
  377. boolean result = false;
  378. for (int i=0; i<attrs.length; i++) {
  379. Attribute newValue =
  380. AttributeSetUtilities.verifyAttributeValue(attrs[i],
  381. myInterface);
  382. Object oldValue = attrMap.put(newValue.getCategory(), newValue);
  383. result = (! newValue.equals(oldValue)) || result;
  384. }
  385. return result;
  386. }
  387. /**
  388. * Returns the number of attributes in this attribute set. If this
  389. * attribute set contains more than <tt>Integer.MAX_VALUE</tt> elements,
  390. * returns <tt>Integer.MAX_VALUE</tt>.
  391. *
  392. * @return The number of attributes in this attribute set.
  393. */
  394. public int size() {
  395. return attrMap.size();
  396. }
  397. /**
  398. *
  399. * @return the Attributes contained in this set as an array, zero length
  400. * if the AttributeSet is empty.
  401. */
  402. public Attribute[] toArray() {
  403. Attribute []attrs = new Attribute[size()];
  404. attrMap.values().toArray(attrs);
  405. return attrs;
  406. }
  407. /**
  408. * Removes all attributes from this attribute set.
  409. *
  410. * @throws UnmodifiableSetException
  411. * (unchecked exception) Thrown if this attribute set does not support
  412. * the <CODE>clear()</CODE> operation.
  413. */
  414. public void clear() {
  415. attrMap.clear();
  416. }
  417. /**
  418. * Returns true if this attribute set contains no attributes.
  419. *
  420. * @return true if this attribute set contains no attributes.
  421. */
  422. public boolean isEmpty() {
  423. return attrMap.isEmpty();
  424. }
  425. /**
  426. * Compares the specified object with this attribute set for equality.
  427. * Returns <tt>true</tt> if the given object is also an attribute set and
  428. * the two attribute sets contain the same attribute category-attribute
  429. * value mappings. This ensures that the
  430. * <tt>equals()</tt> method works properly across different
  431. * implementations of the AttributeSet interface.
  432. *
  433. * @param object to be compared for equality with this attribute set.
  434. *
  435. * @return <tt>true</tt> if the specified object is equal to this
  436. * attribute set.
  437. */
  438. public boolean equals(Object object) {
  439. if (object == null || !(object instanceof AttributeSet)) {
  440. return false;
  441. }
  442. AttributeSet aset = (AttributeSet)object;
  443. if (aset.size() != size()) {
  444. return false;
  445. }
  446. Attribute[] attrs = toArray();
  447. for (int i=0;i<attrs.length; i++) {
  448. if (!aset.containsValue(attrs[i])) {
  449. return false;
  450. }
  451. }
  452. return true;
  453. }
  454. /**
  455. * Returns the hash code value for this attribute set.
  456. * The hash code of an attribute set is defined to be the sum
  457. * of the hash codes of each entry in the AttributeSet.
  458. * This ensures that <tt>t1.equals(t2)</tt> implies that
  459. * <tt>t1.hashCode()==t2.hashCode()</tt> for any two attribute sets
  460. * <tt>t1</tt> and <tt>t2</tt>, as required by the general contract of
  461. * {@link java.lang.Object#hashCode() <CODE>Object.hashCode()</CODE>}.
  462. *
  463. * @return The hash code value for this attribute set.
  464. */
  465. public int hashCode() {
  466. int hcode = 0;
  467. Attribute[] attrs = toArray();
  468. for (int i=0;i<attrs.length; i++) {
  469. hcode += attrs[i].hashCode();
  470. }
  471. return hcode;
  472. }
  473. }