/*
* 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 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());
*
* would produce the following output:
*
* 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 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());
*
*
* @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}.
* * 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 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());
*
* would produce the following output:
*
* 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 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());
*
* would produce the following output:
*
* 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 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());
*
* would produce the following output:
*
* 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: *