FileDocCategorySizeDatePackage
ArrayType.javaAPI DocJava SE 5 API14309Fri Aug 26 14:57:36 BST 2005javax.management.openmbean

ArrayType.java

/*
 * @(#)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;
    }

}