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