1. /*
  2. * @(#)TabularDataSupport.java 3.30 03/12/19
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.management.openmbean;
  8. // java import
  9. //
  10. import java.io.IOException;
  11. import java.io.Serializable;
  12. import java.io.ObjectInputStream;
  13. import java.util.Iterator;
  14. import java.util.HashMap;
  15. import java.util.Map;
  16. import java.util.Set;
  17. import java.util.Collection;
  18. import java.util.Collections;
  19. import java.util.Arrays;
  20. import java.util.ArrayList;
  21. import java.util.List;
  22. // jmx import
  23. //
  24. /**
  25. * The <tt>TabularDataSupport</tt> class is the <i>open data</i> class which implements the <tt>TabularData</tt>
  26. * and the <tt>Map</tt> interfaces, and which is internally based on a hash map data structure.
  27. *
  28. * @version 3.30 03/12/19
  29. * @author Sun Microsystems, Inc.
  30. *
  31. * @since 1.5
  32. * @since.unbundled JMX 1.1
  33. */
  34. public class TabularDataSupport
  35. implements TabularData, Map, Cloneable, Serializable {
  36. /* Serial version */
  37. static final long serialVersionUID = 5720150593236309827L;
  38. /**
  39. * @serial This tabular data instance's contents: a {@link HashMap}
  40. */
  41. private Map dataMap;
  42. /**
  43. * @serial This tabular data instance's tabular type
  44. */
  45. private TabularType tabularType;
  46. /**
  47. * @serial The array of item names that define the index used for rows (convenience field)
  48. */
  49. private transient String[] indexNamesArray;
  50. /* *** Constructors *** */
  51. /**
  52. * Creates an empty <tt>TabularDataSupport</tt> instance whose open-type is <var>tabularType</var>,
  53. * and whose underlying <tt>HashMap</tt> has a default initial capacity (101) and default load factor (0.75).
  54. * <p>
  55. * This constructor simply calls <tt>this(tabularType, 101, 0.75f);</tt>
  56. *
  57. * @param tabularType the <i>tabular type</i> describing this <tt>TabularData</tt> instance;
  58. * cannot be null.
  59. *
  60. * @throws IllegalArgumentException if the tabular type is null.
  61. */
  62. public TabularDataSupport(TabularType tabularType) {
  63. this(tabularType, 101, 0.75f);
  64. }
  65. /**
  66. * Creates an empty <tt>TabularDataSupport</tt> instance whose open-type is <var>tabularType</var>,
  67. * and whose underlying <tt>HashMap</tt> has the specified initial capacity and load factor.
  68. *
  69. * @param tabularType the <i>tabular type</i> describing this <tt>TabularData</tt> instance;
  70. * cannot be null.
  71. *
  72. * @param initialCapacity the initial capacity of the HashMap.
  73. *
  74. * @param loadFactor the load factor of the HashMap
  75. *
  76. * @throws IllegalArgumentException if the initial capacity is less than zero,
  77. * or the load factor is nonpositive,
  78. * or the tabular type is null.
  79. */
  80. public TabularDataSupport(TabularType tabularType, int initialCapacity, float loadFactor) {
  81. // Check tabularType is not null
  82. //
  83. if (tabularType == null) {
  84. throw new IllegalArgumentException("Argument tabularType cannot be null.");
  85. }
  86. // Initialize this.tabularType (and indexNamesArray for convenience)
  87. //
  88. this.tabularType = tabularType;
  89. List tmpNames = tabularType.getIndexNames();
  90. this.indexNamesArray = (String[]) tmpNames.toArray(new String[tmpNames.size()]);
  91. // Construct the empty contents HashMap
  92. //
  93. this.dataMap = new HashMap(initialCapacity, loadFactor);
  94. }
  95. /* *** TabularData specific information methods *** */
  96. /**
  97. * Returns the <i>tabular type</i> describing this <tt>TabularData</tt> instance.
  98. */
  99. public TabularType getTabularType() {
  100. return tabularType;
  101. }
  102. /**
  103. * Calculates the index that would be used in this <tt>TabularData</tt> instance to refer to the specified
  104. * composite data <var>value</var> parameter if it were added to this instance.
  105. * This method checks for the type validity of the specified <var>value</var>,
  106. * but does not check if the calculated index is already used to refer to a value in this <tt>TabularData</tt> instance.
  107. *
  108. * @param value the composite data value whose index in this
  109. * <tt>TabularData</tt> instance is to be calculated;
  110. * must be of the same composite type as this instance's row type;
  111. * must not be null.
  112. *
  113. * @return the index that the specified <var>value</var> would have in this <tt>TabularData</tt> instance.
  114. *
  115. * @throws NullPointerException if <var>value</var> is <tt>null</tt>.
  116. *
  117. * @throws InvalidOpenTypeException if <var>value</var> does not conform to this <tt>TabularData</tt> instance's
  118. * row type definition.
  119. */
  120. public Object[] calculateIndex(CompositeData value) {
  121. // Check value is valid
  122. //
  123. checkValueType(value);
  124. // Return its calculated index
  125. //
  126. return internalCalculateIndex(value).toArray();
  127. }
  128. /* *** Content information query methods *** */
  129. /**
  130. * Returns <tt>true</tt> if and only if this <tt>TabularData</tt> instance contains a <tt>CompositeData</tt> value
  131. * (ie a row) whose index is the specified <var>key</var>. If <var>key</var> cannot be cast to a one dimension array
  132. * of Object instances, this method simply returns <tt>false</tt> otherwise it returns the the result of the call to
  133. * <tt>this.containsKey((Object[]) key)</tt>.
  134. *
  135. * @param key the index value whose presence in this <tt>TabularData</tt> instance is to be tested.
  136. *
  137. * @return <tt>true</tt> if this <tt>TabularData</tt> indexes a row value with the specified key.
  138. */
  139. public boolean containsKey(Object key) {
  140. // if key is not an array of Object instances, return false
  141. //
  142. Object[] k;
  143. try {
  144. k = (Object[]) key;
  145. } catch (ClassCastException e) {
  146. return false;
  147. }
  148. return this.containsKey(k);
  149. }
  150. /**
  151. * Returns <tt>true</tt> if and only if this <tt>TabularData</tt> instance contains a <tt>CompositeData</tt> value
  152. * (ie a row) whose index is the specified <var>key</var>. If <var>key</var> is <tt>null</tt> or does not conform to
  153. * this <tt>TabularData</tt> instance's <tt>TabularType</tt> definition, this method simply returns <tt>false</tt>.
  154. *
  155. * @param key the index value whose presence in this <tt>TabularData</tt> instance is to be tested.
  156. *
  157. * @return <tt>true</tt> if this <tt>TabularData</tt> indexes a row value with the specified key.
  158. */
  159. public boolean containsKey(Object[] key) {
  160. return ( key == null ? false : dataMap.containsKey(Arrays.asList(key)) );
  161. }
  162. /**
  163. * Returns <tt>true</tt> if and only if this <tt>TabularData</tt> instance contains the specified
  164. * <tt>CompositeData</tt> value. If <var>value</var> is <tt>null</tt> or does not conform to
  165. * this <tt>TabularData</tt> instance's row type definition, this method simply returns <tt>false</tt>.
  166. *
  167. * @param value the row value whose presence in this <tt>TabularData</tt> instance is to be tested.
  168. *
  169. * @return <tt>true</tt> if this <tt>TabularData</tt> instance contains the specified row value.
  170. */
  171. public boolean containsValue(CompositeData value) {
  172. return dataMap.containsValue(value);
  173. }
  174. /**
  175. * Returns <tt>true</tt> if and only if this <tt>TabularData</tt> instance contains the specified
  176. * value.
  177. *
  178. * @param value the row value whose presence in this <tt>TabularData</tt> instance is to be tested.
  179. *
  180. * @return <tt>true</tt> if this <tt>TabularData</tt> instance contains the specified row value.
  181. */
  182. public boolean containsValue(Object value) {
  183. return dataMap.containsValue(value);
  184. }
  185. /**
  186. * This method simply calls <tt>get((Object[]) key)</tt>.
  187. *
  188. * @throws NullPointerException if the <var>key</var> is <tt>null</tt>
  189. * @throws ClassCastException if the <var>key</var> is not of the type <tt>Object[]</tt>
  190. * @throws InvalidKeyException if the <var>key</var> does not conform to this <tt>TabularData</tt> instance's
  191. * <tt>TabularType</tt> definition
  192. */
  193. public Object get(Object key) {
  194. return get((Object[]) key);
  195. }
  196. /**
  197. * Returns the <tt>CompositeData</tt> value whose index is
  198. * <var>key</var>, or <tt>null</tt> if there is no value mapping
  199. * to <var>key</var>, in this <tt>TabularData</tt> instance.
  200. *
  201. * @param key the index of the value to get in this
  202. * <tt>TabularData</tt> instance; * must be valid with this
  203. * <tt>TabularData</tt> instance's row type definition; * must not
  204. * be null.
  205. *
  206. * @return the value corresponding to <var>key</var>.
  207. *
  208. * @throws NullPointerException if the <var>key</var> is <tt>null</tt>
  209. * @throws InvalidKeyException if the <var>key</var> does not conform to this <tt>TabularData</tt> instance's
  210. * <tt>TabularType</tt> type definition.
  211. */
  212. public CompositeData get(Object[] key) {
  213. // Check key is not null and valid with tabularType
  214. // (throws NullPointerException, InvalidKeyException)
  215. //
  216. checkKeyType(key);
  217. // Return the mapping stored in the parent HashMap
  218. //
  219. return (CompositeData) dataMap.get(Arrays.asList(key));
  220. }
  221. /* *** Content modification operations (one element at a time) *** */
  222. /**
  223. * This method simply calls <tt>put((CompositeData) value)</tt> and
  224. * therefore ignores its <var>key</var> parameter which can be <tt>null</tt>.
  225. *
  226. * @param key an ignored parameter.
  227. * @param value the {@link CompositeData} to put.
  228. *
  229. * @return the value which is put
  230. *
  231. * @throws NullPointerException if the <var>value</var> is <tt>null</tt>
  232. * @throws ClassCastException if the <var>value</var> is not of the type <tt>CompositeData</tt>
  233. * @throws InvalidOpenTypeException if the <var>value</var> does not conform to this <tt>TabularData</tt> instance's
  234. * <tt>TabularType</tt> definition
  235. * @throws KeyAlreadyExistsException if the key for the <var>value</var> parameter, calculated according to
  236. * this <tt>TabularData</tt> instance's <tt>TabularType</tt> definition
  237. * already maps to an existing value
  238. */
  239. public Object put(Object key, Object value) {
  240. put((CompositeData) value);
  241. return value;
  242. }
  243. public void put(CompositeData value) {
  244. // Check value is not null, value's type is the same as this instance's row type,
  245. // and calculate the value's index according to this instance's tabularType and
  246. // check it is not already used for a mapping in the parent HashMap
  247. //
  248. List index = checkValueAndIndex(value);
  249. // store the (key, value) mapping in the dataMap HashMap
  250. //
  251. dataMap.put(index, value);
  252. }
  253. /**
  254. * This method simply calls <tt>remove((Object[]) key)</tt>.
  255. *
  256. * @param key an <tt>Object[]</tt> representing the key to remove.
  257. *
  258. * @return previous value associated with specified key, or <tt>null</tt>
  259. * if there was no mapping for key.
  260. *
  261. * @throws NullPointerException if the <var>key</var> is <tt>null</tt>
  262. * @throws ClassCastException if the <var>key</var> is not of the type <tt>Object[]</tt>
  263. * @throws InvalidKeyException if the <var>key</var> does not conform to this <tt>TabularData</tt> instance's
  264. * <tt>TabularType</tt> definition
  265. */
  266. public Object remove(Object key) {
  267. return remove((Object[]) key);
  268. }
  269. /**
  270. * Removes the <tt>CompositeData</tt> value whose index is <var>key</var> from this <tt>TabularData</tt> instance,
  271. * and returns the removed value, or returns <tt>null</tt> if there is no value whose index is <var>key</var>.
  272. *
  273. * @param key the index of the value to get in this <tt>TabularData</tt> instance;
  274. * must be valid with this <tt>TabularData</tt> instance's row type definition;
  275. * must not be null.
  276. *
  277. * @return previous value associated with specified key, or <tt>null</tt>
  278. * if there was no mapping for key.
  279. *
  280. * @throws NullPointerException if the <var>key</var> is <tt>null</tt>
  281. * @throws InvalidKeyException if the <var>key</var> does not conform to this <tt>TabularData</tt> instance's
  282. * <tt>TabularType</tt> definition
  283. */
  284. public CompositeData remove(Object[] key) {
  285. // Check key is not null and valid with tabularType
  286. // (throws NullPointerException, InvalidKeyException)
  287. //
  288. checkKeyType(key);
  289. // Removes the (key, value) mapping in the parent HashMap
  290. //
  291. return (CompositeData) dataMap.remove(Arrays.asList(key));
  292. }
  293. /* *** Content modification bulk operations *** */
  294. /**
  295. * Add all the values contained in the specified map <var>t</var> to this <tt>TabularData</tt> instance.
  296. * This method converts the collection of values contained in this map into an array of <tt>CompositeData</tt> values,
  297. * if possible, and then call the method <tt>putAll(CompositeData[])</tt>. Note that the keys used in the specified
  298. * map <var>t</var> are ignored. This method allows, for example to add the content of another <tt>TabularData</tt>
  299. * instance with the same row type (but possibly different index names) into this instance.
  300. *
  301. * @param t the map whose values are to be added as new rows to this <tt>TabularData</tt> instance;
  302. * if <var>t</var> is <tt>null</tt> or empty, this method returns without doing anything.
  303. *
  304. * @throws NullPointerException if a value in <var>t</var> is <tt>null</tt>.
  305. * @throws ClassCastException if a value in <var>t</var> is not an instance of <tt>CompositeData</tt>.
  306. * @throws InvalidOpenTypeException if a value in <var>t</var> does not conform to
  307. * this <tt>TabularData</tt> instance's row type definition.
  308. * @throws KeyAlreadyExistsException if the index for a value in <var>t</var>, calculated according to
  309. * this <tt>TabularData</tt> instance's <tt>TabularType</tt> definition
  310. * already maps to an existing value in this instance,
  311. * or two values in <var>t</var> have the same index.
  312. */
  313. public void putAll(Map t) {
  314. // if t is null or empty, just return
  315. //
  316. if ( (t == null) || (t.size() == 0) ) {
  317. return;
  318. }
  319. // Convert the values in t into an array of <tt>CompositeData</tt>
  320. //
  321. CompositeData[] values;
  322. try {
  323. values = (CompositeData[]) t.values().toArray(new CompositeData[t.size()]);
  324. } catch (java.lang.ArrayStoreException e) {
  325. throw new ClassCastException("Map argument t contains values which are not instances of <tt>CompositeData</tt>");
  326. }
  327. // Add the array of values
  328. //
  329. putAll(values);
  330. }
  331. /**
  332. * Add all the elements in <var>values</var> to this <tt>TabularData</tt> instance.
  333. * If any element in <var>values</var> does not satisfy the constraints defined in {@link #put(CompositeData) <tt>put</tt>},
  334. * or if any two elements in <var>values</var> have the same index calculated according to this <tt>TabularData</tt>
  335. * instance's <tt>TabularType</tt> definition, then an exception describing the failure is thrown
  336. * and no element of <var>values</var> is added, thus leaving this <tt>TabularData</tt> instance unchanged.
  337. *
  338. * @param values the array of composite data values to be added as new rows to this <tt>TabularData</tt> instance;
  339. * if <var>values</var> is <tt>null</tt> or empty, this method returns without doing anything.
  340. *
  341. * @throws NullPointerException if an element of <var>values</var> is <tt>null</tt>
  342. * @throws InvalidOpenTypeException if an element of <var>values</var> does not conform to
  343. * this <tt>TabularData</tt> instance's row type definition
  344. * (ie its <tt>TabularType</tt> definition)
  345. * @throws KeyAlreadyExistsException if the index for an element of <var>values</var>, calculated according to
  346. * this <tt>TabularData</tt> instance's <tt>TabularType</tt> definition
  347. * already maps to an existing value in this instance,
  348. * or two elements of <var>values</var> have the same index
  349. */
  350. public void putAll(CompositeData[] values) {
  351. // if values is null or empty, just return
  352. //
  353. if ( (values == null) || (values.length == 0) ) {
  354. return;
  355. }
  356. // create the list of indexes corresponding to each value
  357. ArrayList indexes = new ArrayList(values.length + 1);
  358. // Check all elements in values and build index list
  359. //
  360. List index;
  361. for (int i=0; i<values.length; i++) {
  362. // check value and calculate index
  363. index = checkValueAndIndex(values[i]);
  364. // check index is different of those previously calculated
  365. if (indexes.contains(index)) {
  366. throw new KeyAlreadyExistsException("Argument elements values["+ i +"] and values["+ indexes.indexOf(index) +
  367. "] have the same indexes, "+
  368. "calculated according to this TabularData instance's tabularType.");
  369. }
  370. // add to index list
  371. indexes.add(index);
  372. }
  373. // store all (index, value) mappings in the dataMap HashMap
  374. //
  375. for (int i=0; i<values.length; i++) {
  376. dataMap.put(indexes.get(i), values[i]);
  377. }
  378. }
  379. /**
  380. * Removes all rows from this <code>TabularDataSupport</code> instance.
  381. */
  382. public void clear() {
  383. dataMap.clear();
  384. }
  385. /* *** Informational methods from java.util.Map *** */
  386. /**
  387. * Returns the number of rows in this <code>TabularDataSupport</code> instance.
  388. *
  389. * @return the number of rows in this <code>TabularDataSupport</code> instance.
  390. */
  391. public int size() {
  392. return dataMap.size();
  393. }
  394. /**
  395. * Returns <tt>true</tt> if this <code>TabularDataSupport</code> instance contains no rows.
  396. *
  397. * @return <tt>true</tt> if this <code>TabularDataSupport</code> instance contains no rows.
  398. */
  399. public boolean isEmpty() {
  400. return (this.size() == 0);
  401. }
  402. /* *** Collection views from java.util.Map *** */
  403. /**
  404. * Returns a set view of the keys contained in the underlying map of this <code>TabularDataSupport</code> instance,
  405. * and used to index the rows. Each key contained in this set is an unmodifiable List.
  406. * The set is backed by the underlying map of this <code>TabularDataSupport</code> instance,
  407. * so changes to the <code>TabularDataSupport</code> instance are reflected in the set, and vice-versa.
  408. *
  409. * The set supports element removal, which removes the
  410. * corresponding row from this <code>TabularDataSupport</code> instance, via the <tt>Iterator.remove</tt>,
  411. * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt>, and
  412. * <tt>clear</tt> operations.
  413. * It does not support the <tt>add</tt> or <tt>addAll</tt> operations
  414. *
  415. * @return a set view of the keys used to index the rows of this <code>TabularDataSupport</code> instance.
  416. */
  417. public Set keySet() {
  418. return dataMap.keySet() ;
  419. }
  420. /**
  421. * Returns a collection view of the rows contained in this <code>TabularDataSupport</code> instance.
  422. * The collection is backed by the underlying map, so changes to the <code>TabularDataSupport</code> instance
  423. * are reflected in the collection, and vice-versa.
  424. *
  425. * The collection supports element removal,
  426. * which removes the corresponding index to row mapping from this <code>TabularDataSupport</code> instance,
  427. * via the <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
  428. * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations.
  429. * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.
  430. *
  431. * @return a collection view of the values contained in this <code>TabularDataSupport</code> instance.
  432. */
  433. public Collection values() {
  434. return dataMap.values() ;
  435. }
  436. /**
  437. * Returns a collection view of the index to row mappings contained in this <code>TabularDataSupport</code> instance.
  438. * Each element in the returned collection is a <tt>Map.Entry</tt>.
  439. * The collection is backed by the underlying map of this <code>TabularDataSupport</code> instance, in
  440. * so changes to the <code>TabularDataSupport</code> instance are reflected the collection, and vice-versa.
  441. * The collection supports element removal, which removes the corresponding mapping from the map, via the
  442. * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
  443. * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations.
  444. * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.
  445. * <p>
  446. * <b>IMPORTANT NOTICE</b>: Do not use the <tt>SetValue</tt> method of <tt>Map.Entry</tt> elements contained in the returned
  447. * collection view. Doing so would corrupt the index to row mappings contained in this <code>TabularDataSupport</code> instance.
  448. * <p>
  449. *
  450. * @return a collection view of the mappings contained in this map.
  451. * @see java.util.Map.Entry
  452. */
  453. public Set entrySet() {
  454. return dataMap.entrySet();
  455. }
  456. /* *** Commodity methods from java.lang.Object *** */
  457. /**
  458. * Returns a clone of this <code>TabularDataSupport</code> instance:
  459. * the clone is obtained by calling <tt>super.clone()</tt>, and then cloning the underlying map.
  460. * Only a shallow clone of the underlying map is made, i.e. no cloning of the indexes and row values is made as they are immutable.
  461. */
  462. public Object clone() {
  463. try {
  464. TabularDataSupport c = (TabularDataSupport) super.clone();
  465. c.dataMap = (HashMap) ((HashMap) c.dataMap).clone();
  466. return c;
  467. }
  468. catch (CloneNotSupportedException e) {
  469. throw new InternalError(e.toString());
  470. }
  471. }
  472. /**
  473. * Compares the specified <var>obj</var> parameter with this <code>TabularDataSupport</code> instance for equality.
  474. * <p>
  475. * Returns <tt>true</tt> if and only if all of the following statements are true:
  476. * <ul>
  477. * <li><var>obj</var> is non null,</li>
  478. * <li><var>obj</var> also implements the <code>TabularData</code> interface,</li>
  479. * <li>their tabular types are equal</li>
  480. * <li>their contents (ie all CompositeData values) are equal.</li>
  481. * </ul>
  482. * This ensures that this <tt>equals</tt> method works properly for <var>obj</var> parameters which are
  483. * different implementations of the <code>TabularData</code> interface.
  484. * <br> 
  485. * @param obj the object to be compared for equality with this <code>TabularDataSupport</code> instance;
  486. *
  487. * @return <code>true</code> if the specified object is equal to this <code>TabularDataSupport</code> instance.
  488. */
  489. public boolean equals(Object obj) {
  490. // if obj is null, return false
  491. //
  492. if (obj == null) {
  493. return false;
  494. }
  495. // if obj is not a TabularData, return false
  496. //
  497. TabularData other;
  498. try {
  499. other = (TabularData) obj;
  500. } catch (ClassCastException e) {
  501. return false;
  502. }
  503. // Now, really test for equality between this TabularData implementation and the other:
  504. //
  505. // their tabularType should be equal
  506. if ( ! this.getTabularType().equals(other.getTabularType()) ) {
  507. return false;
  508. }
  509. // their contents should be equal:
  510. // . same size
  511. // . values in this instance are in the other (we know there are no duplicate elements possible)
  512. // (row values comparison is enough, because keys are calculated according to tabularType)
  513. if (this.size() != other.size()) {
  514. return false;
  515. }
  516. for (Iterator iter = this.values().iterator(); iter.hasNext(); ) {
  517. CompositeData value = (CompositeData) iter.next();
  518. if ( ! other.containsValue(value) ) {
  519. return false;
  520. }
  521. }
  522. // All tests for equality were successfull
  523. //
  524. return true;
  525. }
  526. /**
  527. * Returns the hash code value for this <code>TabularDataSupport</code> instance.
  528. * <p>
  529. * The hash code of a <code>TabularDataSupport</code> instance is the sum of the hash codes
  530. * of all elements of information used in <code>equals</code> comparisons
  531. * (ie: its <i>tabular type</i> and its content, where the content is defined as all the CompositeData values).
  532. * <p>
  533. * This ensures that <code> t1.equals(t2) </code> implies that <code> t1.hashCode()==t2.hashCode() </code>
  534. * for any two <code>TabularDataSupport</code> instances <code>t1</code> and <code>t2</code>,
  535. * as required by the general contract of the method
  536. * {@link Object#hashCode() Object.hashCode()}.
  537. * <p>
  538. * However, note that another instance of a class implementing the <code>TabularData</code> interface
  539. * may be equal to this <code>TabularDataSupport</code> instance as defined by {@link #equals},
  540. * but may have a different hash code if it is calculated differently.
  541. *
  542. * @return the hash code value for this <code>TabularDataSupport</code> instance
  543. */
  544. public int hashCode() {
  545. int result = 0;
  546. result += this.tabularType.hashCode();
  547. for (Iterator iter = this.values().iterator(); iter.hasNext(); ) {
  548. result += ((CompositeData)iter.next()).hashCode();
  549. }
  550. return result;
  551. }
  552. /**
  553. * Returns a string representation of this <code>TabularDataSupport</code> instance.
  554. * <p>
  555. * The string representation consists of the name of this class (ie <code>javax.management.openmbean.TabularDataSupport</code>),
  556. * the string representation of the tabular type of this instance, and the string representation of the contents
  557. * (ie list the key=value mappings as returned by a call to
  558. * <tt>dataMap.</tt>{@link java.util.HashMap#toString() toString()}).
  559. *
  560. * @return a string representation of this <code>TabularDataSupport</code> instance
  561. */
  562. public String toString() {
  563. return new StringBuffer()
  564. .append(this.getClass().getName())
  565. .append("(tabularType=")
  566. .append(tabularType.toString())
  567. .append(",contents=")
  568. .append(dataMap.toString())
  569. .append(")")
  570. .toString();
  571. }
  572. /* *** TabularDataSupport internal utility methods *** */
  573. /**
  574. * Returns the index for value, assuming value is valid for this <tt>TabularData</tt> instance
  575. * (ie value is not null, and its composite type is equal to row type).
  576. *
  577. * The index is a List, and not an array, so that an index.equals(otherIndex) call will actually compare contents,
  578. * not just the objects references as is done for an array object.
  579. *
  580. * The returned List is unmodifiable so that once a row has been put into the dataMap, its index cannot be modified,
  581. * for example by a user that would attempt to modify an index contained in the Set returned by keySet().
  582. */
  583. private List internalCalculateIndex(CompositeData value) {
  584. return Collections.unmodifiableList(Arrays.asList(value.getAll(this.indexNamesArray)));
  585. }
  586. /**
  587. * Checks if the specified key is valid for this <tt>TabularData</tt> instance.
  588. *
  589. * @throws NullPointerException
  590. * @throws InvalidOpenTypeException
  591. */
  592. private void checkKeyType(Object[] key) {
  593. // Check key is neither null nor empty
  594. //
  595. if ( (key == null) || (key.length == 0) ) {
  596. throw new NullPointerException("Argument key cannot be null or empty.");
  597. }
  598. /* Now check key is valid with tabularType index and row type definitions: */
  599. // key[] should have the size expected for an index
  600. //
  601. if (key.length != this.indexNamesArray.length) {
  602. throw new InvalidKeyException("Argument key's length="+ key.length +
  603. " is different from the number of item values, which is "+ indexNamesArray.length +
  604. ", specified for the indexing rows in this TabularData instance.");
  605. }
  606. // each element in key[] should be a value for its corresponding open type specified in rowType
  607. //
  608. OpenType keyElementType;
  609. for (int i=0; i<key.length; i++) {
  610. keyElementType = tabularType.getRowType().getType(this.indexNamesArray[i]);
  611. if ( (key[i] != null) && (! keyElementType.isValue(key[i])) ) {
  612. throw new InvalidKeyException("Argument element key["+ i +"] is not a value for the open type expected for "+
  613. "this element of the index, whose name is \""+ indexNamesArray[i] +
  614. "\" and whose open type is "+ keyElementType);
  615. }
  616. }
  617. }
  618. /**
  619. * Checks the specified value's type is valid for this <tt>TabularData</tt> instance
  620. * (ie value is not null, and its composite type is equal to row type).
  621. *
  622. * @throws NullPointerException
  623. * @throws InvalidOpenTypeException
  624. */
  625. private void checkValueType(CompositeData value) {
  626. // Check value is not null
  627. //
  628. if (value == null) {
  629. throw new NullPointerException("Argument value cannot be null.");
  630. }
  631. // if value's type is not the same as this instance's row type, throw InvalidOpenTypeException
  632. //
  633. if ( ! value.getCompositeType().equals(tabularType.getRowType()) ) {
  634. throw new InvalidOpenTypeException("Argument value's composite type ["+ value.getCompositeType() +
  635. "] is not equal to "+
  636. "this TabularData instance's row type ["+ tabularType.getRowType() +"].");
  637. }
  638. }
  639. /**
  640. * Checks if the specified value can be put (ie added) in this <tt>TabularData</tt> instance
  641. * (ie value is not null, its composite type is equal to row type, and its index is not already used),
  642. * and returns the index calculated for this value.
  643. *
  644. * The index is a List, and not an array, so that an index.equals(otherIndex) call will actually compare contents,
  645. * not just the objects references as is done for an array object.
  646. *
  647. * @throws NullPointerException
  648. * @throws InvalidOpenTypeException
  649. * @throws KeyAlreadyExistsException
  650. */
  651. private List checkValueAndIndex(CompositeData value) {
  652. // Check value is valid
  653. //
  654. checkValueType(value);
  655. // Calculate value's index according to this instance's tabularType
  656. // and check it is not already used for a mapping in the parent HashMap
  657. //
  658. List index = internalCalculateIndex(value);
  659. if (dataMap.containsKey(index)) {
  660. throw new KeyAlreadyExistsException("Argument value's index, calculated according to this TabularData "+
  661. "instance's tabularType, already refers to a value in this table.");
  662. }
  663. // The check is OK, so return the index
  664. //
  665. return index;
  666. }
  667. /**
  668. * Deserializes a {@link TabularDataSupport} from an {@link ObjectInputStream}.
  669. */
  670. private void readObject(ObjectInputStream in)
  671. throws IOException, ClassNotFoundException {
  672. in.defaultReadObject();
  673. List tmpNames = tabularType.getIndexNames();
  674. indexNamesArray = (String[]) tmpNames.toArray(new String[tmpNames.size()]);
  675. }
  676. }