1. /*
  2. * @(#)CompositeDataSupport.java 3.27 04/03/24
  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.Serializable;
  11. import java.util.Set;
  12. import java.util.Map;
  13. import java.util.SortedMap;
  14. import java.util.TreeMap;
  15. import java.util.Collection;
  16. import java.util.Collections;
  17. import java.util.Iterator;
  18. import java.util.Arrays;
  19. // jmx import
  20. //
  21. /**
  22. * The <tt>CompositeDataSupport</tt> class is the <i>open data</i> class which implements the <tt>CompositeData</tt> interface.
  23. *
  24. * @version 3.27 04/03/24
  25. * @author Sun Microsystems, Inc.
  26. *
  27. * @since 1.5
  28. * @since.unbundled JMX 1.1
  29. */
  30. public class CompositeDataSupport
  31. implements CompositeData, Serializable {
  32. /* Serial version */
  33. static final long serialVersionUID = 8003518976613702244L;
  34. /**
  35. * @serial Internal representation of the mapping of item names to their respective values.
  36. * A {@link SortedMap} is used for faster retrieval of elements.
  37. */
  38. private SortedMap contents = new TreeMap();
  39. /**
  40. * @serial The <i>composite type </i> of this <i>composite data</i> instance.
  41. */
  42. private CompositeType compositeType;
  43. /**
  44. * <p>
  45. * Constructs a <tt>CompositeDataSupport</tt> instance with the specified <tt>compositeType</tt>, whose item values
  46. * are specified by <tt>itemValues[]</tt>, in the same order as in <tt>itemNames[]</tt>.
  47. * As a <tt>CompositeType</tt> does not specify any order on its items, the <tt>itemNames[]</tt> parameter is used
  48. * to specify the order in which the values are given in <tt>itemValues[]</tt>.
  49. * The items contained in this <tt>CompositeDataSupport</tt> instance are internally stored in a <tt>TreeMap</tt>,
  50. * thus sorted in ascending lexicographic order of their names, for faster retrieval of individual item values.
  51. * <p>
  52. * The constructor checks that all the constraints listed below for each parameter are satisfied,
  53. * and throws the appropriate exception if they are not.
  54. * <p>
  55. * @param compositeType the <i>composite type </i> of this <i>composite data</i> instance;
  56. * must not be null.
  57. * <p>
  58. * @param itemNames <tt>itemNames</tt> must list, in any order, all the item names defined in <tt>compositeType</tt>
  59. * the order in which the names are listed, is used to match values in <tt>itemValues[]</tt>
  60. * must not be null or empty.
  61. * <p>
  62. * @param itemValues the values of the items, listed in the same order as their respective names in <tt>itemNames</tt>
  63. * each item value can be null, but if it is non-null it must be
  64. * a valid value for the open type defined in <tt>compositeType</tt> for the corresponding item;
  65. * must be of the same size as <tt>itemNames</tt> must not be null or empty.
  66. * <p>
  67. * @throws IllegalArgumentException <tt>compositeType</tt> is null, or <tt>itemNames[]</tt> or <tt>itemValues[]</tt> is null or empty,
  68. * or one of the elements in <tt>itemNames[]</tt> is a null or empty string,
  69. * or <tt>itemNames[]</tt> and <tt>itemValues[]</tt> are not of the same size.
  70. * <p>
  71. * @throws OpenDataException <tt>itemNames[]</tt> or <tt>itemValues[]</tt>'s size differs from
  72. * the number of items defined in <tt>compositeType</tt>,
  73. * or one of the elements in <tt>itemNames[]</tt> does not exist as an item name defined in <tt>compositeType</tt>,
  74. * or one of the elements in <tt>itemValues[]</tt> is not a valid value for the corresponding item
  75. * as defined in <tt>compositeType</tt>.
  76. * <p>
  77. */
  78. public CompositeDataSupport(CompositeType compositeType, String[] itemNames, Object[] itemValues)
  79. throws OpenDataException {
  80. // Check compositeType is not null
  81. //
  82. if (compositeType == null) {
  83. throw new IllegalArgumentException("Argument compositeType cannot be null.");
  84. }
  85. // item names defined in compositeType:
  86. Set namesSet = compositeType.keySet();
  87. // Check the array itemNames is not null or empty (length!=0) and
  88. // that there is no null element or empty string in it
  89. //
  90. checkForNullElement(itemNames, "itemNames");
  91. checkForEmptyString(itemNames, "itemNames");
  92. // Check the array itemValues is not null or empty (length!=0)
  93. // (NOTE: we allow null values as array elements)
  94. //
  95. if ( (itemValues == null) || (itemValues.length == 0) ) {
  96. throw new IllegalArgumentException("Argument itemValues[] cannot be null or empty.");
  97. }
  98. // Check that the sizes of the 2 arrays itemNames and itemValues are the same
  99. //
  100. if (itemNames.length != itemValues.length) {
  101. throw new IllegalArgumentException("Array arguments itemNames[] and itemValues[] "+
  102. "should be of same length (got "+ itemNames.length +
  103. " and "+ itemValues.length +").");
  104. }
  105. // Check the size of the 2 arrays is equal to the number of items defined in compositeType
  106. //
  107. if (itemNames.length != namesSet.size()) {
  108. throw new OpenDataException("The size of array arguments itemNames[] and itemValues[] should be equal to the number of items defined"+
  109. " in argument compositeType (found "+ itemNames.length +" elements in itemNames[] and itemValues[],"+
  110. " expecting "+ namesSet.size() +" elements according to compositeType.");
  111. }
  112. // Check parameter itemNames[] contains all names defined in the compositeType of this instance
  113. //
  114. if ( ! Arrays.asList(itemNames).containsAll(namesSet) ) {
  115. throw new OpenDataException("Argument itemNames[] does not contain all names defined in the compositeType of this instance.");
  116. }
  117. // Check each element of itemValues[], if not null, is of the open type defined for the corresponding item
  118. //
  119. OpenType itemType;
  120. for (int i=0; i<itemValues.length; i++) {
  121. itemType = compositeType.getType(itemNames[i]);
  122. if ( (itemValues[i] != null) && (! itemType.isValue(itemValues[i])) ) {
  123. throw new OpenDataException("Argument's element itemValues["+ i +"]=\""+ itemValues[i] +"\" is not a valid value for"+
  124. " this item (itemName="+ itemNames[i] +",itemType="+ itemType +").");
  125. }
  126. }
  127. // Initialize internal fields: compositeType and contents
  128. //
  129. this.compositeType = compositeType;
  130. for (int i=0; i<itemNames.length; i++) {
  131. this.contents.put(itemNames[i], itemValues[i]);
  132. }
  133. }
  134. /**
  135. * <p>
  136. * Constructs a <tt>CompositeDataSupport</tt> instance with the specified <tt>compositeType</tt>, whose item names and corresponding values
  137. * are given by the mappings in the map <tt>items</tt>.
  138. * This constructor converts the keys to a string array and the values to an object array and calls
  139. * <tt>CompositeDataSupport(javax.management.openmbean.CompositeType, java.lang.String[], java.lang.Object[])</tt>.
  140. * <p>
  141. * @param compositeType the <i>composite type </i> of this <i>composite data</i> instance;
  142. * must not be null.
  143. * <p>
  144. * @param items the mappings of all the item names to their values;
  145. * <tt>items</tt> must contain all the item names defined in <tt>compositeType</tt>
  146. * must not be null or empty.
  147. * <p>
  148. * @throws IllegalArgumentException <tt>compositeType</tt> is null, or <tt>items</tt> is null or empty,
  149. * or one of the keys in <tt>items</tt> is a null or empty string,
  150. * or one of the values in <tt>items</tt> is null.
  151. * <p>
  152. * @throws OpenDataException <tt>items</tt>' size differs from the number of items defined in <tt>compositeType</tt>,
  153. * or one of the keys in <tt>items</tt> does not exist as an item name defined in <tt>compositeType</tt>,
  154. * or one of the values in <tt>items</tt> is not a valid value for the corresponding item
  155. * as defined in <tt>compositeType</tt>.
  156. * <p>
  157. * @throws ArrayStoreException one or more keys in <tt>items</tt> is not of the class <tt>java.lang.String</tt>.
  158. * <p>
  159. */
  160. public CompositeDataSupport(CompositeType compositeType, Map items)
  161. throws OpenDataException {
  162. // Let the other constructor do the job, as the call to another constructor must be the first call
  163. //
  164. this( compositeType,
  165. (items==null ? null : (String[]) items.keySet().toArray(new String[items.size()])), // may raise an ArrayStoreException
  166. (items==null ? null : items.values().toArray()) );
  167. }
  168. /**
  169. *
  170. */
  171. private static void checkForNullElement(Object[] arg, String argName) {
  172. if ( (arg == null) || (arg.length == 0) ) {
  173. throw new IllegalArgumentException("Argument "+ argName +"[] cannot be null or empty.");
  174. }
  175. for (int i=0; i<arg.length; i++) {
  176. if (arg[i] == null) {
  177. throw new IllegalArgumentException("Argument's element "+ argName +"["+ i +"] cannot be null.");
  178. }
  179. }
  180. }
  181. /**
  182. *
  183. */
  184. private static void checkForEmptyString(String[] arg, String argName) {
  185. for (int i=0; i<arg.length; i++) {
  186. if (arg[i].trim().equals("")) {
  187. throw new IllegalArgumentException("Argument's element "+ argName +"["+ i +"] cannot be an empty string.");
  188. }
  189. }
  190. }
  191. /**
  192. * Returns the <i>composite type </i> of this <i>composite data</i> instance.
  193. */
  194. public CompositeType getCompositeType() {
  195. return compositeType;
  196. }
  197. /**
  198. * Returns the value of the item whose name is <tt>key</tt>.
  199. *
  200. * @throws IllegalArgumentException if <tt>key</tt> is a null or empty String.
  201. *
  202. * @throws InvalidKeyException if <tt>key</tt> is not an existing item name for this <tt>CompositeData</tt> instance.
  203. */
  204. public Object get(String key) {
  205. if ( (key == null) || (key.trim().equals("")) ) {
  206. throw new IllegalArgumentException("Argument key cannot be a null or empty String.");
  207. }
  208. if ( ! contents.containsKey(key.trim())) {
  209. throw new InvalidKeyException("Argument key=\""+ key.trim() +"\" is not an existing item name for this CompositeData instance.");
  210. }
  211. return contents.get(key.trim());
  212. }
  213. /**
  214. * Returns an array of the values of the items whose names are specified by <tt>keys</tt>, in the same order as <tt>keys</tt>.
  215. *
  216. * @throws IllegalArgumentException if an element in <tt>keys</tt> is a null or empty String.
  217. *
  218. * @throws InvalidKeyException if an element in <tt>keys</tt> is not an existing item name for this <tt>CompositeData</tt> instance.
  219. */
  220. public Object[] getAll(String[] keys) {
  221. if ( (keys == null) || (keys.length == 0) ) {
  222. return new Object[0];
  223. }
  224. Object[] results = new Object[keys.length];
  225. for (int i=0; i<keys.length; i++) {
  226. results[i] = this.get(keys[i]);
  227. }
  228. return results;
  229. }
  230. /**
  231. * Returns <tt>true</tt> if and only if this <tt>CompositeData</tt> instance contains
  232. * an item whose name is <tt>key</tt>.
  233. * If <tt>key</tt> is a null or empty String, this method simply returns false.
  234. */
  235. public boolean containsKey(String key) {
  236. if ( (key == null) || (key.trim().equals("")) ) {
  237. return false;
  238. }
  239. return contents.containsKey(key);
  240. }
  241. /**
  242. * Returns <tt>true</tt> if and only if this <tt>CompositeData</tt> instance contains an item
  243. * whose value is <tt>value</tt>.
  244. */
  245. public boolean containsValue(Object value) {
  246. return contents.containsValue(value);
  247. }
  248. /**
  249. * Returns an unmodifiable Collection view of the item values contained in this <tt>CompositeData</tt> instance.
  250. * The returned collection's iterator will return the values in the ascending lexicographic order of the corresponding
  251. * item names.
  252. */
  253. public Collection values() {
  254. return Collections.unmodifiableCollection(contents.values());
  255. }
  256. /**
  257. * Compares the specified <var>obj</var> parameter with this <code>CompositeDataSupport</code> instance for equality.
  258. * <p>
  259. * Returns <tt>true</tt> if and only if all of the following statements are true:
  260. * <ul>
  261. * <li><var>obj</var> is non null,</li>
  262. * <li><var>obj</var> also implements the <code>CompositeData</code> interface,</li>
  263. * <li>their composite types are equal</li>
  264. * <li>their contents, i.e. (name, value) pairs are equal. </li>
  265. * </ul>
  266. * This ensures that this <tt>equals</tt> method works properly for <var>obj</var> parameters which are
  267. * different implementations of the <code>CompositeData</code> interface, with the restrictions mentioned in the
  268. * {@link java.util.Collection#equals(Object) equals}
  269. * method of the <tt>java.util.Collection</tt> interface.
  270. * <br> 
  271. * @param obj the object to be compared for equality with this <code>CompositeDataSupport</code> instance;
  272. *
  273. * @return <code>true</code> if the specified object is equal to this <code>CompositeDataSupport</code> instance.
  274. */
  275. public boolean equals(Object obj) {
  276. // if obj is null, return false
  277. //
  278. if (obj == null) {
  279. return false;
  280. }
  281. // if obj is not a CompositeData, return false
  282. //
  283. CompositeData other;
  284. try {
  285. other = (CompositeData) obj;
  286. } catch (ClassCastException e) {
  287. return false;
  288. }
  289. // their compositeType should be equal
  290. if ( ! this.getCompositeType().equals(other.getCompositeType()) ) {
  291. return false;
  292. }
  293. // Currently this test returns false if we have different Array instances with same contents.
  294. // Array objects are equals only if their references are equal, ie they are the same object!
  295. // CompositeData equals() method need to be modified to compare Array contents...
  296. // their content, i.e. (name, value) pairs, should be equal
  297. Map.Entry entry;
  298. boolean ok;
  299. for (Iterator iter = contents.entrySet().iterator(); iter.hasNext(); ) {
  300. entry = (Map.Entry) iter.next();
  301. ok = ( entry.getValue() == null ?
  302. other.get((String)entry.getKey()) == null :
  303. entry.getValue().equals(other.get((String)entry.getKey())) );
  304. if ( ! ok ) {
  305. return false;
  306. }
  307. }
  308. // All tests for equality were successfull
  309. //
  310. return true;
  311. }
  312. /**
  313. * Returns the hash code value for this <code>CompositeDataSupport</code> instance.
  314. * <p>
  315. * The hash code of a <code>CompositeDataSupport</code> instance is the sum of the hash codes
  316. * of all elements of information used in <code>equals</code> comparisons
  317. * (ie: its <i>composite type</i> and all the item values).
  318. * <p>
  319. * This ensures that <code> t1.equals(t2) </code> implies that <code> t1.hashCode()==t2.hashCode() </code>
  320. * for any two <code>CompositeDataSupport</code> instances <code>t1</code> and <code>t2</code>,
  321. * as required by the general contract of the method
  322. * {@link Object#hashCode() Object.hashCode()}.
  323. * <p>
  324. * However, note that another instance of a class implementing the <code>CompositeData</code> interface
  325. * may be equal to this <code>CompositeDataSupport</code> instance as defined by {@link #equals},
  326. * but may have a different hash code if it is calculated differently.
  327. *
  328. * @return the hash code value for this <code>CompositeDataSupport</code> instance
  329. */
  330. public int hashCode() {
  331. int result = 0;
  332. result += compositeType.hashCode();
  333. Map.Entry entry;
  334. for (Iterator iter = contents.entrySet().iterator(); iter.hasNext(); ) {
  335. entry = (Map.Entry) iter.next();
  336. result += ( entry.getValue() == null ? 0 : entry.getValue().hashCode() );
  337. }
  338. return result;
  339. }
  340. /**
  341. * Returns a string representation of this <code>CompositeDataSupport</code> instance.
  342. * <p>
  343. * The string representation consists of the name of this class (ie <code>javax.management.openmbean.CompositeDataSupport</code>),
  344. * the string representation of the composite type of this instance, and the string representation of the contents
  345. * (ie list the itemName=itemValue mappings).
  346. *
  347. * @return a string representation of this <code>CompositeDataSupport</code> instance
  348. */
  349. public String toString() {
  350. return new StringBuffer()
  351. .append(this.getClass().getName())
  352. .append("(compositeType=")
  353. .append(compositeType.toString())
  354. .append(",contents=")
  355. .append(contents.toString())
  356. .append(")")
  357. .toString();
  358. }
  359. }