1. /*
  2. * @(#)CompositeType.java 3.25 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.Serializable;
  11. import java.util.Set;
  12. import java.util.TreeMap;
  13. import java.util.Collections;
  14. import java.util.Iterator;
  15. // jmx import
  16. //
  17. /**
  18. * The <code>CompositeType</code> class is the <i>open type</i> class
  19. * whose instances describe the types of {@link CompositeData <code>CompositeData</code>} values.
  20. *
  21. * @version 3.25 03/12/19
  22. * @author Sun Microsystems, Inc.
  23. *
  24. * @since 1.5
  25. * @since.unbundled JMX 1.1
  26. */
  27. public class CompositeType
  28. extends OpenType
  29. implements Serializable {
  30. /* Serial version */
  31. static final long serialVersionUID = -5366242454346948798L;
  32. /**
  33. * @serial Sorted mapping of the item names to their descriptions
  34. */
  35. private TreeMap nameToDescription;
  36. /**
  37. * @serial Sorted mapping of the item names to their open types
  38. */
  39. private TreeMap nameToType;
  40. private transient Integer myHashCode = null; // As this instance is immutable,
  41. private transient String myToString = null; // these three values
  42. private transient Set myNamesSet = null; // need only be calculated once.
  43. /* *** Constructor *** */
  44. /**
  45. * Constructs a <code>CompositeType</code> instance, checking for the validity of the given parameters.
  46. * The validity constraints are described below for each parameter.
  47. * <p>
  48. * Note that the contents of the three array parameters
  49. * <var>itemNames</var>, <var>itemDescriptions</var> and <var>itemTypes</var>
  50. * are internally copied so that any subsequent modification of these arrays by the caller of this constructor
  51. * has no impact on the constructed <code>CompositeType</code> instance.
  52. * <p>
  53. * The Java class name of composite data values this composite type represents
  54. * (ie the class name returned by the {@link OpenType#getClassName() getClassName} method)
  55. * is set to the string value returned by <code>CompositeData.class.getName()</code>.
  56. * <p>
  57. * @param typeName The name given to the composite type this instance represents; cannot be a null or empty string.
  58. * <br> 
  59. * @param description The human readable description of the composite type this instance represents;
  60. * cannot be a null or empty string.
  61. * <br> 
  62. * @param itemNames The names of the items contained in the
  63. * composite data values described by this <code>CompositeType</code> instance;
  64. * cannot be null and should contain at least one element; no element can be a null or empty string.
  65. * Note that the order in which the item names are given is not important to differentiate a
  66. * <code>CompositeType</code> instance from another;
  67. * the item names are internally stored sorted in ascending alphanumeric order.
  68. * <br> 
  69. * @param itemDescriptions The descriptions, in the same order as <var>itemNames</var>, of the items contained in the
  70. * composite data values described by this <code>CompositeType</code> instance;
  71. * should be of the same size as <var>itemNames</var>
  72. * no element can be a null or empty string.
  73. * <br> 
  74. * @param itemTypes The open type instances, in the same order as <var>itemNames</var>, describing the items contained
  75. * in the composite data values described by this <code>CompositeType</code> instance;
  76. * should be of the same size as <var>itemNames</var>
  77. * no element can be null.
  78. * <br> 
  79. * @throws IllegalArgumentException If <var>typeName</var> or <var>description</var> is a null or empty string,
  80. * or <var>itemNames</var> or <var>itemDescriptions</var> or <var>itemTypes</var> is null,
  81. * or any element of <var>itemNames</var> or <var>itemDescriptions</var>
  82. * is a null or empty string,
  83. * or any element of <var>itemTypes</var> is null,
  84. * or <var>itemNames</var> or <var>itemDescriptions</var> or <var>itemTypes</var>
  85. * are not of the same size.
  86. * <br> 
  87. * @throws OpenDataException If <var>itemNames</var> contains duplicate item names
  88. * (case sensitive, but leading and trailing whitespaces removed).
  89. */
  90. public CompositeType(String typeName,
  91. String description,
  92. String[] itemNames,
  93. String[] itemDescriptions,
  94. OpenType[] itemTypes) throws OpenDataException {
  95. // Check and construct state defined by parent
  96. //
  97. super(CompositeData.class.getName(), typeName, description);
  98. // Check the 3 arrays are not null or empty (ie length==0) and that there is no null element or empty string in them
  99. //
  100. checkForNullElement(itemNames, "itemNames");
  101. checkForNullElement(itemDescriptions, "itemDescriptions");
  102. checkForNullElement(itemTypes, "itemTypes");
  103. checkForEmptyString(itemNames, "itemNames");
  104. checkForEmptyString(itemDescriptions, "itemDescriptions");
  105. // Check the sizes of the 3 arrays are the same
  106. //
  107. if ( (itemNames.length != itemDescriptions.length) || (itemNames.length != itemTypes.length) ) {
  108. throw new IllegalArgumentException("Array arguments itemNames[], itemDescriptions[] and itemTypes[] "+
  109. "should be of same length (got "+ itemNames.length +", "+
  110. itemDescriptions.length +" and "+ itemTypes.length +").");
  111. }
  112. // Initialize internal "names to descriptions" and "names to types" sorted maps,
  113. // and, by doing so, check there are no duplicate item names
  114. //
  115. nameToDescription = new TreeMap();
  116. nameToType = new TreeMap();
  117. String key;
  118. for (int i=0; i<itemNames.length; i++) {
  119. key = itemNames[i].trim();
  120. if (nameToDescription.containsKey(key)) {
  121. throw new OpenDataException("Argument's element itemNames["+ i +"]=\""+ itemNames[i] +
  122. "\" duplicates a previous item names.");
  123. }
  124. nameToDescription.put(key, itemDescriptions[i].trim());
  125. nameToType.put(key, itemTypes[i]);
  126. }
  127. }
  128. private static void checkForNullElement(Object[] arg, String argName) {
  129. if ( (arg == null) || (arg.length == 0) ) {
  130. throw new IllegalArgumentException("Argument "+ argName +"[] cannot be null or empty.");
  131. }
  132. for (int i=0; i<arg.length; i++) {
  133. if (arg[i] == null) {
  134. throw new IllegalArgumentException("Argument's element "+ argName +"["+ i +"] cannot be null.");
  135. }
  136. }
  137. }
  138. private static void checkForEmptyString(String[] arg, String argName) {
  139. for (int i=0; i<arg.length; i++) {
  140. if (arg[i].trim().equals("")) {
  141. throw new IllegalArgumentException("Argument's element "+ argName +"["+ i +"] cannot be an empty string.");
  142. }
  143. }
  144. }
  145. /* *** Composite type specific information methods *** */
  146. /**
  147. * Returns <code>true</code> if this <code>CompositeType</code> instance defines an item
  148. * whose name is <var>itemName</var>.
  149. *
  150. * @param itemName the name of the item.
  151. *
  152. * @return true if an item of this name is present.
  153. */
  154. public boolean containsKey(String itemName) {
  155. if (itemName == null) {
  156. return false;
  157. }
  158. return nameToDescription.containsKey(itemName);
  159. }
  160. /**
  161. * Returns the description of the item whose name is <var>itemName</var>,
  162. * or <code>null</code> if this <code>CompositeType</code> instance does not define any item
  163. * whose name is <var>itemName</var>.
  164. *
  165. * @param itemName the name of the item.
  166. *
  167. * @return the description.
  168. */
  169. public String getDescription(String itemName) {
  170. if (itemName == null) {
  171. return null;
  172. }
  173. return (String) nameToDescription.get(itemName);
  174. }
  175. /**
  176. * Returns the <i>open type</i> of the item whose name is <var>itemName</var>,
  177. * or <code>null</code> if this <code>CompositeType</code> instance does not define any item
  178. * whose name is <var>itemName</var>.
  179. *
  180. * @param itemName the name of the time.
  181. *
  182. * @return the type.
  183. */
  184. public OpenType getType(String itemName) {
  185. if (itemName == null) {
  186. return null;
  187. }
  188. return (OpenType) nameToType.get(itemName);
  189. }
  190. /**
  191. * Returns an unmodifiable Set view of all the item names defined by this <code>CompositeType</code> instance.
  192. * The set's iterator will return the item names in ascending order.
  193. *
  194. * @return a {@link Set} of {@link String}.
  195. */
  196. public Set keySet() {
  197. // Initializes myNamesSet on first call
  198. if (myNamesSet == null) {
  199. myNamesSet = Collections.unmodifiableSet(nameToDescription.keySet());
  200. }
  201. return myNamesSet; // always return the same value
  202. }
  203. /**
  204. * Tests whether <var>obj</var> is a value which could be described by this <code>CompositeType</code> instance.
  205. * <p>
  206. * If <var>obj</var> is null or is not an instance of <code>javax.management.openmbean.CompositeData</code>,
  207. * <code>isValue</code> returns <code>false</code>.
  208. * If <var>obj</var> is an instance of <code>javax.management.openmbean.CompositeData</code>,
  209. * its composite type is tested for equality with this <code>CompositeType</code> instance, and <code>isValue</code>
  210. * returns <code>true</code> if and only if {@link #equals(java.lang.Object) <code>equals</code>}
  211. * returns <code>true</code>.
  212. * <br> 
  213. * @param obj the value whose open type is to be tested for equality with this <code>CompositeType</code> instance.
  214. *
  215. * @return <code>true</code> if <var>obj</var> is a value for this composite type, <code>false</code> otherwise.
  216. */
  217. public boolean isValue(Object obj) {
  218. // if obj is null, return false
  219. //
  220. if (obj == null) {
  221. return false;
  222. }
  223. // if obj is not a CompositeData, return false
  224. //
  225. CompositeData value;
  226. try {
  227. value = (CompositeData) obj;
  228. } catch (ClassCastException e) {
  229. return false;
  230. }
  231. // test value's CompositeType for equality with this CompositeType instance
  232. //
  233. return this.equals(value.getCompositeType());
  234. }
  235. /* *** Methods overriden from class Object *** */
  236. /**
  237. * Compares the specified <code>obj</code> parameter with this <code>CompositeType</code> instance for equality.
  238. * <p>
  239. * Two <code>CompositeType</code> instances are equal if and only if all of the following statements are true:
  240. * <ul>
  241. * <li>their type names are equal</li>
  242. * <li>their items' names and types are equal</li>
  243. * </ul>
  244. * <br> 
  245. * @param obj the object to be compared for equality with this <code>CompositeType</code> instance;
  246. * if <var>obj</var> is <code>null</code>, <code>equals</code> returns <code>false</code>.
  247. *
  248. * @return <code>true</code> if the specified object is equal to this <code>CompositeType</code> instance.
  249. */
  250. public boolean equals(Object obj) {
  251. // if obj is null, return false
  252. //
  253. if (obj == null) {
  254. return false;
  255. }
  256. // if obj is not a CompositeType, return false
  257. //
  258. CompositeType other;
  259. try {
  260. other = (CompositeType) obj;
  261. } catch (ClassCastException e) {
  262. return false;
  263. }
  264. // Now, really test for equality between this CompositeType instance and the other
  265. //
  266. // their names should be equal
  267. if ( ! this.getTypeName().equals(other.getTypeName()) ) {
  268. return false;
  269. }
  270. // their items names and types should be equal
  271. if ( ! this.nameToType.equals(other.nameToType) ) {
  272. return false;
  273. }
  274. // All tests for equality were successfull
  275. //
  276. return true;
  277. }
  278. /**
  279. * Returns the hash code value for this <code>CompositeType</code> instance.
  280. * <p>
  281. * The hash code of a <code>CompositeType</code> instance is the sum of the hash codes
  282. * of all elements of information used in <code>equals</code> comparisons
  283. * (ie: name, items names, items types).
  284. * This ensures that <code> t1.equals(t2) </code> implies that <code> t1.hashCode()==t2.hashCode() </code>
  285. * for any two <code>CompositeType</code> instances <code>t1</code> and <code>t2</code>,
  286. * as required by the general contract of the method
  287. * {@link Object#hashCode() Object.hashCode()}.
  288. * <p>
  289. * As <code>CompositeType</code> instances are immutable, the hash code for this instance is calculated once,
  290. * on the first call to <code>hashCode</code>, and then the same value is returned for subsequent calls.
  291. *
  292. * @return the hash code value for this <code>CompositeType</code> instance
  293. */
  294. public int hashCode() {
  295. // Calculate the hash code value if it has not yet been done (ie 1st call to hashCode())
  296. //
  297. if (myHashCode == null) {
  298. int value = 0;
  299. value += this.getTypeName().hashCode();
  300. String key;
  301. for (Iterator k = nameToDescription.keySet().iterator(); k.hasNext(); ) {
  302. key = (String) k.next();
  303. value += key.hashCode();
  304. value += this.nameToType.get(key).hashCode();
  305. }
  306. myHashCode = new Integer(value);
  307. }
  308. // return always the same hash code for this instance (immutable)
  309. //
  310. return myHashCode.intValue();
  311. }
  312. /**
  313. * Returns a string representation of this <code>CompositeType</code> instance.
  314. * <p>
  315. * The string representation consists of
  316. * the name of this class (ie <code>javax.management.openmbean.CompositeType</code>), the type name for this instance,
  317. * and the list of the items names and types string representation of this instance.
  318. * <p>
  319. * As <code>CompositeType</code> instances are immutable, the string representation for this instance is calculated once,
  320. * on the first call to <code>toString</code>, and then the same value is returned for subsequent calls.
  321. *
  322. * @return a string representation of this <code>CompositeType</code> instance
  323. */
  324. public String toString() {
  325. // Calculate the string representation if it has not yet been done (ie 1st call to toString())
  326. //
  327. if (myToString == null) {
  328. StringBuffer result = new StringBuffer();
  329. result.append(this.getClass().getName());
  330. result.append("(name=");
  331. result.append(getTypeName());
  332. result.append(",items=(");
  333. int i=0;
  334. Iterator k=nameToType.keySet().iterator();
  335. String key;
  336. while (k.hasNext()) {
  337. key = (String) k.next();
  338. if (i > 0) result.append(",");
  339. result.append("(itemName=");
  340. result.append(key);
  341. result.append(",itemType=");
  342. result.append(nameToType.get(key).toString() +")");
  343. i++;
  344. }
  345. result.append("))");
  346. myToString = result.toString();
  347. }
  348. // return always the same string representation for this instance (immutable)
  349. //
  350. return myToString;
  351. }
  352. }