/*
* @(#)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 <code>ArrayType</code> class is the <i>open type</i> class whose instances describe
* all <i>open data</i> values which are n-dimensional arrays of <i>open data</i> 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 <i>open type</i> 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 <tt>ArrayType</tt> instance describing <i>open data</i> values which are
* arrays with dimension <var>dimension</var> of elements whose <i>open type</i> is <var>elementType</var>.
* <p>
* When invoked on an <tt>ArrayType</tt> 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 <code>java.lang.Class</code>), not the class name of the array elements
* (which is returned by a call to <tt>getElementOpenType().getClassName()</tt>).
* <p>
* The internal field corresponding to the type name of this <code>ArrayType</code> instance is also set to
* the class name of the array instances it describes.
* In other words, the methods <code>getClassName</code> and <code>getTypeName</code> return the same string value.
* The internal field corresponding to the description of this <code>ArrayType</code> instance is set to a string value
* which follows the following template:<br>
* <tt><i><dimension></i>-dimension array of <i><element_class_name></i></tt>
* <p>
* As an example, the following piece of code:
* <pre>
* 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());
* </pre>
* would produce the following output:
* <pre>
* 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
* </pre>
*
* @param dimension the dimension of arrays described by this <tt>ArrayType</tt> instance;
* must be greater than or equal to 1.
*
* @param elementType the <i>open type</i> of element values contained in the arrays described by
* this <tt>ArrayType</tt> instance; must be an instance of either
* <tt>SimpleType</tt>, <tt>CompositeType</tt> or <tt>TabularType</tt>.
*
* @throws IllegalArgumentException if <var>dimension</var> is not a positive integer
*
* @throws OpenDataException if <var>elementType</var> is an instance of <tt>ArrayType</tt>
*/
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<dimension; i++) { // add (dimension - 1) '[' characters
result.append('[');
}
result.append("[L");
result.append(elementClassName);
result.append(';');
return result.toString();
}
/* *** ArrayType specific information methods *** */
/**
* Returns the dimension of arrays described by this <tt>ArrayType</tt> instance.
*
* @return the dimension.
*/
public int getDimension() {
return dimension;
}
/**
* Returns the <i>open type</i> of element values contained in the arrays described by this <tt>ArrayType</tt> instance.
*
* @return the element type.
*/
public OpenType getElementOpenType() {
return elementType;
}
/**
* Tests whether <var>obj</var> is a value for this <code>ArrayType</code> instance.
* <p>
* This method returns <code>true</code> if and only if <var>obj</var> is not null, <var>obj</var> is an array
* and any one of the following is <tt>true</tt>:
* <p><ul>
* <li>if this <code>ArrayType</code> instance describes an array of <tt>SimpleType</tt> elements,
* <var>obj</var>'s class name is the same as the className field defined for this <code>ArrayType</code> instance
* (ie the class name returned by the {@link OpenType#getClassName() getClassName} method,
* which includes the dimension information),<br>
* </li>
* <li>if this <code>ArrayType</code> instance describes an array
* of classes implementing the TabularData interface or the CompositeData interface,
* <var>obj</var> is assignable to such a declared array,
* and each element contained in <var>obj</var> is either null or a valid value for the element's open type
* specified by this <code>ArrayType</code> instance.
* </li></ul>
* <p>
*
* @param obj the object to be tested.
*
* @return <code>true</code> if <var>obj</var> is a value for this <code>ArrayType</code> 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; i<x_dim_Array.length; i++) {
if ( ! checkElementsType((Object[])x_dim_Array[i], dim-1) ) {
return false;
}
}
return true;
}
// ...else, for a non-empty array, each element must be a valid value: either null or of the right openType
else {
for (int i=0; i<x_dim_Array.length; i++) {
if ( (x_dim_Array[i] != null) && (! this.getElementOpenType().isValue(x_dim_Array[i])) ) {
return false;
}
}
return true;
}
}
/* *** Methods overriden from class Object *** */
/**
* Compares the specified <code>obj</code> parameter with this <code>ArrayType</code> instance for equality.
* <p>
* Two <code>ArrayType</code> 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 <code>ArrayType</code> instance;
* if <var>obj</var> is <code>null</code> or is not an instance of the class <code>ArrayType</code>,
* <code>equals</code> returns <code>false</code>.
*
* @return <code>true</code> if the specified object is equal to this <code>ArrayType</code> 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 <code>ArrayType</code> instance.
* <p>
* The hash code of a <code>ArrayType</code> instance is the sum of the hash codes
* of all elements of information used in <code>equals</code> comparisons
* (ie: dimension and elements' type).
* This ensures that <code> t1.equals(t2) </code> implies that <code> t1.hashCode()==t2.hashCode() </code>
* for any two <code>ArrayType</code> instances <code>t1</code> and <code>t2</code>,
* as required by the general contract of the method
* {@link Object#hashCode() Object.hashCode()}.
* <p>
* As <code>ArrayType</code> instances are immutable, the hash code for this instance is calculated once,
* on the first call to <code>hashCode</code>, and then the same value is returned for subsequent calls.
*
* @return the hash code value for this <code>ArrayType</code> 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 <code>ArrayType</code> instance.
* <p>
* The string representation consists of
* the name of this class (ie <code>javax.management.openmbean.ArrayType</code>), the type name,
* the dimension and elements' type defined for this instance,
* <p>
* As <code>ArrayType</code> instances are immutable, the string representation for this instance is calculated once,
* on the first call to <code>toString</code>, and then the same value is returned for subsequent calls.
*
* @return a string representation of this <code>ArrayType</code> 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;
}
}
|