/*
* Copyright (c) 2000, 2008, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package javax.management.openmbean;
import java.io.ObjectStreamException;
import java.lang.reflect.Array;
/**
* The ArrayType
class is the open type class whose instances describe
* all open data values which are n-dimensional arrays of open data values.
*
* Examples of valid {@code ArrayType} instances are: *
* // 2-dimension array of java.lang.String * ArrayType* * * @since 1.5 */ /* Generification note: we could have defined a type parameter that is the element type, with class ArrayTypea1 = new ArrayType (2, SimpleType.STRING); * * // 1-dimension array of int * ArrayType a2 = new ArrayType (SimpleType.INTEGER, true); * * // 1-dimension array of java.lang.Integer * ArrayType a3 = new ArrayType (SimpleType.INTEGER, false); * * // 4-dimension array of int * ArrayType a4 = new ArrayType (3, a2); * * // 4-dimension array of java.lang.Integer * ArrayType a5 = new ArrayType (3, a3); * * // 1-dimension array of java.lang.String * ArrayType a6 = new ArrayType (SimpleType.STRING, false); * * // 1-dimension array of long * ArrayType a7 = new ArrayType (SimpleType.LONG, true); * * // 1-dimension array of java.lang.Integer * ArrayType a8 = ArrayType.getArrayType(SimpleType.INTEGER); * * // 2-dimension array of java.lang.Integer * ArrayType a9 = ArrayType.getArrayType(a8); * * // 2-dimension array of int * ArrayType a10 = ArrayType.getPrimitiveArrayType(int[][].class); * * // 3-dimension array of int * ArrayType a11 = ArrayType.getArrayType(a10); * * // 1-dimension array of float * ArrayType a12 = ArrayType.getPrimitiveArrayType(float[].class); * * // 2-dimension array of float * ArrayType a13 = ArrayType.getArrayType(a12); * * // 1-dimension array of javax.management.ObjectName * ArrayType a14 = ArrayType.getArrayType(SimpleType.OBJECTNAME); * * // 2-dimension array of javax.management.ObjectName * ArrayType a15 = ArrayType.getArrayType(a14); * * // 3-dimension array of java.lang.String * ArrayType a16 = new ArrayType (3, SimpleType.STRING); * * // 1-dimension array of java.lang.String * ArrayType a17 = new ArrayType (1, SimpleType.STRING); * * // 2-dimension array of java.lang.String * ArrayType a18 = new ArrayType (1, a17); * * // 3-dimension array of java.lang.String * ArrayType a19 = new ArrayType (1, a18); *
* When invoked on an ArrayType instance, the {@link OpenType#getClassName() getClassName} method
* returns the class name of the array instances it describes (following the rules defined by the
* {@link Class#getName() getName} method of java.lang.Class
), not the class name of the array elements
* (which is returned by a call to getElementOpenType().getClassName()).
*
* The internal field corresponding to the type name of this ArrayType
instance is also set to
* the class name of the array instances it describes.
* In other words, the methods getClassName
and getTypeName
return the same string value.
* The internal field corresponding to the description of this ArrayType
instance is set to a string value
* which follows the following template:
*
* As an example, the following piece of code: *
* ArrayType* would produce the following output: *t = new ArrayType (3, SimpleType.STRING); * System.out.println("array class name = " + t.getClassName()); * System.out.println("element class name = " + t.getElementOpenType().getClassName()); * System.out.println("array type name = " + t.getTypeName()); * System.out.println("array type description = " + t.getDescription()); *
* array class name = [[[Ljava.lang.String; * element class name = java.lang.String * array type name = [[[Ljava.lang.String; * array type description = 3-dimension array of java.lang.String ** And the following piece of code which is equivalent to the one listed * above would also produce the same output: *
* ArrayType* * @param dimension the dimension of arrays described by this ArrayType instance; * must be greater than or equal to 1. * * @param elementType the open type of element values contained * in the arrays described by this ArrayType * instance; must be an instance of either * SimpleType, CompositeType, * TabularType or another ArrayType * with a SimpleType, CompositeType * or TabularType as its elementType. * * @throws IllegalArgumentException if {@code dimension} is not a positive * integer. * @throws OpenDataException if elementType's className is not * one of the allowed Java class names for open * data. */ public ArrayType(int dimension, OpenType> elementType) throws OpenDataException { // Check and construct state defined by parent. // We can't use the package-private OpenType constructor because // we don't know if the elementType parameter is sane. super(buildArrayClassName(dimension, elementType), buildArrayClassName(dimension, elementType), buildArrayDescription(dimension, elementType)); // Check and construct state specific to ArrayType // if (elementType.isArray()) { ArrayType> at = (ArrayType>) elementType; this.dimension = at.getDimension() + dimension; this.elementType = at.getElementOpenType(); this.primitiveArray = at.isPrimitiveArray(); } else { this.dimension = dimension; this.elementType = elementType; this.primitiveArray = false; } } /** * Constructs a unidimensional {@code ArrayType} instance for the * supplied {@code SimpleType}. *t1 = new ArrayType (1, SimpleType.STRING); * ArrayType t2 = new ArrayType (1, t1); * ArrayType t3 = new ArrayType (1, t2); * System.out.println("array class name = " + t3.getClassName()); * System.out.println("element class name = " + t3.getElementOpenType().getClassName()); * System.out.println("array type name = " + t3.getTypeName()); * System.out.println("array type description = " + t3.getDescription()); *
* This constructor supports the creation of arrays of primitive * types when {@code primitiveArray} is {@code true}. *
* For primitive arrays the {@link #getElementOpenType()} method * returns the {@link SimpleType} corresponding to the wrapper * type of the primitive type of the array. *
* When invoked on an ArrayType instance, the {@link OpenType#getClassName() getClassName} method
* returns the class name of the array instances it describes (following the rules defined by the
* {@link Class#getName() getName} method of java.lang.Class
), not the class name of the array elements
* (which is returned by a call to getElementOpenType().getClassName()).
*
* The internal field corresponding to the type name of this ArrayType
instance is also set to
* the class name of the array instances it describes.
* In other words, the methods getClassName
and getTypeName
return the same string value.
* The internal field corresponding to the description of this ArrayType
instance is set to a string value
* which follows the following template:
*
* As an example, the following piece of code: *
* ArrayType* would produce the following output: *t = new ArrayType (SimpleType.INTEGER, true); * System.out.println("array class name = " + t.getClassName()); * System.out.println("element class name = " + t.getElementOpenType().getClassName()); * System.out.println("array type name = " + t.getTypeName()); * System.out.println("array type description = " + t.getDescription()); *
* array class name = [I * element class name = java.lang.Integer * array type name = [I * array type description = 1-dimension array of int ** * @param elementType the {@code SimpleType} of the element values * contained in the arrays described by this * {@code ArrayType} instance. * * @param primitiveArray {@code true} when this array describes * primitive arrays. * * @throws IllegalArgumentException if {@code dimension} is not a positive * integer. * @throws OpenDataException if {@code primitiveArray} is {@code true} and * {@code elementType} is not a valid {@code SimpleType} for a primitive * type. * * @since 1.6 */ public ArrayType(SimpleType> elementType, boolean primitiveArray) throws OpenDataException { // Check and construct state defined by parent. // We can call the package-private OpenType constructor because the // set of SimpleTypes is fixed and SimpleType can't be subclassed. super(buildArrayClassName(1, elementType, primitiveArray), buildArrayClassName(1, elementType, primitiveArray), buildArrayDescription(1, elementType, primitiveArray), true); // Check and construct state specific to ArrayType // this.dimension = 1; this.elementType = elementType; this.primitiveArray = primitiveArray; } /* Package-private constructor for callers we trust to get it right. */ ArrayType(String className, String typeName, String description, int dimension, OpenType> elementType, boolean primitiveArray) { super(className, typeName, description, true); this.dimension = dimension; this.elementType = elementType; this.primitiveArray = primitiveArray; } private static String buildArrayClassName(int dimension, OpenType> elementType) throws OpenDataException { boolean isPrimitiveArray = false; if (elementType.isArray()) { isPrimitiveArray = ((ArrayType>) elementType).isPrimitiveArray(); } return buildArrayClassName(dimension, elementType, isPrimitiveArray); } private static String buildArrayClassName(int dimension, OpenType> elementType, boolean isPrimitiveArray) throws OpenDataException { if (dimension < 1) { throw new IllegalArgumentException( "Value of argument dimension must be greater than 0"); } StringBuilder result = new StringBuilder(); String elementClassName = elementType.getClassName(); // Add N (= dimension) additional '[' characters to the existing array for (int i = 1; i <= dimension; i++) { result.append('['); } if (elementType.isArray()) { result.append(elementClassName); } else { if (isPrimitiveArray) { final String key = getPrimitiveTypeKey(elementClassName); // Ideally we should throw an IllegalArgumentException here, // but for compatibility reasons we throw an OpenDataException. // (used to be thrown by OpenType() constructor). // if (key == null) throw new OpenDataException("Element type is not primitive: " + elementClassName); result.append(key); } else { result.append("L"); result.append(elementClassName); result.append(';'); } } return result.toString(); } private static String buildArrayDescription(int dimension, OpenType> elementType) throws OpenDataException { boolean isPrimitiveArray = false; if (elementType.isArray()) { isPrimitiveArray = ((ArrayType>) elementType).isPrimitiveArray(); } return buildArrayDescription(dimension, elementType, isPrimitiveArray); } private static String buildArrayDescription(int dimension, OpenType> elementType, boolean isPrimitiveArray) throws OpenDataException { if (elementType.isArray()) { ArrayType> at = (ArrayType>) elementType; dimension += at.getDimension(); elementType = at.getElementOpenType(); isPrimitiveArray = at.isPrimitiveArray(); } StringBuilder result = new StringBuilder(dimension + "-dimension array of "); final String elementClassName = elementType.getClassName(); if (isPrimitiveArray) { // Convert from wrapper type to primitive type final String primitiveType = getPrimitiveTypeName(elementClassName); // Ideally we should throw an IllegalArgumentException here, // but for compatibility reasons we throw an OpenDataException. // (used to be thrown by OpenType() constructor). // if (primitiveType == null) throw new OpenDataException("Element is not a primitive type: "+ elementClassName); result.append(primitiveType); } else { result.append(elementClassName); } return result.toString(); } /* *** ArrayType specific information methods *** */ /** * Returns the dimension of arrays described by this ArrayType instance. * * @return the dimension. */ public int getDimension() { return dimension; } /** * Returns the open type of element values contained in the arrays described by this ArrayType instance. * * @return the element type. */ public OpenType> getElementOpenType() { return elementType; } /** * Returns
true
if the open data values this open
* type describes are primitive arrays, false
otherwise.
*
* @return true if this is a primitive array type.
*
* @since 1.6
*/
public boolean isPrimitiveArray() {
return primitiveArray;
}
/**
* Tests whether obj is a value for this ArrayType
* instance.
*
* This method returns true
if and only if obj
* is not null, obj is an array and any one of the following
* is true:
*
*
ArrayType
instance describes an array of
* SimpleType elements or their corresponding primitive types,
* obj's class name is the same as the className field defined
* for this ArrayType
instance (i.e. the class name returned
* by the {@link OpenType#getClassName() getClassName} method, which
* includes the dimension information),ArrayType
instance describes an array of
* classes implementing the {@code TabularData} interface or the
* {@code CompositeData} interface, obj is assignable to
* such a declared array, and each element contained in obj
* is either null or a valid value for the element's open type specified
* by this ArrayType
instance.true
if obj is a value for this
* ArrayType
instance.
*/
public boolean isValue(Object obj) {
// if obj is null, return false
//
if (obj == null) {
return false;
}
Class> objClass = obj.getClass();
String objClassName = objClass.getName();
// if obj is not an array, return false
//
if ( ! objClass.isArray() ) {
return false;
}
// Test if obj's class name is the same as for the array values that this instance describes
// (this is fine if elements are of simple types, which are final classes)
//
if ( this.getClassName().equals(objClassName) ) {
return true;
}
// In case this ArrayType instance describes an array of classes implementing the TabularData or CompositeData interface,
// we first check for the assignability of obj to such an array of TabularData or CompositeData,
// which ensures that:
// . obj is of the the same dimension as this ArrayType instance,
// . it is declared as an array of elements which are either all TabularData or all CompositeData.
//
// If the assignment check is positive,
// then we have to check that each element in obj is of the same TabularType or CompositeType
// as the one described by this ArrayType instance.
//
// [About assignment check, note that the call below returns true: ]
// [Class.forName("[Lpackage.CompositeData;").isAssignableFrom(Class.forName("[Lpackage.CompositeDataImpl;)")); ]
//
if ( (this.elementType.getClassName().equals(TabularData.class.getName())) ||
(this.elementType.getClassName().equals(CompositeData.class.getName())) ) {
boolean isTabular =
(elementType.getClassName().equals(TabularData.class.getName()));
int[] dims = new int[getDimension()];
Class> elementClass = isTabular ? TabularData.class : CompositeData.class;
Class> targetClass = Array.newInstance(elementClass, dims).getClass();
// assignment check: return false if negative
if ( ! targetClass.isAssignableFrom(objClass) ) {
return false;
}
// check that all elements in obj are valid values for this ArrayType
if ( ! checkElementsType( (Object[]) obj, this.dimension) ) { // we know obj's dimension is this.dimension
return false;
}
return true;
}
// if previous tests did not return, then obj is not a value for this ArrayType instance
return false;
}
/**
* Returns true if and only if all elements contained in the array argument x_dim_Array of dimension dim
* are valid values (ie either null or of the right openType)
* for the element open type specified by this ArrayType instance.
*
* This method's implementation uses recursion to go down the dimensions of the array argument.
*/
private boolean checkElementsType(Object[] x_dim_Array, int dim) {
// if the elements of x_dim_Array are themselves array: go down recursively....
if ( dim > 1 ) {
for (int i=0; iobj
parameter with this
* ArrayType
instance for equality.
*
* Two ArrayType
instances are equal if and only if they
* describe array instances which have the same dimension, elements'
* open type and primitive array flag.
*
* @param obj the object to be compared for equality with this
* ArrayType
instance; if obj
* is null
or is not an instance of the
* class ArrayType
this method returns
* false
.
*
* @return true
if the specified object is equal to
* this ArrayType
instance.
*/
public boolean equals(Object obj) {
// if obj is null, return false
//
if (obj == null) {
return false;
}
// if obj is not an ArrayType, return false
//
if (!(obj instanceof ArrayType>))
return false;
ArrayType> other = (ArrayType>) obj;
// if other's dimension is different than this instance's, return false
//
if (this.dimension != other.dimension) {
return false;
}
// Test if other's elementType field is the same as for this instance
//
if (!this.elementType.equals(other.elementType)) {
return false;
}
// Test if other's primitiveArray flag is the same as for this instance
//
return this.primitiveArray == other.primitiveArray;
}
/**
* Returns the hash code value for this ArrayType
instance.
*
* The hash code of an ArrayType
instance is the sum of the
* hash codes of all the elements of information used in equals
* comparisons (i.e. dimension, elements' open type and primitive array flag).
* The hashcode for a primitive value is the hashcode of the corresponding boxed
* object (e.g. the hashcode for true is Boolean.TRUE.hashCode()).
* This ensures that t1.equals(t2)
implies that
* t1.hashCode()==t2.hashCode()
for any two
* ArrayType
instances t1
and t2
,
* as required by the general contract of the method
* {@link Object#hashCode() Object.hashCode()}.
*
* As ArrayType
instances are immutable, the hash
* code for this instance is calculated once, on the first call
* to hashCode
, and then the same value is returned
* for subsequent calls.
*
* @return the hash code value for this ArrayType
instance
*/
public int hashCode() {
// Calculate the hash code value if it has not yet been done (ie 1st call to hashCode())
//
if (myHashCode == null) {
int value = 0;
value += dimension;
value += elementType.hashCode();
value += Boolean.valueOf(primitiveArray).hashCode();
myHashCode = Integer.valueOf(value);
}
// return always the same hash code for this instance (immutable)
//
return myHashCode.intValue();
}
/**
* Returns a string representation of this ArrayType
instance.
*
* The string representation consists of the name of this class (i.e.
* javax.management.openmbean.ArrayType
), the type name,
* the dimension, the elements' open type and the primitive array flag
* defined for this instance.
*
* As ArrayType
instances are immutable, the
* string representation for this instance is calculated
* once, on the first call to toString
, and
* then the same value is returned for subsequent calls.
*
* @return a string representation of this ArrayType
instance
*/
public String toString() {
// Calculate the string representation if it has not yet been done (ie 1st call to toString())
//
if (myToString == null) {
myToString = getClass().getName() +
"(name=" + getTypeName() +
",dimension=" + dimension +
",elementType=" + elementType +
",primitiveArray=" + primitiveArray + ")";
}
// return always the same string representation for this instance (immutable)
//
return myToString;
}
/**
* Create an {@code ArrayType} instance in a type-safe manner.
*
* Multidimensional arrays can be built up by calling this method as many * times as necessary. *
* Calling this method twice with the same parameters may return the same * object or two equal but not identical objects. *
* As an example, the following piece of code: *
* ArrayType* would produce the following output: *t1 = ArrayType.getArrayType(SimpleType.STRING); * ArrayType t2 = ArrayType.getArrayType(t1); * ArrayType t3 = ArrayType.getArrayType(t2); * System.out.println("array class name = " + t3.getClassName()); * System.out.println("element class name = " + t3.getElementOpenType().getClassName()); * System.out.println("array type name = " + t3.getTypeName()); * System.out.println("array type description = " + t3.getDescription()); *
* array class name = [[[Ljava.lang.String; * element class name = java.lang.String * array type name = [[[Ljava.lang.String; * array type description = 3-dimension array of java.lang.String ** * @param elementType the open type of element values contained * in the arrays described by this ArrayType * instance; must be an instance of either * SimpleType, CompositeType, * TabularType or another ArrayType * with a SimpleType, CompositeType * or TabularType as its elementType. * * @throws OpenDataException if elementType's className is not * one of the allowed Java class names for open * data. * * @since 1.6 */ public static
* Calling this method twice with the same parameters may return the * same object or two equal but not identical objects. *
* As an example, the following piece of code: *
* ArrayType* would produce the following output: *t = ArrayType.getPrimitiveArrayType(int[][][].class); * System.out.println("array class name = " + t.getClassName()); * System.out.println("element class name = " + t.getElementOpenType().getClassName()); * System.out.println("array type name = " + t.getTypeName()); * System.out.println("array type description = " + t.getDescription()); *
* array class name = [[[I * element class name = java.lang.Integer * array type name = [[[I * array type description = 3-dimension array of int ** * @param arrayClass a primitive array class such as {@code int[].class}, * {@code boolean[][].class}, etc. The {@link * #getElementOpenType()} method of the returned * {@code ArrayType} returns the {@link SimpleType} * corresponding to the wrapper type of the primitive * type of the array. * * @throws IllegalArgumentException if arrayClass is not * a primitive array. * * @since 1.6 */ @SuppressWarnings("unchecked") // can't get appropriate T for primitive array public static
* Therefore the following serializable fields are deserialized as follows: *
* Therefore the following serializable fields are serialized as follows: *