/*
* @(#)ArrayType.java 3.24 03/12/19
*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package javax.management.openmbean;
// java import
//
import java.io.Serializable;
// jmx import
//
/**
* The ArrayType
class is the open type class whose instances describe
* all open data values which are n-dimensional arrays of open data values.
*
* @version 3.24 03/12/19
* @author Sun Microsystems, Inc.
*
* @since 1.5
* @since.unbundled JMX 1.1
*/
public class ArrayType
extends OpenType
implements Serializable {
/* Serial version */
static final long serialVersionUID = 720504429830309770L;
/**
* @serial The dimension of arrays described by this {@link ArrayType} instance
*/
private int dimension;
/**
* @serial The open type of element values contained in the arrays described by
* this {@link ArrayType} instance
*/
private OpenType elementType;
private transient Integer myHashCode = null; // As this instance is immutable, these two values
private transient String myToString = null; // need only be calculated once.
/* *** Constructor *** */
/**
* Constructs an ArrayType instance describing open data values which are
* arrays with dimension dimension of elements whose open type is elementType.
*
* 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:
* <dimension>-dimension array of <element_class_name>
*
* 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 = [[[java.lang.String; * element class name = java.lang.String * array type name = [[[java.lang.String; * array type description = 3-dimension array of java.lang.String ** * @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 or TabularType. * * @throws IllegalArgumentException if dimension is not a positive integer * * @throws OpenDataException if elementType is an instance of ArrayType */ public ArrayType(int dimension, OpenType elementType) throws OpenDataException { // Check and construct state defined by parent. // super(buildArrayClassName(dimension, elementType.getClassName()), buildArrayClassName(dimension, elementType.getClassName()), String.valueOf(dimension) +"-dimension array of "+ elementType.getClassName()); // Check and construct state specific to ArrayType // this.dimension = dimension; // already checked >=1 in buildArrayClassName this.elementType = elementType; // cannot be ArrayType: super() would throw exception on the built classname } /** * */ private static String buildArrayClassName(int dimension, String elementClassName) throws OpenDataException { if (dimension < 1) { throw new IllegalArgumentException("Value of argument dimension must be greater than 0"); } StringBuffer result = new StringBuffer(); for (int i=1; i
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,
* obj's class name is the same as the className field defined for this ArrayType
instance
* (ie 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 TabularData interface or the 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.
*
*
* @param obj the object to be tested.
*
* @return
* Two
* The hash code of a
* As
* The string representation consists of
* the name of this class (ie
* As 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())) ) {
/* this.getClassName() is
* "[Ljavax.management.openmbean.TabularData;" or the same
* thing for CompositeData, either one optionally preceded
* by n '[' characters. So the class is necessarily known
* to the ClassLoader of ArrayType, and Class.forName is
* safe. */
Class targetClass;
try {
targetClass = Class.forName(this.getClassName());
} catch (ClassNotFoundException e) { // should not happen
return false;
}
// 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; iArrayType
instance for equality.
* ArrayType
instances are equal if and only if they describes array instances
* which have the same dimension and elements' open type.
*
* @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
,
* equals
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
//
ArrayType other;
try {
other = (ArrayType) obj;
} catch (ClassCastException e) {
return false;
}
// if other's dimension is different than this instance's, return false
//
if (other.dimension != this.dimension) {
return false;
}
// Test if other's elementType field is the same as for this instance
//
return this.elementType.equals(other.elementType);
}
/**
* Returns the hash code value for this ArrayType
instance.
* ArrayType
instance is the sum of the hash codes
* of all elements of information used in equals
comparisons
* (ie: dimension and elements' type).
* 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()}.
* 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 += this.dimension;
value += this.elementType.hashCode();
myHashCode = new Integer(value);
}
// return always the same hash code for this instance (immutable)
//
return myHashCode.intValue();
}
/**
* Returns a string representation of this ArrayType
instance.
* javax.management.openmbean.ArrayType
), the type name,
* the dimension and elements' type defined for this instance,
* 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) {
StringBuffer result = new StringBuffer();
result.append(this.getClass().getName());
result.append("(name=");
result.append(getTypeName());
result.append(",dimension=");
result.append(String.valueOf(this.dimension));
result.append(",elementType=");
result.append(this.elementType.toString());
result.append(")");
myToString = result.toString();
}
// return always the same string representation for this instance (immutable)
//
return myToString;
}
}