1 /*
2 * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package javax.management.openmbean;
27
28 import java.io.ObjectStreamException;
29 import java.lang.reflect.Array;
30
31 /**
32 * The {@code ArrayType} class is the <i>open type</i> class whose instances describe
33 * all <i>open data</i> values which are n-dimensional arrays of <i>open data</i> values.
34 * <p>
35 * Examples of valid {@code ArrayType} instances are:
36 * <pre>{@code
37 * // 2-dimension array of java.lang.String
38 * ArrayType<String[][]> a1 = new ArrayType<String[][]>(2, SimpleType.STRING);
39 *
40 * // 1-dimension array of int
41 * ArrayType<int[]> a2 = new ArrayType<int[]>(SimpleType.INTEGER, true);
42 *
43 * // 1-dimension array of java.lang.Integer
44 * ArrayType<Integer[]> a3 = new ArrayType<Integer[]>(SimpleType.INTEGER, false);
45 *
46 * // 4-dimension array of int
47 * ArrayType<int[][][][]> a4 = new ArrayType<int[][][][]>(3, a2);
48 *
49 * // 4-dimension array of java.lang.Integer
50 * ArrayType<Integer[][][][]> a5 = new ArrayType<Integer[][][][]>(3, a3);
51 *
52 * // 1-dimension array of java.lang.String
53 * ArrayType<String[]> a6 = new ArrayType<String[]>(SimpleType.STRING, false);
54 *
55 * // 1-dimension array of long
56 * ArrayType<long[]> a7 = new ArrayType<long[]>(SimpleType.LONG, true);
57 *
58 * // 1-dimension array of java.lang.Integer
59 * ArrayType<Integer[]> a8 = ArrayType.getArrayType(SimpleType.INTEGER);
60 *
61 * // 2-dimension array of java.lang.Integer
62 * ArrayType<Integer[][]> a9 = ArrayType.getArrayType(a8);
63 *
64 * // 2-dimension array of int
65 * ArrayType<int[][]> a10 = ArrayType.getPrimitiveArrayType(int[][].class);
66 *
67 * // 3-dimension array of int
68 * ArrayType<int[][][]> a11 = ArrayType.getArrayType(a10);
69 *
70 * // 1-dimension array of float
71 * ArrayType<float[]> a12 = ArrayType.getPrimitiveArrayType(float[].class);
72 *
73 * // 2-dimension array of float
74 * ArrayType<float[][]> a13 = ArrayType.getArrayType(a12);
75 *
76 * // 1-dimension array of javax.management.ObjectName
77 * ArrayType<ObjectName[]> a14 = ArrayType.getArrayType(SimpleType.OBJECTNAME);
78 *
79 * // 2-dimension array of javax.management.ObjectName
80 * ArrayType<ObjectName[][]> a15 = ArrayType.getArrayType(a14);
81 *
82 * // 3-dimension array of java.lang.String
83 * ArrayType<String[][][]> a16 = new ArrayType<String[][][]>(3, SimpleType.STRING);
84 *
85 * // 1-dimension array of java.lang.String
86 * ArrayType<String[]> a17 = new ArrayType<String[]>(1, SimpleType.STRING);
87 *
88 * // 2-dimension array of java.lang.String
89 * ArrayType<String[][]> a18 = new ArrayType<String[][]>(1, a17);
90 *
91 * // 3-dimension array of java.lang.String
92 * ArrayType<String[][][]> a19 = new ArrayType<String[][][]>(1, a18);
93 * }</pre>
94 *
95 *
96 * @since 1.5
97 */
98 /*
99 Generification note: we could have defined a type parameter that is the
100 element type, with class ArrayType<E> extends OpenType<E[]>. However,
101 that doesn't buy us all that much. We can't say
102 public OpenType<E> getElementOpenType()
103 because this ArrayType could be a multi-dimensional array.
104 For example, if we had
105 ArrayType(2, SimpleType.INTEGER)
106 then E would have to be Integer[], while getElementOpenType() would
107 return SimpleType.INTEGER, which is an OpenType<Integer>.
108
109 Furthermore, we would like to support int[] (as well as Integer[]) as
110 an Open Type (RFE 5045358). We would want this to be an OpenType<int[]>
111 which can't be expressed as <E[]> because E can't be a primitive type
112 like int.
113 */
114 public class ArrayType<T> extends OpenType<T> {
115
116 /* Serial version */
117 static final long serialVersionUID = 720504429830309770L;
118
119 /**
120 * @serial The dimension of arrays described by this {@link ArrayType}
121 * instance.
122 */
123 private int dimension;
124
125 /**
126 * @serial The <i>open type</i> of element values contained in the arrays
127 * described by this {@link ArrayType} instance.
128 */
129 private OpenType<?> elementType;
130
131 /**
132 * @serial This flag indicates whether this {@link ArrayType}
133 * describes a primitive array.
134 *
135 * @since 1.6
136 */
137 private boolean primitiveArray;
138
139 private transient Integer myHashCode = null; // As this instance is immutable, these two values
140 private transient String myToString = null; // need only be calculated once.
141
142 // indexes refering to columns in the PRIMITIVE_ARRAY_TYPES table.
143 private static final int PRIMITIVE_WRAPPER_NAME_INDEX = 0;
144 private static final int PRIMITIVE_TYPE_NAME_INDEX = 1;
145 private static final int PRIMITIVE_TYPE_KEY_INDEX = 2;
146 private static final int PRIMITIVE_OPEN_TYPE_INDEX = 3;
147
148 private static final Object[][] PRIMITIVE_ARRAY_TYPES = {
149 { Boolean.class.getName(), boolean.class.getName(), "Z", SimpleType.BOOLEAN },
150 { Character.class.getName(), char.class.getName(), "C", SimpleType.CHARACTER },
151 { Byte.class.getName(), byte.class.getName(), "B", SimpleType.BYTE },
152 { Short.class.getName(), short.class.getName(), "S", SimpleType.SHORT },
153 { Integer.class.getName(), int.class.getName(), "I", SimpleType.INTEGER },
154 { Long.class.getName(), long.class.getName(), "J", SimpleType.LONG },
155 { Float.class.getName(), float.class.getName(), "F", SimpleType.FLOAT },
156 { Double.class.getName(), double.class.getName(), "D", SimpleType.DOUBLE }
157 };
158
159 static boolean isPrimitiveContentType(final String primitiveKey) {
160 for (Object[] typeDescr : PRIMITIVE_ARRAY_TYPES) {
161 if (typeDescr[PRIMITIVE_TYPE_KEY_INDEX].equals(primitiveKey)) {
162 return true;
163 }
164 }
165 return false;
166 }
167
168 /**
169 * Return the key used to identify the element type in
170 * arrays - e.g. "Z" for boolean, "C" for char etc...
171 * @param elementClassName the wrapper class name of the array
172 * element ("Boolean", "Character", etc...)
173 * @return the key corresponding to the given type ("Z", "C", etc...)
174 * return null if the given elementClassName is not a primitive
175 * wrapper class name.
176 **/
177 static String getPrimitiveTypeKey(String elementClassName) {
178 for (Object[] typeDescr : PRIMITIVE_ARRAY_TYPES) {
179 if (elementClassName.equals(typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX]))
180 return (String)typeDescr[PRIMITIVE_TYPE_KEY_INDEX];
181 }
182 return null;
183 }
184
185 /**
186 * Return the primitive type name corresponding to the given wrapper class.
187 * e.g. "boolean" for "Boolean", "char" for "Character" etc...
188 * @param elementClassName the type of the array element ("Boolean",
189 * "Character", etc...)
190 * @return the primitive type name corresponding to the given wrapper class
191 * ("boolean", "char", etc...)
192 * return null if the given elementClassName is not a primitive
193 * wrapper type name.
194 **/
195 static String getPrimitiveTypeName(String elementClassName) {
196 for (Object[] typeDescr : PRIMITIVE_ARRAY_TYPES) {
197 if (elementClassName.equals(typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX]))
198 return (String)typeDescr[PRIMITIVE_TYPE_NAME_INDEX];
199 }
200 return null;
201 }
202
203 /**
204 * Return the primitive open type corresponding to the given primitive type.
205 * e.g. SimpleType.BOOLEAN for "boolean", SimpleType.CHARACTER for
206 * "char", etc...
207 * @param primitiveTypeName the primitive type of the array element ("boolean",
208 * "char", etc...)
209 * @return the OpenType corresponding to the given primitive type name
210 * (SimpleType.BOOLEAN, SimpleType.CHARACTER, etc...)
211 * return null if the given elementClassName is not a primitive
212 * type name.
213 **/
214 static SimpleType<?> getPrimitiveOpenType(String primitiveTypeName) {
215 for (Object[] typeDescr : PRIMITIVE_ARRAY_TYPES) {
216 if (primitiveTypeName.equals(typeDescr[PRIMITIVE_TYPE_NAME_INDEX]))
217 return (SimpleType<?>)typeDescr[PRIMITIVE_OPEN_TYPE_INDEX];
218 }
219 return null;
220 }
221
222 /* *** Constructor *** */
223
224 /**
225 * Constructs an {@code ArrayType} instance describing <i>open data</i> values which are
226 * arrays with dimension <var>dimension</var> of elements
227 * whose <i>open type</i> is <var>elementType</var>.
228 * <p>
229 * When invoked on an {@code ArrayType} instance,
230 * the {@link OpenType#getClassName() getClassName} method
231 * returns the class name of the array instances it describes
232 * (following the rules defined by the
233 * {@link Class#getName() getName} method of {@code java.lang.Class}),
234 * not the class name of the array elements
235 * (which is returned by a call to {@code getElementOpenType().getClassName()}).
236 * <p>
237 * The internal field corresponding to the type name of this
238 * {@code ArrayType} instance is also set to
239 * the class name of the array instances it describes.
240 * In other words, the methods {@code getClassName} and
241 * {@code getTypeName} return the same string value.
242 * The internal field corresponding to the description of this
243 * {@code ArrayType} instance is set to a string value
244 * which follows the following template:
245 * <ul>
246 * <li>if non-primitive array: <code><i><dimension></i>-dimension array
247 * of <i><element_class_name></i></code></li>
248 * <li>if primitive array: <code><i><dimension></i>-dimension array
249 * of <i><primitive_type_of_the_element_class_name></i></code></li>
250 * </ul>
251 * <p>
252 * As an example, the following piece of code:
253 * <pre>{@code
254 * ArrayType<String[][][]> t = new ArrayType<String[][][]>(3, SimpleType.STRING);
255 * System.out.println("array class name = " + t.getClassName());
256 * System.out.println("element class name = " + t.getElementOpenType().getClassName());
257 * System.out.println("array type name = " + t.getTypeName());
258 * System.out.println("array type description = " + t.getDescription());
259 * }</pre>
260 * would produce the following output:
261 * <pre>{@code
262 * array class name = [[[Ljava.lang.String;
263 * element class name = java.lang.String
264 * array type name = [[[Ljava.lang.String;
265 * array type description = 3-dimension array of java.lang.String
266 * }</pre>
267 * And the following piece of code which is equivalent to the one listed
268 * above would also produce the same output:
269 * <pre>{@code
270 * ArrayType<String[]> t1 = new ArrayType<String[]>(1, SimpleType.STRING);
271 * ArrayType<String[][]> t2 = new ArrayType<String[][]>(1, t1);
272 * ArrayType<String[][][]> t3 = new ArrayType<String[][][]>(1, t2);
273 * System.out.println("array class name = " + t3.getClassName());
274 * System.out.println("element class name = " + t3.getElementOpenType().getClassName());
275 * System.out.println("array type name = " + t3.getTypeName());
276 * System.out.println("array type description = " + t3.getDescription());
277 * }</pre>
278 *
279 * @param dimension the dimension of arrays described by this {@code ArrayType} instance;
280 * must be greater than or equal to 1.
281 *
282 * @param elementType the <i>open type</i> of element values contained
283 * in the arrays described by this {@code ArrayType}
284 * instance; must be an instance of either
285 * {@code SimpleType}, {@code CompositeType},
286 * {@code TabularType} or another {@code ArrayType}
287 * with a {@code SimpleType}, {@code CompositeType}
288 * or {@code TabularType} as its {@code elementType}.
289 *
290 * @throws IllegalArgumentException if {@code dimension} is not a positive
291 * integer.
292 * @throws OpenDataException if <var>elementType's className</var> is not
293 * one of the allowed Java class names for open
294 * data.
295 */
296 public ArrayType(int dimension,
297 OpenType<?> elementType) throws OpenDataException {
298 // Check and construct state defined by parent.
299 // We can't use the package-private OpenType constructor because
300 // we don't know if the elementType parameter is sane.
301 super(buildArrayClassName(dimension, elementType),
302 buildArrayClassName(dimension, elementType),
303 buildArrayDescription(dimension, elementType));
304
305 // Check and construct state specific to ArrayType
306 //
307 if (elementType.isArray()) {
308 ArrayType<?> at = (ArrayType<?>) elementType;
309 this.dimension = at.getDimension() + dimension;
310 this.elementType = at.getElementOpenType();
311 this.primitiveArray = at.isPrimitiveArray();
312 } else {
313 this.dimension = dimension;
314 this.elementType = elementType;
315 this.primitiveArray = false;
316 }
317 }
318
319 /**
320 * Constructs a unidimensional {@code ArrayType} instance for the
321 * supplied {@code SimpleType}.
322 * <p>
323 * This constructor supports the creation of arrays of primitive
324 * types when {@code primitiveArray} is {@code true}.
325 * <p>
326 * For primitive arrays the {@link #getElementOpenType()} method
327 * returns the {@link SimpleType} corresponding to the wrapper
328 * type of the primitive type of the array.
329 * <p>
330 * When invoked on an {@code ArrayType} instance,
331 * the {@link OpenType#getClassName() getClassName} method
332 * returns the class name of the array instances it describes
333 * (following the rules defined by the
334 * {@link Class#getName() getName} method of {@code java.lang.Class}),
335 * not the class name of the array elements
336 * (which is returned by a call to {@code getElementOpenType().getClassName()}).
337 * <p>
338 * The internal field corresponding to the type name of this
339 * {@code ArrayType} instance is also set to
340 * the class name of the array instances it describes.
341 * In other words, the methods {@code getClassName} and
342 * {@code getTypeName} return the same string value.
343 * The internal field corresponding to the description
344 * of this {@code ArrayType} instance is set to a string value
345 * which follows the following template:
346 * <ul>
347 * <li>if non-primitive array: <code>1-dimension array
348 * of <i><element_class_name></i></code></li>
349 * <li>if primitive array: <code>1-dimension array
350 * of <i><primitive_type_of_the_element_class_name></i></code></li>
351 * </ul>
352 * <p>
353 * As an example, the following piece of code:
354 * <pre>{@code
355 * ArrayType<int[]> t = new ArrayType<int[]>(SimpleType.INTEGER, true);
356 * System.out.println("array class name = " + t.getClassName());
357 * System.out.println("element class name = " + t.getElementOpenType().getClassName());
358 * System.out.println("array type name = " + t.getTypeName());
359 * System.out.println("array type description = " + t.getDescription());
360 * }</pre>
361 * would produce the following output:
362 * <pre>{@code
363 * array class name = [I
364 * element class name = java.lang.Integer
365 * array type name = [I
366 * array type description = 1-dimension array of int
367 * }</pre>
368 *
369 * @param elementType the {@code SimpleType} of the element values
370 * contained in the arrays described by this
371 * {@code ArrayType} instance.
372 *
373 * @param primitiveArray {@code true} when this array describes
374 * primitive arrays.
375 *
376 * @throws IllegalArgumentException if {@code dimension} is not a positive
377 * integer.
378 * @throws OpenDataException if {@code primitiveArray} is {@code true} and
379 * {@code elementType} is not a valid {@code SimpleType} for a primitive
380 * type.
381 *
382 * @since 1.6
383 */
384 public ArrayType(SimpleType<?> elementType,
385 boolean primitiveArray) throws OpenDataException {
386
387 // Check and construct state defined by parent.
388 // We can call the package-private OpenType constructor because the
389 // set of SimpleTypes is fixed and SimpleType can't be subclassed.
390 super(buildArrayClassName(1, elementType, primitiveArray),
391 buildArrayClassName(1, elementType, primitiveArray),
392 buildArrayDescription(1, elementType, primitiveArray),
393 true);
394
395 // Check and construct state specific to ArrayType
396 //
397 this.dimension = 1;
398 this.elementType = elementType;
399 this.primitiveArray = primitiveArray;
400 }
401
402 /* Package-private constructor for callers we trust to get it right. */
403 ArrayType(String className, String typeName, String description,
404 int dimension, OpenType<?> elementType,
405 boolean primitiveArray) {
406 super(className, typeName, description, true);
407 this.dimension = dimension;
408 this.elementType = elementType;
409 this.primitiveArray = primitiveArray;
410 }
411
412 private static String buildArrayClassName(int dimension,
413 OpenType<?> elementType)
414 throws OpenDataException {
415 boolean isPrimitiveArray = false;
416 if (elementType.isArray()) {
417 isPrimitiveArray = ((ArrayType<?>) elementType).isPrimitiveArray();
418 }
419 return buildArrayClassName(dimension, elementType, isPrimitiveArray);
420 }
421
422 private static String buildArrayClassName(int dimension,
423 OpenType<?> elementType,
424 boolean isPrimitiveArray)
425 throws OpenDataException {
426 if (dimension < 1) {
427 throw new IllegalArgumentException(
428 "Value of argument dimension must be greater than 0");
429 }
430 StringBuilder result = new StringBuilder();
431 String elementClassName = elementType.getClassName();
432 // Add N (= dimension) additional '[' characters to the existing array
433 for (int i = 1; i <= dimension; i++) {
434 result.append('[');
435 }
436 if (elementType.isArray()) {
437 result.append(elementClassName);
438 } else {
439 if (isPrimitiveArray) {
440 final String key = getPrimitiveTypeKey(elementClassName);
441 // Ideally we should throw an IllegalArgumentException here,
442 // but for compatibility reasons we throw an OpenDataException.
443 // (used to be thrown by OpenType() constructor).
444 //
445 if (key == null)
446 throw new OpenDataException("Element type is not primitive: "
447 + elementClassName);
448 result.append(key);
449 } else {
450 result.append("L");
451 result.append(elementClassName);
452 result.append(';');
453 }
454 }
455 return result.toString();
456 }
457
458 private static String buildArrayDescription(int dimension,
459 OpenType<?> elementType)
460 throws OpenDataException {
461 boolean isPrimitiveArray = false;
462 if (elementType.isArray()) {
463 isPrimitiveArray = ((ArrayType<?>) elementType).isPrimitiveArray();
464 }
465 return buildArrayDescription(dimension, elementType, isPrimitiveArray);
466 }
467
468 private static String buildArrayDescription(int dimension,
469 OpenType<?> elementType,
470 boolean isPrimitiveArray)
471 throws OpenDataException {
472 if (elementType.isArray()) {
473 ArrayType<?> at = (ArrayType<?>) elementType;
474 dimension += at.getDimension();
475 elementType = at.getElementOpenType();
476 isPrimitiveArray = at.isPrimitiveArray();
477 }
478 StringBuilder result = new StringBuilder();
479 result.append(dimension).append("-dimension array of ");
480 final String elementClassName = elementType.getClassName();
481 if (isPrimitiveArray) {
482 // Convert from wrapper type to primitive type
483 final String primitiveType =
484 getPrimitiveTypeName(elementClassName);
485
486 // Ideally we should throw an IllegalArgumentException here,
487 // but for compatibility reasons we throw an OpenDataException.
488 // (used to be thrown by OpenType() constructor).
489 //
490 if (primitiveType == null)
491 throw new OpenDataException("Element is not a primitive type: "+
492 elementClassName);
493 result.append(primitiveType);
494 } else {
495 result.append(elementClassName);
496 }
497 return result.toString();
498 }
499
500 /* *** ArrayType specific information methods *** */
501
502 /**
503 * Returns the dimension of arrays described by this {@code ArrayType} instance.
504 *
505 * @return the dimension.
506 */
507 public int getDimension() {
508
509 return dimension;
510 }
511
512 /**
513 * Returns the <i>open type</i> of element values contained
514 * in the arrays described by this {@code ArrayType} instance.
515 *
516 * @return the element type.
517 */
518 public OpenType<?> getElementOpenType() {
519
520 return elementType;
521 }
522
523 /**
524 * Returns {@code true} if the open data values this open
525 * type describes are primitive arrays, {@code false} otherwise.
526 *
527 * @return true if this is a primitive array type.
528 *
529 * @since 1.6
530 */
531 public boolean isPrimitiveArray() {
532
533 return primitiveArray;
534 }
535
536 /**
537 * Tests whether <var>obj</var> is a value for this {@code ArrayType}
538 * instance.
539 * <p>
540 * This method returns {@code true} if and only if <var>obj</var>
541 * is not null, <var>obj</var> is an array and any one of the following
542 * is {@code true}:
543 *
544 * <ul>
545 * <li>if this {@code ArrayType} instance describes an array of
546 * {@code SimpleType} elements or their corresponding primitive types,
547 * <var>obj</var>'s class name is the same as the className field defined
548 * for this {@code ArrayType} instance (i.e. the class name returned
549 * by the {@link OpenType#getClassName() getClassName} method, which
550 * includes the dimension information),<br> </li>
551 * <li>if this {@code ArrayType} instance describes an array of
552 * classes implementing the {@code TabularData} interface or the
553 * {@code CompositeData} interface, <var>obj</var> is assignable to
554 * such a declared array, and each element contained in {<var>obj</var>
555 * is either null or a valid value for the element's open type specified
556 * by this {@code ArrayType} instance.</li>
557 * </ul>
558 *
559 * @param obj the object to be tested.
560 *
561 * @return {@code true} if <var>obj</var> is a value for this
562 * {@code ArrayType} instance.
563 */
564 public boolean isValue(Object obj) {
565
566 // if obj is null, return false
567 //
568 if (obj == null) {
569 return false;
570 }
571
572 Class<?> objClass = obj.getClass();
573 String objClassName = objClass.getName();
574
575 // if obj is not an array, return false
576 //
577 if ( ! objClass.isArray() ) {
578 return false;
579 }
580
581 // Test if obj's class name is the same as for the array values that this instance describes
582 // (this is fine if elements are of simple types, which are final classes)
583 //
584 if ( this.getClassName().equals(objClassName) ) {
585 return true;
586 }
587
588 // In case this ArrayType instance describes an array of classes implementing the TabularData or CompositeData interface,
589 // we first check for the assignability of obj to such an array of TabularData or CompositeData,
590 // which ensures that:
591 // . obj is of the same dimension as this ArrayType instance,
592 // . it is declared as an array of elements which are either all TabularData or all CompositeData.
593 //
594 // If the assignment check is positive,
595 // then we have to check that each element in obj is of the same TabularType or CompositeType
596 // as the one described by this ArrayType instance.
597 //
598 // [About assignment check, note that the call below returns true: ]
599 // [Class.forName("[Lpackage.CompositeData;").isAssignableFrom(Class.forName("[Lpackage.CompositeDataImpl;)")); ]
600 //
601 if ( (this.elementType.getClassName().equals(TabularData.class.getName())) ||
602 (this.elementType.getClassName().equals(CompositeData.class.getName())) ) {
603
604 boolean isTabular =
605 (elementType.getClassName().equals(TabularData.class.getName()));
606 int[] dims = new int[getDimension()];
607 Class<?> elementClass = isTabular ? TabularData.class : CompositeData.class;
608 Class<?> targetClass = Array.newInstance(elementClass, dims).getClass();
609
610 // assignment check: return false if negative
611 if ( ! targetClass.isAssignableFrom(objClass) ) {
612 return false;
613 }
614
615 // check that all elements in obj are valid values for this ArrayType
616 if ( ! checkElementsType( (Object[]) obj, this.dimension) ) { // we know obj's dimension is this.dimension
617 return false;
618 }
619
620 return true;
621 }
622
623 // if previous tests did not return, then obj is not a value for this ArrayType instance
624 return false;
625 }
626
627 /**
628 * Returns true if and only if all elements contained in the array argument x_dim_Array of dimension dim
629 * are valid values (ie either null or of the right openType)
630 * for the element open type specified by this ArrayType instance.
631 *
632 * This method's implementation uses recursion to go down the dimensions of the array argument.
633 */
634 private boolean checkElementsType(Object[] x_dim_Array, int dim) {
635
636 // if the elements of x_dim_Array are themselves array: go down recursively....
637 if ( dim > 1 ) {
638 for (int i=0; i<x_dim_Array.length; i++) {
639 if ( ! checkElementsType((Object[])x_dim_Array[i], dim-1) ) {
640 return false;
641 }
642 }
643 return true;
644 }
645 // ...else, for a non-empty array, each element must be a valid value: either null or of the right openType
646 else {
647 for (int i=0; i<x_dim_Array.length; i++) {
648 if ( (x_dim_Array[i] != null) && (! this.getElementOpenType().isValue(x_dim_Array[i])) ) {
649 return false;
650 }
651 }
652 return true;
653 }
654 }
655
656 @Override
657 boolean isAssignableFrom(OpenType<?> ot) {
658 if (!(ot instanceof ArrayType<?>))
659 return false;
660 ArrayType<?> at = (ArrayType<?>) ot;
661 return (at.getDimension() == getDimension() &&
662 at.isPrimitiveArray() == isPrimitiveArray() &&
663 at.getElementOpenType().isAssignableFrom(getElementOpenType()));
664 }
665
666
667 /* *** Methods overriden from class Object *** */
668
669 /**
670 * Compares the specified {@code obj} parameter with this
671 * {@code ArrayType} instance for equality.
672 * <p>
673 * Two {@code ArrayType} instances are equal if and only if they
674 * describe array instances which have the same dimension, elements'
675 * open type and primitive array flag.
676 *
677 * @param obj the object to be compared for equality with this
678 * {@code ArrayType} instance; if <var>obj</var>
679 * is {@code null} or is not an instance of the
680 * class {@code ArrayType} this method returns
681 * {@code false}.
682 *
683 * @return {@code true} if the specified object is equal to
684 * this {@code ArrayType} instance.
685 */
686 public boolean equals(Object obj) {
687
688 // if obj is null, return false
689 //
690 if (obj == null) {
691 return false;
692 }
693
694 // if obj is not an ArrayType, return false
695 //
696 if (!(obj instanceof ArrayType<?>))
697 return false;
698 ArrayType<?> other = (ArrayType<?>) obj;
699
700 // if other's dimension is different than this instance's, return false
701 //
702 if (this.dimension != other.dimension) {
703 return false;
704 }
705
706 // Test if other's elementType field is the same as for this instance
707 //
708 if (!this.elementType.equals(other.elementType)) {
709 return false;
710 }
711
712 // Test if other's primitiveArray flag is the same as for this instance
713 //
714 return this.primitiveArray == other.primitiveArray;
715 }
716
717 /**
718 * Returns the hash code value for this {@code ArrayType} instance.
719 * <p>
720 * The hash code of an {@code ArrayType} instance is the sum of the
721 * hash codes of all the elements of information used in {@code equals}
722 * comparisons (i.e. dimension, elements' open type and primitive array flag).
723 * The hashcode for a primitive value is the hashcode of the corresponding boxed
724 * object (e.g. the hashcode for {@code true} is {@code Boolean.TRUE.hashCode()}).
725 * This ensures that {@code t1.equals(t2)} implies that
726 * {@code t1.hashCode()==t2.hashCode()} for any two
727 * {@code ArrayType} instances {@code t1} and {@code t2},
728 * as required by the general contract of the method
729 * {@link Object#hashCode() Object.hashCode()}.
730 * <p>
731 * As {@code ArrayType} instances are immutable, the hash
732 * code for this instance is calculated once, on the first call
733 * to {@code hashCode}, and then the same value is returned
734 * for subsequent calls.
735 *
736 * @return the hash code value for this {@code ArrayType} instance
737 */
738 public int hashCode() {
739
740 // Calculate the hash code value if it has not yet been done (ie 1st call to hashCode())
741 //
742 if (myHashCode == null) {
743 int value = 0;
744 value += dimension;
745 value += elementType.hashCode();
746 value += Boolean.valueOf(primitiveArray).hashCode();
747 myHashCode = Integer.valueOf(value);
748 }
749
750 // return always the same hash code for this instance (immutable)
751 //
752 return myHashCode.intValue();
753 }
754
755 /**
756 * Returns a string representation of this {@code ArrayType} instance.
757 * <p>
758 * The string representation consists of the name of this class (i.e.
759 * {@code javax.management.openmbean.ArrayType}), the type name,
760 * the dimension, the elements' open type and the primitive array flag
761 * defined for this instance.
762 * <p>
763 * As {@code ArrayType} instances are immutable, the
764 * string representation for this instance is calculated
765 * once, on the first call to {@code toString}, and
766 * then the same value is returned for subsequent calls.
767 *
768 * @return a string representation of this {@code ArrayType} instance
769 */
770 public String toString() {
771
772 // Calculate the string representation if it has not yet been done (ie 1st call to toString())
773 //
774 if (myToString == null) {
775 myToString = getClass().getName() +
776 "(name=" + getTypeName() +
777 ",dimension=" + dimension +
778 ",elementType=" + elementType +
779 ",primitiveArray=" + primitiveArray + ")";
780 }
781
782 // return always the same string representation for this instance (immutable)
783 //
784 return myToString;
785 }
786
787 /**
788 * Create an {@code ArrayType} instance in a type-safe manner.
789 * <p>
790 * Multidimensional arrays can be built up by calling this method as many
791 * times as necessary.
792 * <p>
793 * Calling this method twice with the same parameters may return the same
794 * object or two equal but not identical objects.
795 * <p>
796 * As an example, the following piece of code:
797 * <pre>{@code
798 * ArrayType<String[]> t1 = ArrayType.getArrayType(SimpleType.STRING);
799 * ArrayType<String[][]> t2 = ArrayType.getArrayType(t1);
800 * ArrayType<String[][][]> t3 = ArrayType.getArrayType(t2);
801 * System.out.println("array class name = " + t3.getClassName());
802 * System.out.println("element class name = " + t3.getElementOpenType().getClassName());
803 * System.out.println("array type name = " + t3.getTypeName());
804 * System.out.println("array type description = " + t3.getDescription());
805 * }</pre>
806 * would produce the following output:
807 * <pre>{@code
808 * array class name = [[[Ljava.lang.String;
809 * element class name = java.lang.String
810 * array type name = [[[Ljava.lang.String;
811 * array type description = 3-dimension array of java.lang.String
812 * }</pre>
813 *
814 * @param <E> the Java type that described instances must have
815 * @param elementType the <i>open type</i> of element values contained
816 * in the arrays described by this {@code ArrayType}
817 * instance; must be an instance of either
818 * {@code SimpleType}, {@code CompositeType},
819 * {@code TabularType} or another {@code ArrayType}
820 * with a {@code SimpleType}, {@code CompositeType}
821 * or {@code TabularType} as its {@code elementType}.
822 * @return an {@code ArrayType} instance
823 * @throws OpenDataException if <var>elementType's className</var> is not
824 * one of the allowed Java class names for open
825 * data.
826 *
827 * @since 1.6
828 */
829 public static <E> ArrayType<E[]> getArrayType(OpenType<E> elementType)
830 throws OpenDataException {
831 return new ArrayType<E[]>(1, elementType);
832 }
833
834 /**
835 * Create an {@code ArrayType} instance in a type-safe manner.
836 * <p>
837 * Calling this method twice with the same parameters may return the
838 * same object or two equal but not identical objects.
839 * <p>
840 * As an example, the following piece of code:
841 * <pre>{@code
842 * ArrayType<int[][][]> t = ArrayType.getPrimitiveArrayType(int[][][].class);
843 * System.out.println("array class name = " + t.getClassName());
844 * System.out.println("element class name = " + t.getElementOpenType().getClassName());
845 * System.out.println("array type name = " + t.getTypeName());
846 * System.out.println("array type description = " + t.getDescription());
847 * }</pre>
848 * would produce the following output:
849 * <pre>{@code
850 * array class name = [[[I
851 * element class name = java.lang.Integer
852 * array type name = [[[I
853 * array type description = 3-dimension array of int
854 * }</pre>
855 *
856 * @param <T> the Java type that described instances must have
857 * @param arrayClass a primitive array class such as {@code int[].class},
858 * {@code boolean[][].class}, etc. The {@link
859 * #getElementOpenType()} method of the returned
860 * {@code ArrayType} returns the {@link SimpleType}
861 * corresponding to the wrapper type of the primitive
862 * type of the array.
863 * @return an {@code ArrayType} instance
864 *
865 * @throws IllegalArgumentException if <var>arrayClass</var> is not
866 * a primitive array.
867 *
868 * @since 1.6
869 */
870 @SuppressWarnings("unchecked") // can't get appropriate T for primitive array
871 public static <T> ArrayType<T> getPrimitiveArrayType(Class<T> arrayClass) {
872 // Check if the supplied parameter is an array
873 //
874 if (!arrayClass.isArray()) {
875 throw new IllegalArgumentException("arrayClass must be an array");
876 }
877
878 // Calculate array dimension and component type name
879 //
880 int n = 1;
881 Class<?> componentType = arrayClass.getComponentType();
882 while (componentType.isArray()) {
883 n++;
884 componentType = componentType.getComponentType();
885 }
886 String componentTypeName = componentType.getName();
887
888 // Check if the array's component type is a primitive type
889 //
890 if (!componentType.isPrimitive()) {
891 throw new IllegalArgumentException(
892 "component type of the array must be a primitive type");
893 }
894
895 // Map component type name to corresponding SimpleType
896 //
897 final SimpleType<?> simpleType =
898 getPrimitiveOpenType(componentTypeName);
899
900 // Build primitive array
901 //
902 try {
903 @SuppressWarnings("rawtypes")
904 ArrayType at = new ArrayType(simpleType, true);
905 if (n > 1)
906 at = new ArrayType<T>(n - 1, at);
907 return at;
908 } catch (OpenDataException e) {
909 throw new IllegalArgumentException(e); // should not happen
910 }
911 }
912
913 /**
914 * Replace/resolve the object read from the stream before it is returned
915 * to the caller.
916 *
917 * @serialData The new serial form of this class defines a new serializable
918 * {@code boolean} field {@code primitiveArray}. In order to guarantee the
919 * interoperability with previous versions of this class the new serial
920 * form must continue to refer to primitive wrapper types even when the
921 * {@code ArrayType} instance describes a primitive type array. So when
922 * {@code primitiveArray} is {@code true} the {@code className},
923 * {@code typeName} and {@code description} serializable fields
924 * are converted into primitive types before the deserialized
925 * {@code ArrayType} instance is return to the caller. The
926 * {@code elementType} field always returns the {@code SimpleType}
927 * corresponding to the primitive wrapper type of the array's
928 * primitive type.
929 * <p>
930 * Therefore the following serializable fields are deserialized as follows:
931 * <ul>
932 * <li>if {@code primitiveArray} is {@code true} the {@code className}
933 * field is deserialized by replacing the array's component primitive
934 * wrapper type by the corresponding array's component primitive type,
935 * e.g. {@code "[[Ljava.lang.Integer;"} will be deserialized as
936 * {@code "[[I"}.</li>
937 * <li>if {@code primitiveArray} is {@code true} the {@code typeName}
938 * field is deserialized by replacing the array's component primitive
939 * wrapper type by the corresponding array's component primitive type,
940 * e.g. {@code "[[Ljava.lang.Integer;"} will be deserialized as
941 * {@code "[[I"}.</li>
942 * <li>if {@code primitiveArray} is {@code true} the {@code description}
943 * field is deserialized by replacing the array's component primitive
944 * wrapper type by the corresponding array's component primitive type,
945 * e.g. {@code "2-dimension array of java.lang.Integer"} will be
946 * deserialized as {@code "2-dimension array of int"}.</li>
947 * </ul>
948 *
949 * @since 1.6
950 */
951 private Object readResolve() throws ObjectStreamException {
952 if (primitiveArray) {
953 return convertFromWrapperToPrimitiveTypes();
954 } else {
955 return this;
956 }
957 }
958
959 private <T> ArrayType<T> convertFromWrapperToPrimitiveTypes() {
960 String cn = getClassName();
961 String tn = getTypeName();
962 String d = getDescription();
963 for (Object[] typeDescr : PRIMITIVE_ARRAY_TYPES) {
964 if (cn.indexOf((String)typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX]) != -1) {
965 cn = cn.replaceFirst(
966 "L" + typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX] + ";",
967 (String) typeDescr[PRIMITIVE_TYPE_KEY_INDEX]);
968 tn = tn.replaceFirst(
969 "L" + typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX] + ";",
970 (String) typeDescr[PRIMITIVE_TYPE_KEY_INDEX]);
971 d = d.replaceFirst(
972 (String) typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX],
973 (String) typeDescr[PRIMITIVE_TYPE_NAME_INDEX]);
974 break;
975 }
976 }
977 return new ArrayType<T>(cn, tn, d,
978 dimension, elementType, primitiveArray);
979 }
980
981 /**
982 * Nominate a replacement for this object in the stream before the object
983 * is written.
984 *
985 * @serialData The new serial form of this class defines a new serializable
986 * {@code boolean} field {@code primitiveArray}. In order to guarantee the
987 * interoperability with previous versions of this class the new serial
988 * form must continue to refer to primitive wrapper types even when the
989 * {@code ArrayType} instance describes a primitive type array. So when
990 * {@code primitiveArray} is {@code true} the {@code className},
991 * {@code typeName} and {@code description} serializable fields
992 * are converted into wrapper types before the serialized
993 * {@code ArrayType} instance is written to the stream. The
994 * {@code elementType} field always returns the {@code SimpleType}
995 * corresponding to the primitive wrapper type of the array's
996 * primitive type.
997 * <p>
998 * Therefore the following serializable fields are serialized as follows:
999 * <ul>
1000 * <li>if {@code primitiveArray} is {@code true} the {@code className}
1001 * field is serialized by replacing the array's component primitive
1002 * type by the corresponding array's component primitive wrapper type,
1003 * e.g. {@code "[[I"} will be serialized as
1004 * {@code "[[Ljava.lang.Integer;"}.</li>
1005 * <li>if {@code primitiveArray} is {@code true} the {@code typeName}
1006 * field is serialized by replacing the array's component primitive
1007 * type by the corresponding array's component primitive wrapper type,
1008 * e.g. {@code "[[I"} will be serialized as
1009 * {@code "[[Ljava.lang.Integer;"}.</li>
1010 * <li>if {@code primitiveArray} is {@code true} the {@code description}
1011 * field is serialized by replacing the array's component primitive
1012 * type by the corresponding array's component primitive wrapper type,
1013 * e.g. {@code "2-dimension array of int"} will be serialized as
1014 * {@code "2-dimension array of java.lang.Integer"}.</li>
1015 * </ul>
1016 *
1017 * @since 1.6
1018 */
1019 private Object writeReplace() throws ObjectStreamException {
1020 if (primitiveArray) {
1021 return convertFromPrimitiveToWrapperTypes();
1022 } else {
1023 return this;
1024 }
1025 }
1026
1027 private <T> ArrayType<T> convertFromPrimitiveToWrapperTypes() {
1028 String cn = getClassName();
1029 String tn = getTypeName();
1030 String d = getDescription();
1031 for (Object[] typeDescr : PRIMITIVE_ARRAY_TYPES) {
1032 if (cn.indexOf((String) typeDescr[PRIMITIVE_TYPE_KEY_INDEX]) != -1) {
1033 cn = cn.replaceFirst(
1034 (String) typeDescr[PRIMITIVE_TYPE_KEY_INDEX],
1035 "L" + typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX] + ";");
1036 tn = tn.replaceFirst(
1037 (String) typeDescr[PRIMITIVE_TYPE_KEY_INDEX],
1038 "L" + typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX] + ";");
1039 d = d.replaceFirst(
1040 (String) typeDescr[PRIMITIVE_TYPE_NAME_INDEX],
1041 (String) typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX]);
1042 break;
1043 }
1044 }
1045 return new ArrayType<T>(cn, tn, d,
1046 dimension, elementType, primitiveArray);
1047 }
1048 }
1049