0001 /*
0002 * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved.
0003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004 *
0005 * This code is free software; you can redistribute it and/or modify it
0006 * under the terms of the GNU General Public License version 2 only, as
0007 * published by the Free Software Foundation. Sun designates this
0008 * particular file as subject to the "Classpath" exception as provided
0009 * by Sun in the LICENSE file that accompanied this code.
0010 *
0011 * This code is distributed in the hope that it will be useful, but WITHOUT
0012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014 * version 2 for more details (a copy is included in the LICENSE file that
0015 * accompanied this code).
0016 *
0017 * You should have received a copy of the GNU General Public License version
0018 * 2 along with this work; if not, write to the Free Software Foundation,
0019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020 *
0021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022 * CA 95054 USA or visit www.sun.com if you need additional information or
0023 * have any questions.
0024 */
0025
0026 package javax.management.openmbean;
0027
0028 import java.io.ObjectStreamException;
0029 import java.lang.reflect.Array;
0030
0031 /**
0032 * The <code>ArrayType</code> class is the <i>open type</i> class whose instances describe
0033 * all <i>open data</i> values which are n-dimensional arrays of <i>open data</i> values.
0034 * <p>
0035 * Examples of valid {@code ArrayType} instances are:
0036 * <pre>
0037 * // 2-dimension array of java.lang.String
0038 * ArrayType<String[][]> a1 = new ArrayType<String[][]>(2, SimpleType.STRING);
0039 *
0040 * // 1-dimension array of int
0041 * ArrayType<int[]> a2 = new ArrayType<int[]>(SimpleType.INTEGER, true);
0042 *
0043 * // 1-dimension array of java.lang.Integer
0044 * ArrayType<Integer[]> a3 = new ArrayType<Integer[]>(SimpleType.INTEGER, false);
0045 *
0046 * // 4-dimension array of int
0047 * ArrayType<int[][][][]> a4 = new ArrayType<int[][][][]>(3, a2);
0048 *
0049 * // 4-dimension array of java.lang.Integer
0050 * ArrayType<Integer[][][][]> a5 = new ArrayType<Integer[][][][]>(3, a3);
0051 *
0052 * // 1-dimension array of java.lang.String
0053 * ArrayType<String[]> a6 = new ArrayType<String[]>(SimpleType.STRING, false);
0054 *
0055 * // 1-dimension array of long
0056 * ArrayType<long[]> a7 = new ArrayType<long[]>(SimpleType.LONG, true);
0057 *
0058 * // 1-dimension array of java.lang.Integer
0059 * ArrayType<Integer[]> a8 = ArrayType.getArrayType(SimpleType.INTEGER);
0060 *
0061 * // 2-dimension array of java.lang.Integer
0062 * ArrayType<Integer[][]> a9 = ArrayType.getArrayType(a8);
0063 *
0064 * // 2-dimension array of int
0065 * ArrayType<int[][]> a10 = ArrayType.getPrimitiveArrayType(int[][].class);
0066 *
0067 * // 3-dimension array of int
0068 * ArrayType<int[][][]> a11 = ArrayType.getArrayType(a10);
0069 *
0070 * // 1-dimension array of float
0071 * ArrayType<float[]> a12 = ArrayType.getPrimitiveArrayType(float[].class);
0072 *
0073 * // 2-dimension array of float
0074 * ArrayType<float[][]> a13 = ArrayType.getArrayType(a12);
0075 *
0076 * // 1-dimension array of javax.management.ObjectName
0077 * ArrayType<ObjectName[]> a14 = ArrayType.getArrayType(SimpleType.OBJECTNAME);
0078 *
0079 * // 2-dimension array of javax.management.ObjectName
0080 * ArrayType<ObjectName[][]> a15 = ArrayType.getArrayType(a14);
0081 *
0082 * // 3-dimension array of java.lang.String
0083 * ArrayType<String[][][]> a16 = new ArrayType<String[][][]>(3, SimpleType.STRING);
0084 *
0085 * // 1-dimension array of java.lang.String
0086 * ArrayType<String[]> a17 = new ArrayType<String[]>(1, SimpleType.STRING);
0087 *
0088 * // 2-dimension array of java.lang.String
0089 * ArrayType<String[][]> a18 = new ArrayType<String[][]>(1, a17);
0090 *
0091 * // 3-dimension array of java.lang.String
0092 * ArrayType<String[][][]> a19 = new ArrayType<String[][][]>(1, a18);
0093 * </pre>
0094 *
0095 *
0096 * @since 1.5
0097 */
0098 /*
0099 Generification note: we could have defined a type parameter that is the
0100 element type, with class ArrayType<E> extends OpenType<E[]>. However,
0101 that doesn't buy us all that much. We can't say
0102 public OpenType<E> getElementOpenType()
0103 because this ArrayType could be a multi-dimensional array.
0104 For example, if we had
0105 ArrayType(2, SimpleType.INTEGER)
0106 then E would have to be Integer[], while getElementOpenType() would
0107 return SimpleType.INTEGER, which is an OpenType<Integer>.
0108
0109 Furthermore, we would like to support int[] (as well as Integer[]) as
0110 an Open Type (RFE 5045358). We would want this to be an OpenType<int[]>
0111 which can't be expressed as <E[]> because E can't be a primitive type
0112 like int.
0113 */
0114 public class ArrayType<T> extends OpenType<T> {
0115
0116 /* Serial version */
0117 static final long serialVersionUID = 720504429830309770L;
0118
0119 /**
0120 * @serial The dimension of arrays described by this {@link ArrayType}
0121 * instance.
0122 */
0123 private int dimension;
0124
0125 /**
0126 * @serial The <i>open type</i> of element values contained in the arrays
0127 * described by this {@link ArrayType} instance.
0128 */
0129 private OpenType<?> elementType;
0130
0131 /**
0132 * @serial This flag indicates whether this {@link ArrayType}
0133 * describes a primitive array.
0134 *
0135 * @since 1.6
0136 */
0137 private boolean primitiveArray;
0138
0139 private transient Integer myHashCode = null; // As this instance is immutable, these two values
0140 private transient String myToString = null; // need only be calculated once.
0141
0142 // indexes refering to columns in the PRIMITIVE_ARRAY_TYPES table.
0143 private static final int PRIMITIVE_WRAPPER_NAME_INDEX = 0;
0144 private static final int PRIMITIVE_TYPE_NAME_INDEX = 1;
0145 private static final int PRIMITIVE_TYPE_KEY_INDEX = 2;
0146 private static final int PRIMITIVE_OPEN_TYPE_INDEX = 3;
0147
0148 private static final Object[][] PRIMITIVE_ARRAY_TYPES = {
0149 { Boolean.class.getName(), boolean.class.getName(), "Z",
0150 SimpleType.BOOLEAN },
0151 { Character.class.getName(), char.class.getName(), "C",
0152 SimpleType.CHARACTER },
0153 { Byte.class.getName(), byte.class.getName(), "B",
0154 SimpleType.BYTE },
0155 { Short.class.getName(), short.class.getName(), "S",
0156 SimpleType.SHORT },
0157 { Integer.class.getName(), int.class.getName(), "I",
0158 SimpleType.INTEGER },
0159 { Long.class.getName(), long.class.getName(), "J",
0160 SimpleType.LONG },
0161 { Float.class.getName(), float.class.getName(), "F",
0162 SimpleType.FLOAT },
0163 { Double.class.getName(), double.class.getName(), "D",
0164 SimpleType.DOUBLE } };
0165
0166 static boolean isPrimitiveContentType(final String primitiveKey) {
0167 for (Object[] typeDescr : PRIMITIVE_ARRAY_TYPES) {
0168 if (typeDescr[PRIMITIVE_TYPE_KEY_INDEX]
0169 .equals(primitiveKey)) {
0170 return true;
0171 }
0172 }
0173 return false;
0174 }
0175
0176 /**
0177 * Return the key used to identify the element type in
0178 * arrays - e.g. "Z" for boolean, "C" for char etc...
0179 * @param elementClassName the wrapper class name of the array
0180 * element ("Boolean", "Character", etc...)
0181 * @return the key corresponding to the given type ("Z", "C", etc...)
0182 * return null if the given elementClassName is not a primitive
0183 * wrapper class name.
0184 **/
0185 static String getPrimitiveTypeKey(String elementClassName) {
0186 for (Object[] typeDescr : PRIMITIVE_ARRAY_TYPES) {
0187 if (elementClassName
0188 .equals(typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX]))
0189 return (String) typeDescr[PRIMITIVE_TYPE_KEY_INDEX];
0190 }
0191 return null;
0192 }
0193
0194 /**
0195 * Return the primitive type name corresponding to the given wrapper class.
0196 * e.g. "boolean" for "Boolean", "char" for "Character" etc...
0197 * @param elementClassName the type of the array element ("Boolean",
0198 * "Character", etc...)
0199 * @return the primitive type name corresponding to the given wrapper class
0200 * ("boolean", "char", etc...)
0201 * return null if the given elementClassName is not a primitive
0202 * wrapper type name.
0203 **/
0204 static String getPrimitiveTypeName(String elementClassName) {
0205 for (Object[] typeDescr : PRIMITIVE_ARRAY_TYPES) {
0206 if (elementClassName
0207 .equals(typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX]))
0208 return (String) typeDescr[PRIMITIVE_TYPE_NAME_INDEX];
0209 }
0210 return null;
0211 }
0212
0213 /**
0214 * Return the primitive open type corresponding to the given primitive type.
0215 * e.g. SimpleType.BOOLEAN for "boolean", SimpleType.CHARACTER for
0216 * "char", etc...
0217 * @param primitiveTypeName the primitive type of the array element ("boolean",
0218 * "char", etc...)
0219 * @return the OpenType corresponding to the given primitive type name
0220 * (SimpleType.BOOLEAN, SimpleType.CHARACTER, etc...)
0221 * return null if the given elementClassName is not a primitive
0222 * type name.
0223 **/
0224 static SimpleType<?> getPrimitiveOpenType(String primitiveTypeName) {
0225 for (Object[] typeDescr : PRIMITIVE_ARRAY_TYPES) {
0226 if (primitiveTypeName
0227 .equals(typeDescr[PRIMITIVE_TYPE_NAME_INDEX]))
0228 return (SimpleType<?>) typeDescr[PRIMITIVE_OPEN_TYPE_INDEX];
0229 }
0230 return null;
0231 }
0232
0233 /* *** Constructor *** */
0234
0235 /**
0236 * Constructs an <tt>ArrayType</tt> instance describing <i>open data</i> values which are
0237 * arrays with dimension <var>dimension</var> of elements whose <i>open type</i> is <var>elementType</var>.
0238 * <p>
0239 * When invoked on an <tt>ArrayType</tt> instance, the {@link OpenType#getClassName() getClassName} method
0240 * returns the class name of the array instances it describes (following the rules defined by the
0241 * {@link Class#getName() getName} method of <code>java.lang.Class</code>), not the class name of the array elements
0242 * (which is returned by a call to <tt>getElementOpenType().getClassName()</tt>).
0243 * <p>
0244 * The internal field corresponding to the type name of this <code>ArrayType</code> instance is also set to
0245 * the class name of the array instances it describes.
0246 * In other words, the methods <code>getClassName</code> and <code>getTypeName</code> return the same string value.
0247 * The internal field corresponding to the description of this <code>ArrayType</code> instance is set to a string value
0248 * which follows the following template:
0249 * <ul>
0250 * <li>if non-primitive array: <tt><i><dimension></i>-dimension array of <i><element_class_name></i></tt></li>
0251 * <li>if primitive array: <tt><i><dimension></i>-dimension array of <i><primitive_type_of_the_element_class_name></i></tt></li>
0252 * </ul>
0253 * <p>
0254 * As an example, the following piece of code:
0255 * <pre>
0256 * ArrayType<String[][][]> t = new ArrayType<String[][][]>(3, SimpleType.STRING);
0257 * System.out.println("array class name = " + t.getClassName());
0258 * System.out.println("element class name = " + t.getElementOpenType().getClassName());
0259 * System.out.println("array type name = " + t.getTypeName());
0260 * System.out.println("array type description = " + t.getDescription());
0261 * </pre>
0262 * would produce the following output:
0263 * <pre>
0264 * array class name = [[[Ljava.lang.String;
0265 * element class name = java.lang.String
0266 * array type name = [[[Ljava.lang.String;
0267 * array type description = 3-dimension array of java.lang.String
0268 * </pre>
0269 * And the following piece of code which is equivalent to the one listed
0270 * above would also produce the same output:
0271 * <pre>
0272 * ArrayType<String[]> t1 = new ArrayType<String[]>(1, SimpleType.STRING);
0273 * ArrayType<String[][]> t2 = new ArrayType<String[][]>(1, t1);
0274 * ArrayType<String[][][]> t3 = new ArrayType<String[][][]>(1, t2);
0275 * System.out.println("array class name = " + t3.getClassName());
0276 * System.out.println("element class name = " + t3.getElementOpenType().getClassName());
0277 * System.out.println("array type name = " + t3.getTypeName());
0278 * System.out.println("array type description = " + t3.getDescription());
0279 * </pre>
0280 *
0281 * @param dimension the dimension of arrays described by this <tt>ArrayType</tt> instance;
0282 * must be greater than or equal to 1.
0283 *
0284 * @param elementType the <i>open type</i> of element values contained
0285 * in the arrays described by this <tt>ArrayType</tt>
0286 * instance; must be an instance of either
0287 * <tt>SimpleType</tt>, <tt>CompositeType</tt>,
0288 * <tt>TabularType</tt> or another <tt>ArrayType</tt>
0289 * with a <tt>SimpleType</tt>, <tt>CompositeType</tt>
0290 * or <tt>TabularType</tt> as its <tt>elementType</tt>.
0291 *
0292 * @throws IllegalArgumentException if {@code dimension} is not a positive
0293 * integer.
0294 * @throws OpenDataException if <var>elementType's className</var> is not
0295 * one of the allowed Java class names for open
0296 * data.
0297 */
0298 public ArrayType(int dimension, OpenType<?> elementType)
0299 throws OpenDataException {
0300 // Check and construct state defined by parent.
0301 // We can't use the package-private OpenType constructor because
0302 // we don't know if the elementType parameter is sane.
0303 super (buildArrayClassName(dimension, elementType),
0304 buildArrayClassName(dimension, elementType),
0305 buildArrayDescription(dimension, elementType));
0306
0307 // Check and construct state specific to ArrayType
0308 //
0309 if (elementType.isArray()) {
0310 ArrayType at = (ArrayType) elementType;
0311 this .dimension = at.getDimension() + dimension;
0312 this .elementType = at.getElementOpenType();
0313 this .primitiveArray = at.isPrimitiveArray();
0314 } else {
0315 this .dimension = dimension;
0316 this .elementType = elementType;
0317 this .primitiveArray = false;
0318 }
0319 }
0320
0321 /**
0322 * Constructs a unidimensional {@code ArrayType} instance for the
0323 * supplied {@code SimpleType}.
0324 * <p>
0325 * This constructor supports the creation of arrays of primitive
0326 * types when {@code primitiveArray} is {@code true}.
0327 * <p>
0328 * For primitive arrays the {@link #getElementOpenType()} method
0329 * returns the {@link SimpleType} corresponding to the wrapper
0330 * type of the primitive type of the array.
0331 * <p>
0332 * When invoked on an <tt>ArrayType</tt> instance, the {@link OpenType#getClassName() getClassName} method
0333 * returns the class name of the array instances it describes (following the rules defined by the
0334 * {@link Class#getName() getName} method of <code>java.lang.Class</code>), not the class name of the array elements
0335 * (which is returned by a call to <tt>getElementOpenType().getClassName()</tt>).
0336 * <p>
0337 * The internal field corresponding to the type name of this <code>ArrayType</code> instance is also set to
0338 * the class name of the array instances it describes.
0339 * In other words, the methods <code>getClassName</code> and <code>getTypeName</code> return the same string value.
0340 * The internal field corresponding to the description of this <code>ArrayType</code> instance is set to a string value
0341 * which follows the following template:
0342 * <ul>
0343 * <li>if non-primitive array: <tt>1-dimension array of <i><element_class_name></i></tt></li>
0344 * <li>if primitive array: <tt>1-dimension array of <i><primitive_type_of_the_element_class_name></i></tt></li>
0345 * </ul>
0346 * <p>
0347 * As an example, the following piece of code:
0348 * <pre>
0349 * ArrayType<int[]> t = new ArrayType<int[]>(SimpleType.INTEGER, true);
0350 * System.out.println("array class name = " + t.getClassName());
0351 * System.out.println("element class name = " + t.getElementOpenType().getClassName());
0352 * System.out.println("array type name = " + t.getTypeName());
0353 * System.out.println("array type description = " + t.getDescription());
0354 * </pre>
0355 * would produce the following output:
0356 * <pre>
0357 * array class name = [I
0358 * element class name = java.lang.Integer
0359 * array type name = [I
0360 * array type description = 1-dimension array of int
0361 * </pre>
0362 *
0363 * @param elementType the {@code SimpleType} of the element values
0364 * contained in the arrays described by this
0365 * {@code ArrayType} instance.
0366 *
0367 * @param primitiveArray {@code true} when this array describes
0368 * primitive arrays.
0369 *
0370 * @throws IllegalArgumentException if {@code dimension} is not a positive
0371 * integer.
0372 * @throws OpenDataException if {@code primitiveArray} is {@code true} and
0373 * {@code elementType} is not a valid {@code SimpleType} for a primitive
0374 * type.
0375 *
0376 * @since 1.6
0377 */
0378 public ArrayType(SimpleType<?> elementType, boolean primitiveArray)
0379 throws OpenDataException {
0380
0381 // Check and construct state defined by parent.
0382 // We can call the package-private OpenType constructor because the
0383 // set of SimpleTypes is fixed and SimpleType can't be subclassed.
0384 super (buildArrayClassName(1, elementType, primitiveArray),
0385 buildArrayClassName(1, elementType, primitiveArray),
0386 buildArrayDescription(1, elementType, primitiveArray),
0387 true);
0388
0389 // Check and construct state specific to ArrayType
0390 //
0391 this .dimension = 1;
0392 this .elementType = elementType;
0393 this .primitiveArray = primitiveArray;
0394 }
0395
0396 /* Package-private constructor for callers we trust to get it right. */
0397 ArrayType(String className, String typeName, String description,
0398 int dimension, OpenType elementType, boolean primitiveArray) {
0399 super (className, typeName, description, true);
0400 this .dimension = dimension;
0401 this .elementType = elementType;
0402 this .primitiveArray = primitiveArray;
0403 }
0404
0405 private static String buildArrayClassName(int dimension,
0406 OpenType<?> elementType) throws OpenDataException {
0407 boolean isPrimitiveArray = false;
0408 if (elementType.isArray()) {
0409 isPrimitiveArray = ((ArrayType) elementType)
0410 .isPrimitiveArray();
0411 }
0412 return buildArrayClassName(dimension, elementType,
0413 isPrimitiveArray);
0414 }
0415
0416 private static String buildArrayClassName(int dimension,
0417 OpenType<?> elementType, boolean isPrimitiveArray)
0418 throws OpenDataException {
0419 if (dimension < 1) {
0420 throw new IllegalArgumentException(
0421 "Value of argument dimension must be greater than 0");
0422 }
0423 StringBuilder result = new StringBuilder();
0424 String elementClassName = elementType.getClassName();
0425 // Add N (= dimension) additional '[' characters to the existing array
0426 for (int i = 1; i <= dimension; i++) {
0427 result.append('[');
0428 }
0429 if (elementType.isArray()) {
0430 result.append(elementClassName);
0431 } else {
0432 if (isPrimitiveArray) {
0433 final String key = getPrimitiveTypeKey(elementClassName);
0434 // Ideally we should throw an IllegalArgumentException here,
0435 // but for compatibility reasons we throw an OpenDataException.
0436 // (used to be thrown by OpenType() constructor).
0437 //
0438 if (key == null)
0439 throw new OpenDataException(
0440 "Element type is not primitive: "
0441 + elementClassName);
0442 result.append(key);
0443 } else {
0444 result.append("L");
0445 result.append(elementClassName);
0446 result.append(';');
0447 }
0448 }
0449 return result.toString();
0450 }
0451
0452 private static String buildArrayDescription(int dimension,
0453 OpenType<?> elementType) throws OpenDataException {
0454 boolean isPrimitiveArray = false;
0455 if (elementType.isArray()) {
0456 isPrimitiveArray = ((ArrayType) elementType)
0457 .isPrimitiveArray();
0458 }
0459 return buildArrayDescription(dimension, elementType,
0460 isPrimitiveArray);
0461 }
0462
0463 private static String buildArrayDescription(int dimension,
0464 OpenType<?> elementType, boolean isPrimitiveArray)
0465 throws OpenDataException {
0466 if (elementType.isArray()) {
0467 ArrayType at = (ArrayType) elementType;
0468 dimension += at.getDimension();
0469 elementType = at.getElementOpenType();
0470 isPrimitiveArray = at.isPrimitiveArray();
0471 }
0472 StringBuilder result = new StringBuilder(dimension
0473 + "-dimension array of ");
0474 final String elementClassName = elementType.getClassName();
0475 if (isPrimitiveArray) {
0476 // Convert from wrapper type to primitive type
0477 final String primitiveType = getPrimitiveTypeName(elementClassName);
0478
0479 // Ideally we should throw an IllegalArgumentException here,
0480 // but for compatibility reasons we throw an OpenDataException.
0481 // (used to be thrown by OpenType() constructor).
0482 //
0483 if (primitiveType == null)
0484 throw new OpenDataException(
0485 "Element is not a primitive type: "
0486 + elementClassName);
0487 result.append(primitiveType);
0488 } else {
0489 result.append(elementClassName);
0490 }
0491 return result.toString();
0492 }
0493
0494 /* *** ArrayType specific information methods *** */
0495
0496 /**
0497 * Returns the dimension of arrays described by this <tt>ArrayType</tt> instance.
0498 *
0499 * @return the dimension.
0500 */
0501 public int getDimension() {
0502
0503 return dimension;
0504 }
0505
0506 /**
0507 * Returns the <i>open type</i> of element values contained in the arrays described by this <tt>ArrayType</tt> instance.
0508 *
0509 * @return the element type.
0510 */
0511 public OpenType<?> getElementOpenType() {
0512
0513 return elementType;
0514 }
0515
0516 /**
0517 * Returns <code>true</code> if the open data values this open
0518 * type describes are primitive arrays, <code>false</code> otherwise.
0519 *
0520 * @return true if this is a primitive array type.
0521 *
0522 * @since 1.6
0523 */
0524 public boolean isPrimitiveArray() {
0525
0526 return primitiveArray;
0527 }
0528
0529 /**
0530 * Tests whether <var>obj</var> is a value for this <code>ArrayType</code>
0531 * instance.
0532 * <p>
0533 * This method returns <code>true</code> if and only if <var>obj</var>
0534 * is not null, <var>obj</var> is an array and any one of the following
0535 * is <tt>true</tt>:
0536 *
0537 * <ul>
0538 * <li>if this <code>ArrayType</code> instance describes an array of
0539 * <tt>SimpleType</tt> elements or their corresponding primitive types,
0540 * <var>obj</var>'s class name is the same as the className field defined
0541 * for this <code>ArrayType</code> instance (i.e. the class name returned
0542 * by the {@link OpenType#getClassName() getClassName} method, which
0543 * includes the dimension information),<br> </li>
0544 * <li>if this <code>ArrayType</code> instance describes an array of
0545 * classes implementing the {@code TabularData} interface or the
0546 * {@code CompositeData} interface, <var>obj</var> is assignable to
0547 * such a declared array, and each element contained in <var>obj</var>
0548 * is either null or a valid value for the element's open type specified
0549 * by this <code>ArrayType</code> instance.</li>
0550 * </ul>
0551 *
0552 * @param obj the object to be tested.
0553 *
0554 * @return <code>true</code> if <var>obj</var> is a value for this
0555 * <code>ArrayType</code> instance.
0556 */
0557 public boolean isValue(Object obj) {
0558
0559 // if obj is null, return false
0560 //
0561 if (obj == null) {
0562 return false;
0563 }
0564
0565 Class objClass = obj.getClass();
0566 String objClassName = objClass.getName();
0567
0568 // if obj is not an array, return false
0569 //
0570 if (!objClass.isArray()) {
0571 return false;
0572 }
0573
0574 // Test if obj's class name is the same as for the array values that this instance describes
0575 // (this is fine if elements are of simple types, which are final classes)
0576 //
0577 if (this .getClassName().equals(objClassName)) {
0578 return true;
0579 }
0580
0581 // In case this ArrayType instance describes an array of classes implementing the TabularData or CompositeData interface,
0582 // we first check for the assignability of obj to such an array of TabularData or CompositeData,
0583 // which ensures that:
0584 // . obj is of the the same dimension as this ArrayType instance,
0585 // . it is declared as an array of elements which are either all TabularData or all CompositeData.
0586 //
0587 // If the assignment check is positive,
0588 // then we have to check that each element in obj is of the same TabularType or CompositeType
0589 // as the one described by this ArrayType instance.
0590 //
0591 // [About assignment check, note that the call below returns true: ]
0592 // [Class.forName("[Lpackage.CompositeData;").isAssignableFrom(Class.forName("[Lpackage.CompositeDataImpl;)")); ]
0593 //
0594 if ((this .elementType.getClassName().equals(TabularData.class
0595 .getName()))
0596 || (this .elementType.getClassName()
0597 .equals(CompositeData.class.getName()))) {
0598
0599 boolean isTabular = (elementType.getClassName()
0600 .equals(TabularData.class.getName()));
0601 int[] dims = new int[getDimension()];
0602 Class<?> elementClass = isTabular ? TabularData.class
0603 : CompositeData.class;
0604 Class<?> targetClass = Array
0605 .newInstance(elementClass, dims).getClass();
0606
0607 // assignment check: return false if negative
0608 if (!targetClass.isAssignableFrom(objClass)) {
0609 return false;
0610 }
0611
0612 // check that all elements in obj are valid values for this ArrayType
0613 if (!checkElementsType((Object[]) obj, this .dimension)) { // we know obj's dimension is this.dimension
0614 return false;
0615 }
0616
0617 return true;
0618 }
0619
0620 // if previous tests did not return, then obj is not a value for this ArrayType instance
0621 return false;
0622 }
0623
0624 /**
0625 * Returns true if and only if all elements contained in the array argument x_dim_Array of dimension dim
0626 * are valid values (ie either null or of the right openType)
0627 * for the element open type specified by this ArrayType instance.
0628 *
0629 * This method's implementation uses recursion to go down the dimensions of the array argument.
0630 */
0631 private boolean checkElementsType(Object[] x_dim_Array, int dim) {
0632
0633 // if the elements of x_dim_Array are themselves array: go down recursively....
0634 if (dim > 1) {
0635 for (int i = 0; i < x_dim_Array.length; i++) {
0636 if (!checkElementsType((Object[]) x_dim_Array[i],
0637 dim - 1)) {
0638 return false;
0639 }
0640 }
0641 return true;
0642 }
0643 // ...else, for a non-empty array, each element must be a valid value: either null or of the right openType
0644 else {
0645 for (int i = 0; i < x_dim_Array.length; i++) {
0646 if ((x_dim_Array[i] != null)
0647 && (!this .getElementOpenType().isValue(
0648 x_dim_Array[i]))) {
0649 return false;
0650 }
0651 }
0652 return true;
0653 }
0654 }
0655
0656 @Override
0657 boolean isAssignableFrom(OpenType ot) {
0658 if (!(ot instanceof ArrayType))
0659 return false;
0660 ArrayType<?> at = (ArrayType<?>) ot;
0661 return (at.getDimension() == getDimension()
0662 && at.isPrimitiveArray() == isPrimitiveArray() && at
0663 .getElementOpenType().isAssignableFrom(
0664 getElementOpenType()));
0665 }
0666
0667 /* *** Methods overriden from class Object *** */
0668
0669 /**
0670 * Compares the specified <code>obj</code> parameter with this
0671 * <code>ArrayType</code> instance for equality.
0672 * <p>
0673 * Two <code>ArrayType</code> instances are equal if and only if they
0674 * describe array instances which have the same dimension, elements'
0675 * open type and primitive array flag.
0676 *
0677 * @param obj the object to be compared for equality with this
0678 * <code>ArrayType</code> instance; if <var>obj</var>
0679 * is <code>null</code> or is not an instance of the
0680 * class <code>ArrayType</code> this method returns
0681 * <code>false</code>.
0682 *
0683 * @return <code>true</code> if the specified object is equal to
0684 * this <code>ArrayType</code> instance.
0685 */
0686 public boolean equals(Object obj) {
0687
0688 // if obj is null, return false
0689 //
0690 if (obj == null) {
0691 return false;
0692 }
0693
0694 // if obj is not an ArrayType, return false
0695 //
0696 if (!(obj instanceof ArrayType))
0697 return false;
0698 ArrayType other = (ArrayType) obj;
0699
0700 // if other's dimension is different than this instance's, return false
0701 //
0702 if (this .dimension != other.dimension) {
0703 return false;
0704 }
0705
0706 // Test if other's elementType field is the same as for this instance
0707 //
0708 if (!this .elementType.equals(other.elementType)) {
0709 return false;
0710 }
0711
0712 // Test if other's primitiveArray flag is the same as for this instance
0713 //
0714 return this .primitiveArray == other.primitiveArray;
0715 }
0716
0717 /**
0718 * Returns the hash code value for this <code>ArrayType</code> instance.
0719 * <p>
0720 * The hash code of an <code>ArrayType</code> instance is the sum of the
0721 * hash codes of all the elements of information used in <code>equals</code>
0722 * comparisons (i.e. dimension, elements' open type and primitive array flag).
0723 * The hashcode for a primitive value is the hashcode of the corresponding boxed
0724 * object (e.g. the hashcode for <tt>true</tt> is <tt>Boolean.TRUE.hashCode()</tt>).
0725 * This ensures that <code> t1.equals(t2) </code> implies that
0726 * <code> t1.hashCode()==t2.hashCode() </code> for any two
0727 * <code>ArrayType</code> instances <code>t1</code> and <code>t2</code>,
0728 * as required by the general contract of the method
0729 * {@link Object#hashCode() Object.hashCode()}.
0730 * <p>
0731 * As <code>ArrayType</code> instances are immutable, the hash
0732 * code for this instance is calculated once, on the first call
0733 * to <code>hashCode</code>, and then the same value is returned
0734 * for subsequent calls.
0735 *
0736 * @return the hash code value for this <code>ArrayType</code> instance
0737 */
0738 public int hashCode() {
0739
0740 // Calculate the hash code value if it has not yet been done (ie 1st call to hashCode())
0741 //
0742 if (myHashCode == null) {
0743 int value = 0;
0744 value += dimension;
0745 value += elementType.hashCode();
0746 value += Boolean.valueOf(primitiveArray).hashCode();
0747 myHashCode = new Integer(value);
0748 }
0749
0750 // return always the same hash code for this instance (immutable)
0751 //
0752 return myHashCode.intValue();
0753 }
0754
0755 /**
0756 * Returns a string representation of this <code>ArrayType</code> instance.
0757 * <p>
0758 * The string representation consists of the name of this class (i.e.
0759 * <code>javax.management.openmbean.ArrayType</code>), the type name,
0760 * the dimension, the elements' open type and the primitive array flag
0761 * defined for this instance.
0762 * <p>
0763 * As <code>ArrayType</code> instances are immutable, the
0764 * string representation for this instance is calculated
0765 * once, on the first call to <code>toString</code>, and
0766 * then the same value is returned for subsequent calls.
0767 *
0768 * @return a string representation of this <code>ArrayType</code> instance
0769 */
0770 public String toString() {
0771
0772 // Calculate the string representation if it has not yet been done (ie 1st call to toString())
0773 //
0774 if (myToString == null) {
0775 myToString = getClass().getName() + "(name="
0776 + getTypeName() + ",dimension=" + dimension
0777 + ",elementType=" + elementType
0778 + ",primitiveArray=" + primitiveArray + ")";
0779 }
0780
0781 // return always the same string representation for this instance (immutable)
0782 //
0783 return myToString;
0784 }
0785
0786 /**
0787 * Create an {@code ArrayType} instance in a type-safe manner.
0788 * <p>
0789 * Multidimensional arrays can be built up by calling this method as many
0790 * times as necessary.
0791 * <p>
0792 * Calling this method twice with the same parameters may return the same
0793 * object or two equal but not identical objects.
0794 * <p>
0795 * As an example, the following piece of code:
0796 * <pre>
0797 * ArrayType<String[]> t1 = ArrayType.getArrayType(SimpleType.STRING);
0798 * ArrayType<String[][]> t2 = ArrayType.getArrayType(t1);
0799 * ArrayType<String[][][]> t3 = ArrayType.getArrayType(t2);
0800 * System.out.println("array class name = " + t3.getClassName());
0801 * System.out.println("element class name = " + t3.getElementOpenType().getClassName());
0802 * System.out.println("array type name = " + t3.getTypeName());
0803 * System.out.println("array type description = " + t3.getDescription());
0804 * </pre>
0805 * would produce the following output:
0806 * <pre>
0807 * array class name = [[[Ljava.lang.String;
0808 * element class name = java.lang.String
0809 * array type name = [[[Ljava.lang.String;
0810 * array type description = 3-dimension array of java.lang.String
0811 * </pre>
0812 *
0813 * @param elementType the <i>open type</i> of element values contained
0814 * in the arrays described by this <tt>ArrayType</tt>
0815 * instance; must be an instance of either
0816 * <tt>SimpleType</tt>, <tt>CompositeType</tt>,
0817 * <tt>TabularType</tt> or another <tt>ArrayType</tt>
0818 * with a <tt>SimpleType</tt>, <tt>CompositeType</tt>
0819 * or <tt>TabularType</tt> as its <tt>elementType</tt>.
0820 *
0821 * @throws OpenDataException if <var>elementType's className</var> is not
0822 * one of the allowed Java class names for open
0823 * data.
0824 *
0825 * @since 1.6
0826 */
0827 public static <E> ArrayType<E[]> getArrayType(
0828 OpenType<E> elementType) throws OpenDataException {
0829 return new ArrayType<E[]>(1, elementType);
0830 }
0831
0832 /**
0833 * Create an {@code ArrayType} instance in a type-safe manner.
0834 * <p>
0835 * Calling this method twice with the same parameters may return the
0836 * same object or two equal but not identical objects.
0837 * <p>
0838 * As an example, the following piece of code:
0839 * <pre>
0840 * ArrayType<int[][][]> t = ArrayType.getPrimitiveArrayType(int[][][].class);
0841 * System.out.println("array class name = " + t.getClassName());
0842 * System.out.println("element class name = " + t.getElementOpenType().getClassName());
0843 * System.out.println("array type name = " + t.getTypeName());
0844 * System.out.println("array type description = " + t.getDescription());
0845 * </pre>
0846 * would produce the following output:
0847 * <pre>
0848 * array class name = [[[I
0849 * element class name = java.lang.Integer
0850 * array type name = [[[I
0851 * array type description = 3-dimension array of int
0852 * </pre>
0853 *
0854 * @param arrayClass a primitive array class such as {@code int[].class},
0855 * {@code boolean[][].class}, etc. The {@link
0856 * #getElementOpenType()} method of the returned
0857 * {@code ArrayType} returns the {@link SimpleType}
0858 * corresponding to the wrapper type of the primitive
0859 * type of the array.
0860 *
0861 * @throws IllegalArgumentException if <var>arrayClass</var> is not
0862 * a primitive array.
0863 *
0864 * @since 1.6
0865 */
0866 @SuppressWarnings("unchecked")
0867 // can't get appropriate T for primitive array
0868 public static <T> ArrayType<T> getPrimitiveArrayType(
0869 Class<T> arrayClass) {
0870 // Check if the supplied parameter is an array
0871 //
0872 if (!arrayClass.isArray()) {
0873 throw new IllegalArgumentException(
0874 "arrayClass must be an array");
0875 }
0876
0877 // Calculate array dimension and component type name
0878 //
0879 int n = 1;
0880 Class<?> componentType = arrayClass.getComponentType();
0881 while (componentType.isArray()) {
0882 n++;
0883 componentType = componentType.getComponentType();
0884 }
0885 String componentTypeName = componentType.getName();
0886
0887 // Check if the array's component type is a primitive type
0888 //
0889 if (!componentType.isPrimitive()) {
0890 throw new IllegalArgumentException(
0891 "component type of the array must be a primitive type");
0892 }
0893
0894 // Map component type name to corresponding SimpleType
0895 //
0896 final SimpleType<?> simpleType = getPrimitiveOpenType(componentTypeName);
0897
0898 // Build primitive array
0899 //
0900 try {
0901 ArrayType at = new ArrayType(simpleType, true);
0902 if (n > 1)
0903 at = new ArrayType<T>(n - 1, at);
0904 return at;
0905 } catch (OpenDataException e) {
0906 throw new IllegalArgumentException(e); // should not happen
0907 }
0908 }
0909
0910 /**
0911 * Replace/resolve the object read from the stream before it is returned
0912 * to the caller.
0913 *
0914 * @serialData The new serial form of this class defines a new serializable
0915 * {@code boolean} field {@code primitiveArray}. In order to guarantee the
0916 * interoperability with previous versions of this class the new serial
0917 * form must continue to refer to primitive wrapper types even when the
0918 * {@code ArrayType} instance describes a primitive type array. So when
0919 * {@code primitiveArray} is {@code true} the {@code className},
0920 * {@code typeName} and {@code description} serializable fields
0921 * are converted into primitive types before the deserialized
0922 * {@code ArrayType} instance is return to the caller. The
0923 * {@code elementType} field always returns the {@code SimpleType}
0924 * corresponding to the primitive wrapper type of the array's
0925 * primitive type.
0926 * <p>
0927 * Therefore the following serializable fields are deserialized as follows:
0928 * <ul>
0929 * <li>if {@code primitiveArray} is {@code true} the {@code className}
0930 * field is deserialized by replacing the array's component primitive
0931 * wrapper type by the corresponding array's component primitive type,
0932 * e.g. {@code "[[Ljava.lang.Integer;"} will be deserialized as
0933 * {@code "[[I"}.</li>
0934 * <li>if {@code primitiveArray} is {@code true} the {@code typeName}
0935 * field is deserialized by replacing the array's component primitive
0936 * wrapper type by the corresponding array's component primitive type,
0937 * e.g. {@code "[[Ljava.lang.Integer;"} will be deserialized as
0938 * {@code "[[I"}.</li>
0939 * <li>if {@code primitiveArray} is {@code true} the {@code description}
0940 * field is deserialized by replacing the array's component primitive
0941 * wrapper type by the corresponding array's component primitive type,
0942 * e.g. {@code "2-dimension array of java.lang.Integer"} will be
0943 * deserialized as {@code "2-dimension array of int"}.</li>
0944 * </ul>
0945 *
0946 * @since 1.6
0947 */
0948 private Object readResolve() throws ObjectStreamException {
0949 if (primitiveArray) {
0950 return convertFromWrapperToPrimitiveTypes();
0951 } else {
0952 return this ;
0953 }
0954 }
0955
0956 private ArrayType convertFromWrapperToPrimitiveTypes() {
0957 String cn = getClassName();
0958 String tn = getTypeName();
0959 String d = getDescription();
0960 for (Object[] typeDescr : PRIMITIVE_ARRAY_TYPES) {
0961 if (cn
0962 .indexOf((String) typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX]) != -1) {
0963 cn = cn.replaceFirst(
0964 "L" + typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX]
0965 + ";",
0966 (String) typeDescr[PRIMITIVE_TYPE_KEY_INDEX]);
0967 tn = tn.replaceFirst(
0968 "L" + typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX]
0969 + ";",
0970 (String) typeDescr[PRIMITIVE_TYPE_KEY_INDEX]);
0971 d = d
0972 .replaceFirst(
0973 (String) typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX],
0974 (String) typeDescr[PRIMITIVE_TYPE_NAME_INDEX]);
0975 break;
0976 }
0977 }
0978 return new ArrayType(cn, tn, d, dimension, elementType,
0979 primitiveArray);
0980 }
0981
0982 /**
0983 * Nominate a replacement for this object in the stream before the object
0984 * is written.
0985 *
0986 * @serialData The new serial form of this class defines a new serializable
0987 * {@code boolean} field {@code primitiveArray}. In order to guarantee the
0988 * interoperability with previous versions of this class the new serial
0989 * form must continue to refer to primitive wrapper types even when the
0990 * {@code ArrayType} instance describes a primitive type array. So when
0991 * {@code primitiveArray} is {@code true} the {@code className},
0992 * {@code typeName} and {@code description} serializable fields
0993 * are converted into wrapper types before the serialized
0994 * {@code ArrayType} instance is written to the stream. The
0995 * {@code elementType} field always returns the {@code SimpleType}
0996 * corresponding to the primitive wrapper type of the array's
0997 * primitive type.
0998 * <p>
0999 * Therefore the following serializable fields are serialized as follows:
1000 * <ul>
1001 * <li>if {@code primitiveArray} is {@code true} the {@code className}
1002 * field is serialized by replacing the array's component primitive
1003 * type by the corresponding array's component primitive wrapper type,
1004 * e.g. {@code "[[I"} will be serialized as
1005 * {@code "[[Ljava.lang.Integer;"}.</li>
1006 * <li>if {@code primitiveArray} is {@code true} the {@code typeName}
1007 * field is serialized by replacing the array's component primitive
1008 * type by the corresponding array's component primitive wrapper type,
1009 * e.g. {@code "[[I"} will be serialized as
1010 * {@code "[[Ljava.lang.Integer;"}.</li>
1011 * <li>if {@code primitiveArray} is {@code true} the {@code description}
1012 * field is serialized by replacing the array's component primitive
1013 * type by the corresponding array's component primitive wrapper type,
1014 * e.g. {@code "2-dimension array of int"} will be serialized as
1015 * {@code "2-dimension array of java.lang.Integer"}.</li>
1016 * </ul>
1017 *
1018 * @since 1.6
1019 */
1020 private Object writeReplace() throws ObjectStreamException {
1021 if (primitiveArray) {
1022 return convertFromPrimitiveToWrapperTypes();
1023 } else {
1024 return this ;
1025 }
1026 }
1027
1028 private ArrayType convertFromPrimitiveToWrapperTypes() {
1029 String cn = getClassName();
1030 String tn = getTypeName();
1031 String d = getDescription();
1032 for (Object[] typeDescr : PRIMITIVE_ARRAY_TYPES) {
1033 if (cn
1034 .indexOf((String) typeDescr[PRIMITIVE_TYPE_KEY_INDEX]) != -1) {
1035 cn = cn.replaceFirst(
1036 (String) typeDescr[PRIMITIVE_TYPE_KEY_INDEX],
1037 "L" + typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX]
1038 + ";");
1039 tn = tn.replaceFirst(
1040 (String) typeDescr[PRIMITIVE_TYPE_KEY_INDEX],
1041 "L" + typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX]
1042 + ";");
1043 d = d
1044 .replaceFirst(
1045 (String) typeDescr[PRIMITIVE_TYPE_NAME_INDEX],
1046 (String) typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX]);
1047 break;
1048 }
1049 }
1050 return new ArrayType(cn, tn, d, dimension, elementType,
1051 primitiveArray);
1052 }
1053 }
|