1. /*
  2. * @(#)ArrayType.java 3.24 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. // jmx import
  12. //
  13. /**
  14. * The <code>ArrayType</code> class is the <i>open type</i> class whose instances describe
  15. * all <i>open data</i> values which are n-dimensional arrays of <i>open data</i> values.
  16. *
  17. * @version 3.24 03/12/19
  18. * @author Sun Microsystems, Inc.
  19. *
  20. * @since 1.5
  21. * @since.unbundled JMX 1.1
  22. */
  23. public class ArrayType
  24. extends OpenType
  25. implements Serializable {
  26. /* Serial version */
  27. static final long serialVersionUID = 720504429830309770L;
  28. /**
  29. * @serial The dimension of arrays described by this {@link ArrayType} instance
  30. */
  31. private int dimension;
  32. /**
  33. * @serial The <i>open type</i> of element values contained in the arrays described by
  34. * this {@link ArrayType} instance
  35. */
  36. private OpenType elementType;
  37. private transient Integer myHashCode = null; // As this instance is immutable, these two values
  38. private transient String myToString = null; // need only be calculated once.
  39. /* *** Constructor *** */
  40. /**
  41. * Constructs an <tt>ArrayType</tt> instance describing <i>open data</i> values which are
  42. * arrays with dimension <var>dimension</var> of elements whose <i>open type</i> is <var>elementType</var>.
  43. * <p>
  44. * When invoked on an <tt>ArrayType</tt> instance, the {@link OpenType#getClassName() getClassName} method
  45. * returns the class name of the array instances it describes (following the rules defined by the
  46. * {@link Class#getName() getName} method of <code>java.lang.Class</code>), not the class name of the array elements
  47. * (which is returned by a call to <tt>getElementOpenType().getClassName()</tt>).
  48. * <p>
  49. * The internal field corresponding to the type name of this <code>ArrayType</code> instance is also set to
  50. * the class name of the array instances it describes.
  51. * In other words, the methods <code>getClassName</code> and <code>getTypeName</code> return the same string value.
  52. * The internal field corresponding to the description of this <code>ArrayType</code> instance is set to a string value
  53. * which follows the following template:<br>
  54. * <tt><i><dimension></i>-dimension array of <i><element_class_name></i></tt>
  55. * <p>
  56. * As an example, the following piece of code:
  57. * <pre>
  58. * ArrayType t = new ArrayType(3, SimpleType.STRING);
  59. * System.out.println("array class name = "+ t.getClassName());
  60. * System.out.println("element class name = "+ t.getElementOpenType().getClassName());
  61. * System.out.println("array type name = "+ t.getTypeName());
  62. * System.out.println("array type description = "+ t.getDescription());
  63. * </pre>
  64. * would produce the following output:
  65. * <pre>
  66. * array class name = [[[java.lang.String;
  67. * element class name = java.lang.String
  68. * array type name = [[[java.lang.String;
  69. * array type description = 3-dimension array of java.lang.String
  70. * </pre>
  71. *
  72. * @param dimension the dimension of arrays described by this <tt>ArrayType</tt> instance;
  73. * must be greater than or equal to 1.
  74. *
  75. * @param elementType the <i>open type</i> of element values contained in the arrays described by
  76. * this <tt>ArrayType</tt> instance; must be an instance of either
  77. * <tt>SimpleType</tt>, <tt>CompositeType</tt> or <tt>TabularType</tt>.
  78. *
  79. * @throws IllegalArgumentException if <var>dimension</var> is not a positive integer
  80. *
  81. * @throws OpenDataException if <var>elementType</var> is an instance of <tt>ArrayType</tt>
  82. */
  83. public ArrayType(int dimension,
  84. OpenType elementType) throws OpenDataException {
  85. // Check and construct state defined by parent.
  86. //
  87. super(buildArrayClassName(dimension, elementType.getClassName()),
  88. buildArrayClassName(dimension, elementType.getClassName()),
  89. String.valueOf(dimension) +"-dimension array of "+ elementType.getClassName());
  90. // Check and construct state specific to ArrayType
  91. //
  92. this.dimension = dimension; // already checked >=1 in buildArrayClassName
  93. this.elementType = elementType; // cannot be ArrayType: super() would throw exception on the built classname
  94. }
  95. /**
  96. *
  97. */
  98. private static String buildArrayClassName(int dimension, String elementClassName) throws OpenDataException {
  99. if (dimension < 1) {
  100. throw new IllegalArgumentException("Value of argument dimension must be greater than 0");
  101. }
  102. StringBuffer result = new StringBuffer();
  103. for (int i=1; i<dimension; i++) { // add (dimension - 1) '[' characters
  104. result.append('[');
  105. }
  106. result.append("[L");
  107. result.append(elementClassName);
  108. result.append(';');
  109. return result.toString();
  110. }
  111. /* *** ArrayType specific information methods *** */
  112. /**
  113. * Returns the dimension of arrays described by this <tt>ArrayType</tt> instance.
  114. *
  115. * @return the dimension.
  116. */
  117. public int getDimension() {
  118. return dimension;
  119. }
  120. /**
  121. * Returns the <i>open type</i> of element values contained in the arrays described by this <tt>ArrayType</tt> instance.
  122. *
  123. * @return the element type.
  124. */
  125. public OpenType getElementOpenType() {
  126. return elementType;
  127. }
  128. /**
  129. * Tests whether <var>obj</var> is a value for this <code>ArrayType</code> instance.
  130. * <p>
  131. * This method returns <code>true</code> if and only if <var>obj</var> is not null, <var>obj</var> is an array
  132. * and any one of the following is <tt>true</tt>:
  133. * <p><ul>
  134. * <li>if this <code>ArrayType</code> instance describes an array of <tt>SimpleType</tt> elements,
  135. * <var>obj</var>'s class name is the same as the className field defined for this <code>ArrayType</code> instance
  136. * (ie the class name returned by the {@link OpenType#getClassName() getClassName} method,
  137. * which includes the dimension information),<br> 
  138. * </li>
  139. * <li>if this <code>ArrayType</code> instance describes an array
  140. * of classes implementing the TabularData interface or the CompositeData interface,
  141. * <var>obj</var> is assignable to such a declared array,
  142. * and each element contained in <var>obj</var> is either null or a valid value for the element's open type
  143. * specified by this <code>ArrayType</code> instance.
  144. * </li></ul>
  145. * <p>
  146. *
  147. * @param obj the object to be tested.
  148. *
  149. * @return <code>true</code> if <var>obj</var> is a value for this <code>ArrayType</code> instance.
  150. */
  151. public boolean isValue(Object obj) {
  152. // if obj is null, return false
  153. //
  154. if (obj == null) {
  155. return false;
  156. }
  157. Class objClass = obj.getClass();
  158. String objClassName = objClass.getName();
  159. // if obj is not an array, return false
  160. //
  161. if ( ! objClass.isArray() ) {
  162. return false;
  163. }
  164. // Test if obj's class name is the same as for the array values that this instance describes
  165. // (this is fine if elements are of simple types, which are final classes)
  166. //
  167. if ( this.getClassName().equals(objClassName) ) {
  168. return true;
  169. }
  170. // In case this ArrayType instance describes an array of classes implementing the TabularData or CompositeData interface,
  171. // we first check for the assignability of obj to such an array of TabularData or CompositeData,
  172. // which ensures that:
  173. // . obj is of the the same dimension as this ArrayType instance,
  174. // . it is declared as an array of elements which are either all TabularData or all CompositeData.
  175. //
  176. // If the assignment check is positive,
  177. // then we have to check that each element in obj is of the same TabularType or CompositeType
  178. // as the one described by this ArrayType instance.
  179. //
  180. // [About assignment check, note that the call below returns true: ]
  181. // [Class.forName("[Lpackage.CompositeData;").isAssignableFrom(Class.forName("[Lpackage.CompositeDataImpl;)")); ]
  182. //
  183. if ( (this.elementType.getClassName().equals(TabularData.class.getName())) ||
  184. (this.elementType.getClassName().equals(CompositeData.class.getName())) ) {
  185. /* this.getClassName() is
  186. * "[Ljavax.management.openmbean.TabularData;" or the same
  187. * thing for CompositeData, either one optionally preceded
  188. * by n '[' characters. So the class is necessarily known
  189. * to the ClassLoader of ArrayType, and Class.forName is
  190. * safe. */
  191. Class targetClass;
  192. try {
  193. targetClass = Class.forName(this.getClassName());
  194. } catch (ClassNotFoundException e) { // should not happen
  195. return false;
  196. }
  197. // assignment check: return false if negative
  198. if ( ! targetClass.isAssignableFrom(objClass) ) {
  199. return false;
  200. }
  201. // check that all elements in obj are valid values for this ArrayType
  202. if ( ! checkElementsType( (Object[]) obj, this.dimension) ) { // we know obj's dimension is this.dimension
  203. return false;
  204. }
  205. return true;
  206. }
  207. // if previous tests did not return, then obj is not a value for this ArrayType instance
  208. return false;
  209. }
  210. /**
  211. * Returns true if and only if all elements contained in the array argument x_dim_Array of dimension dim
  212. * are valid values (ie either null or of the right openType)
  213. * for the element open type specified by this ArrayType instance.
  214. *
  215. * This method's implementation uses recursion to go down the dimensions of the array argument.
  216. */
  217. private boolean checkElementsType(Object[] x_dim_Array, int dim) {
  218. // if the elements of x_dim_Array are themselves array: go down recursively....
  219. if ( dim > 1 ) {
  220. for (int i=0; i<x_dim_Array.length; i++) {
  221. if ( ! checkElementsType((Object[])x_dim_Array[i], dim-1) ) {
  222. return false;
  223. }
  224. }
  225. return true;
  226. }
  227. // ...else, for a non-empty array, each element must be a valid value: either null or of the right openType
  228. else {
  229. for (int i=0; i<x_dim_Array.length; i++) {
  230. if ( (x_dim_Array[i] != null) && (! this.getElementOpenType().isValue(x_dim_Array[i])) ) {
  231. return false;
  232. }
  233. }
  234. return true;
  235. }
  236. }
  237. /* *** Methods overriden from class Object *** */
  238. /**
  239. * Compares the specified <code>obj</code> parameter with this <code>ArrayType</code> instance for equality.
  240. * <p>
  241. * Two <code>ArrayType</code> instances are equal if and only if they describes array instances
  242. * which have the same dimension and elements' open type.
  243. *
  244. * @param obj the object to be compared for equality with this <code>ArrayType</code> instance;
  245. * if <var>obj</var> is <code>null</code> or is not an instance of the class <code>ArrayType</code>,
  246. * <code>equals</code> returns <code>false</code>.
  247. *
  248. * @return <code>true</code> if the specified object is equal to this <code>ArrayType</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 an ArrayType, return false
  257. //
  258. ArrayType other;
  259. try {
  260. other = (ArrayType) obj;
  261. } catch (ClassCastException e) {
  262. return false;
  263. }
  264. // if other's dimension is different than this instance's, return false
  265. //
  266. if (other.dimension != this.dimension) {
  267. return false;
  268. }
  269. // Test if other's elementType field is the same as for this instance
  270. //
  271. return this.elementType.equals(other.elementType);
  272. }
  273. /**
  274. * Returns the hash code value for this <code>ArrayType</code> instance.
  275. * <p>
  276. * The hash code of a <code>ArrayType</code> instance is the sum of the hash codes
  277. * of all elements of information used in <code>equals</code> comparisons
  278. * (ie: dimension and elements' type).
  279. * This ensures that <code> t1.equals(t2) </code> implies that <code> t1.hashCode()==t2.hashCode() </code>
  280. * for any two <code>ArrayType</code> instances <code>t1</code> and <code>t2</code>,
  281. * as required by the general contract of the method
  282. * {@link Object#hashCode() Object.hashCode()}.
  283. * <p>
  284. * As <code>ArrayType</code> instances are immutable, the hash code for this instance is calculated once,
  285. * on the first call to <code>hashCode</code>, and then the same value is returned for subsequent calls.
  286. *
  287. * @return the hash code value for this <code>ArrayType</code> instance
  288. */
  289. public int hashCode() {
  290. // Calculate the hash code value if it has not yet been done (ie 1st call to hashCode())
  291. //
  292. if (myHashCode == null) {
  293. int value = 0;
  294. value += this.dimension;
  295. value += this.elementType.hashCode();
  296. myHashCode = new Integer(value);
  297. }
  298. // return always the same hash code for this instance (immutable)
  299. //
  300. return myHashCode.intValue();
  301. }
  302. /**
  303. * Returns a string representation of this <code>ArrayType</code> instance.
  304. * <p>
  305. * The string representation consists of
  306. * the name of this class (ie <code>javax.management.openmbean.ArrayType</code>), the type name,
  307. * the dimension and elements' type defined for this instance,
  308. * <p>
  309. * As <code>ArrayType</code> instances are immutable, the string representation for this instance is calculated once,
  310. * on the first call to <code>toString</code>, and then the same value is returned for subsequent calls.
  311. *
  312. * @return a string representation of this <code>ArrayType</code> instance
  313. */
  314. public String toString() {
  315. // Calculate the string representation if it has not yet been done (ie 1st call to toString())
  316. //
  317. if (myToString == null) {
  318. StringBuffer result = new StringBuffer();
  319. result.append(this.getClass().getName());
  320. result.append("(name=");
  321. result.append(getTypeName());
  322. result.append(",dimension=");
  323. result.append(String.valueOf(this.dimension));
  324. result.append(",elementType=");
  325. result.append(this.elementType.toString());
  326. result.append(")");
  327. myToString = result.toString();
  328. }
  329. // return always the same string representation for this instance (immutable)
  330. //
  331. return myToString;
  332. }
  333. }