Frames | No Frames |
1: /* ArrayType.java -- Open type descriptor for an array. 2: Copyright (C) 2006, 2007 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: package javax.management.openmbean; 39: 40: import java.lang.reflect.Array; 41: 42: import java.util.HashMap; 43: import java.util.Map; 44: 45: /** 46: * The open type descriptor for arrays of open data values. 47: * 48: * @author Andrew John Hughes (gnu_andrew@member.fsf.org) 49: * @since 1.5 50: */ 51: public class ArrayType<T> 52: extends OpenType<T> 53: { 54: 55: /** 56: * Compatible with JDK 1.5 57: */ 58: private static final long serialVersionUID = 720504429830309770L; 59: 60: /** 61: * The number of dimensions arrays of this type has. 62: */ 63: private int dimension; 64: 65: /** 66: * The element type of arrays of this type. 67: */ 68: private OpenType<?> elementType; 69: 70: /** 71: * True if this type represents a primitive array. 72: */ 73: private boolean primitiveArray; 74: 75: /** 76: * The hash code of this instance. 77: */ 78: private transient Integer hashCode; 79: 80: /** 81: * The <code>toString()</code> result of this instance. 82: */ 83: private transient String string; 84: 85: /** 86: * A cache of {@link ArrayType} instances created 87: * by {@link #getArrayType(OpenType)}. 88: */ 89: private static final Map<OpenType<?>,ArrayType<?>> cache = 90: new HashMap<OpenType<?>,ArrayType<?>>(); 91: 92: /** 93: * A cache of {@link ArrayType} instances created 94: * by {@link #getPrimitiveArrayType(Class)}. 95: */ 96: private static final Map<Class<?>,ArrayType<?>> primCache = 97: new HashMap<Class<?>,ArrayType<?>>(); 98: 99: /** 100: * Returns the class name of the array, given the element 101: * class name and its dimensions. 102: * 103: * @param elementType the type of the array's elements. 104: * @param dim the dimensions of the array. 105: * @param primitive true if this should be a primitive array. 106: * @return the array's class name. 107: * @throws OpenDataException if the class name does not reference 108: * a loadable class. 109: */ 110: private static final String getArrayClassName(OpenType<?> elementType, 111: int dim, 112: boolean primitive) 113: throws OpenDataException 114: { 115: Class<?> type; 116: if (primitive) 117: type = getPrimitiveTypeClass((SimpleType<?>) elementType); 118: else 119: { 120: String className = elementType.getClassName(); 121: try 122: { 123: type = Class.forName(className); 124: } 125: catch (ClassNotFoundException e) 126: { 127: throw new OpenDataException("The class name, " + className + 128: ", is unavailable."); 129: } 130: } 131: while (type.isArray()) 132: type = type.getComponentType(); 133: return 134: Array.newInstance(type, 135: new int[getDimensions(elementType, dim)]).getClass().getName(); 136: } 137: 138: /** 139: * Returns the dimensions of the new {@link ArrayType}, 140: * based on whether the given element type is already an 141: * {@link ArrayType} or not. 142: * 143: * @param elementType the type of the array. 144: * @param dim the proposed dimensions. 145: * @return the resultant dimensions. 146: * @throws IllegalArgumentException if <code>dim</code> is less than 1. 147: */ 148: private static final int getDimensions(OpenType<?> elementType, 149: int dim) 150: { 151: if (dim < 1) 152: throw new IllegalArgumentException("Dimensions must be greater " + 153: "than or equal to 1."); 154: if (elementType instanceof ArrayType) 155: return dim + ((ArrayType<?>) elementType).getDimension(); 156: return dim; 157: } 158: 159: /** 160: * Returns the appropriate primitive type name, given the 161: * corresponding wrapper class. 162: * 163: * @param type the type to convert. 164: * @return the corresponding primitive type. 165: * @throws OpenDataException if {@code type} is not a valid 166: * {@link Class} for a primitive type. 167: * 168: */ 169: private static final SimpleType<?> getPrimitiveType(Class<?> type) 170: throws OpenDataException 171: { 172: if (type.equals(Boolean.TYPE)) 173: return SimpleType.BOOLEAN; 174: if (type.equals(Byte.TYPE)) 175: return SimpleType.BYTE; 176: if (type.equals(Character.TYPE)) 177: return SimpleType.CHARACTER; 178: if (type.equals(Double.TYPE)) 179: return SimpleType.DOUBLE; 180: if (type.equals(Float.TYPE)) 181: return SimpleType.FLOAT; 182: if (type.equals(Integer.TYPE)) 183: return SimpleType.INTEGER; 184: if (type.equals(Long.TYPE)) 185: return SimpleType.LONG; 186: if (type.equals(Short.TYPE)) 187: return SimpleType.SHORT; 188: if (type.equals(Void.TYPE)) 189: return SimpleType.VOID; 190: throw new OpenDataException(type + " is not a primitive type."); 191: } 192: 193: /** 194: * Returns the appropriate primitive type name, given the 195: * corresponding wrapper class. 196: * 197: * @param type the type to convert. 198: * @return the corresponding primitive type. 199: * @throws OpenDataException if {@code type} is not a valid 200: * {@link SimpleType} for a primitive type. 201: * 202: */ 203: private static final Class<?> getPrimitiveTypeClass(SimpleType<?> type) 204: throws OpenDataException 205: { 206: if (type.equals(SimpleType.BOOLEAN)) 207: return Boolean.TYPE; 208: if (type.equals(SimpleType.BYTE)) 209: return Byte.TYPE; 210: if (type.equals(SimpleType.CHARACTER)) 211: return Character.TYPE; 212: if (type.equals(SimpleType.DOUBLE)) 213: return Double.TYPE; 214: if (type.equals(SimpleType.FLOAT)) 215: return Float.TYPE; 216: if (type.equals(SimpleType.INTEGER)) 217: return Integer.TYPE; 218: if (type.equals(SimpleType.LONG)) 219: return Long.TYPE; 220: if (type.equals(SimpleType.SHORT)) 221: return Short.TYPE; 222: if (type.equals(SimpleType.VOID)) 223: return Void.TYPE; 224: throw new OpenDataException(type + " is not a primitive type."); 225: } 226: 227: /** 228: * Returns the element type that will actually be used, if the 229: * specified element type is passed to a constructor. This is 230: * necessary to ensure that a non-array type is still returned when 231: * an {@link ArrayType} is constructed from an {@link ArrayType}. 232: * 233: * @param elemType the element type that was supplied. 234: * @return the element type that will be used. 235: */ 236: private static final OpenType<?> getElementType(OpenType<?> elemType) 237: { 238: if (elemType instanceof ArrayType) 239: return ((ArrayType<?>) elemType).getElementOpenType(); 240: return elemType; 241: } 242: 243: /** 244: * Returns the element type name that will actually be used, if the 245: * specified element type is passed to a constructor. This is 246: * necessary to ensure that a non-array type is still returned when 247: * an {@link ArrayType} is constructed from an {@link ArrayType}, 248: * and that primitive arrays are described correctly. 249: * 250: * @param elemType the element type that was supplied. 251: * @return the element type name that will be used. 252: * @throws OpenDataException if the element type is not a valid 253: * {@link SimpleType} for a primitive type. 254: */ 255: private static final String getElementTypeName(OpenType<?> elemType) 256: throws OpenDataException 257: { 258: OpenType<?> trueElemType = getElementType(elemType); 259: if (elemType instanceof ArrayType && 260: ((ArrayType<?>) elemType).isPrimitiveArray()) 261: return getPrimitiveTypeClass((SimpleType<?>) trueElemType).getName(); 262: return trueElemType.getClassName(); 263: } 264: 265: /** 266: * <p> 267: * Constructs a new {@link ArrayType} instance for an array of the 268: * specified type with the supplied number of dimensions. The attributes 269: * used by the superclass, {@link OpenType}, are automatically defined, 270: * based on these values. Both the class name and type name are set 271: * to the value returned by the {@link java.lang.Class#getName()} of 272: * the array's class (i.e. the element type, preceded by n instances of 273: * '[' and an 'L', where n is the number of dimensions the array has). 274: * The description is based upon the template <code>n-dimension array 275: * of e</code>, where n is the number of dimensions of the array, and 276: * e is the element type. The class name of the actual elements is 277: * obtainable by calling {@link OpenType#getClassName()} on the result 278: * of {@link #getElementOpenType()}. 279: * </p> 280: * <p> 281: * As an example, the array type returned by 282: * <code>new ArrayType(6, SimpleType.INTEGER)</code> has the following 283: * values: 284: * </p> 285: * <table> 286: * <th><td>Attribute</td><td>Value</td></th> 287: * <tr><td>Class Name</td><td><code>[[[[[[Ljava.lang.Integer;</code> 288: * </td></tr> 289: * <tr><td>Type Name</td><td><code>[[[[[[Ljava.lang.Integer;</code> 290: * </td></tr> 291: * <tr><td>Description</td><td><code>6-dimension array of 292: * java.lang.Integer</code></td></tr> 293: * <tr><td>Element Type Class Name</td><td><code>java.lang.Integer</code> 294: * </td></tr> 295: * </table> 296: * <p> 297: * The dimensions of the array must be equal to or greater than 1. The 298: * element type must be an instance of {@link SimpleType}, 299: * {@link CompositeType} or {@link TabularType}. 300: * </p> 301: * 302: * @param dim the dimensions of the array. 303: * @param elementType the type of the elements of the array. 304: * @throws IllegalArgumentException if <code>dim</code> is less than 1. 305: * @throws OpenDataException if the element type is not an instance of either 306: * {@link SimpleType}, {@link CompositeType} 307: * or {@link TabularType}. 308: */ 309: public ArrayType(int dim, OpenType<?> elementType) 310: throws OpenDataException 311: { 312: super(getArrayClassName(elementType, dim, false), 313: getArrayClassName(elementType, dim, false), 314: getDimensions(elementType, dim) + "-dimension array of " 315: + getElementTypeName(elementType)); 316: if (!(elementType instanceof SimpleType || 317: elementType instanceof CompositeType || 318: elementType instanceof TabularType || 319: elementType instanceof ArrayType)) 320: throw new OpenDataException("The element type must be a simple " + 321: "type, an array type, a composite type " + 322: "or a tabular type."); 323: dimension = getDimensions(elementType, dim); 324: this.elementType = getElementType(elementType); 325: primitiveArray = (elementType instanceof ArrayType && 326: ((ArrayType<?>) elementType).isPrimitiveArray()); 327: } 328: 329: /** 330: * <p> 331: * Constructs a new {@link ArrayType} instance for a unidimensional 332: * array of the specified {@link SimpleType}. The attributes 333: * used by the superclass, {@link OpenType}, are automatically defined, 334: * based on these values. Both the class name and type name are set 335: * to the value returned by the {@link java.lang.Class#getName()} of 336: * the array's class. If the array is of a primitive type (indicated 337: * by giving {@code primitiveArray} the value {@code true}), the 338: * name will be '[' followed by the appropriate letter for the 339: * primitive type (see {@link java.lang.Class#getName()}). If the 340: * array is not of a primitive type, then the name is formed from 341: * the element type, preceded by '[' and an 'L', in the same way 342: * as when the multi-dimensional constructor is used. 343: * </p> 344: * <p> 345: * The description is based upon the template <code>1-dimension array 346: * of e</code>, where e is either the primitive type or a class name, 347: * depending on whether the array itself is of a primitive type or not. 348: * The class name of the actual elements is obtainable by calling 349: * {@link OpenType#getClassName()} on the result of 350: * {@link #getElementOpenType()}. This will be the appropriate wrapper 351: * class for a primitive type. 352: * </p> 353: * <p> 354: * As an example, the array type returned by 355: * <code>new ArrayType(SimpleType.INTEGER, true)</code> has the following 356: * values: 357: * </p> 358: * <table> 359: * <th><td>Attribute</td><td>Value</td></th> 360: * <tr><td>Class Name</td><td><code>[I</code> 361: * </td></tr> 362: * <tr><td>Type Name</td><td><code>[I</code> 363: * </td></tr> 364: * <tr><td>Description</td><td><code>1-dimension array of int</code></td></tr> 365: * <tr><td>Element Type Class Name</td><td><code>java.lang.Integer</code> 366: * </td></tr> 367: * </table> 368: * 369: * @param elementType the type of the elements of the array. 370: * @param primitiveArray true if the array should be of a primitive type. 371: * @throws OpenDataException if {@code primitiveArray} is {@code true}, 372: * and {@link elementType} is not a valid 373: * {@link SimpleType} for a primitive type. 374: * @since 1.6 375: */ 376: public ArrayType(SimpleType<?> elementType, boolean primitiveArray) 377: throws OpenDataException 378: { 379: super(getArrayClassName(elementType, 1, primitiveArray), 380: getArrayClassName(elementType, 1, primitiveArray), 381: "1-dimension array of " + 382: (primitiveArray ? getPrimitiveTypeClass(elementType).getName() 383: : elementType.getClassName())); 384: dimension = 1; 385: this.elementType = elementType; 386: this.primitiveArray = primitiveArray; 387: } 388: 389: /** 390: * <p> 391: * Compares this array type with another object 392: * for equality. The objects are judged to be equal if: 393: * </p> 394: * <ul> 395: * <li><code>obj</code> is not null.</li> 396: * <li><code>obj</code> is an instance of 397: * {@link ArrayType}.</li> 398: * <li>The dimensions are equal.</li> 399: * <li>The element types are equal.</li> 400: * <li>The primitive array flag is set the same in both 401: * instances.</li> 402: * </ul> 403: * 404: * @param obj the object to compare with. 405: * @return true if the conditions above hold. 406: */ 407: public boolean equals(Object obj) 408: { 409: if (!(obj instanceof ArrayType)) 410: return false; 411: ArrayType<?> atype = (ArrayType<?>) obj; 412: return (atype.getDimension() == dimension && 413: atype.getElementOpenType().equals(elementType) && 414: atype.isPrimitiveArray() == primitiveArray); 415: } 416: 417: /** 418: * <p> 419: * Returns a new {@link ArrayType} instance in a type-safe 420: * manner, by ensuring that the type of the given {@link OpenType} 421: * matches the component type used in the type of the 422: * returned instance. If the given {@link OpenType} is a 423: * {@link SimpleType}, {@link CompositeType} or 424: * {@link TabularType}, then a 1-dimensional array of that 425: * type is returned. Otherwise, if the type is 426: * an {@link ArrayType} of n dimensions, the returned 427: * type is also an {@link ArrayType} but of n+1 dimensions. 428: * For example, 429: * {@code ArrayType.getArrayType(ArrayType.getArrayType(SimpleType.STRING))} 430: * returns a 2-dimensional array of {@link SimpleType#String}. 431: * </p> 432: * <p> 433: * This method caches its results, so that the same instance 434: * is returned from subsequent calls with the same parameters. 435: * </p> 436: * 437: * @param elementType the element type of the new array type. 438: * @throws OpenDataException if the class name of {@code elementType} 439: * is not in {@link OpenType#ALLOWED_CLASSNAMES_LIST}. 440: * @since 1.6 441: */ 442: @SuppressWarnings("unchecked") 443: public static <E> ArrayType<E[]> getArrayType(OpenType<E> elementType) 444: throws OpenDataException 445: { 446: ArrayType<E[]> arr = (ArrayType<E[]>) cache.get(elementType); 447: if (arr != null) 448: return arr; 449: arr = new ArrayType<E[]>(1, elementType); 450: cache.put(elementType, arr); 451: return arr; 452: } 453: 454: /** 455: * <p> 456: * Returns a new {@link ArrayType} instance for the given 457: * primitive type in a type-safe* manner, by ensuring that 458: * the type of the given {@link OpenType} matches the type 459: * used in the returned instance. If the type is 460: * an array of n dimensions, the returned 461: * type is also an {@link ArrayType} of n dimensions. 462: * </p> 463: * <p> 464: * As an example, the array type returned by 465: * <code>getPrimitiveArrayType(Integer.TYPE)</code> has the 466: * following values: 467: * </p> 468: * <table> 469: * <th><td>Attribute</td><td>Value</td></th> 470: * <tr><td>Class Name</td><td><code>[I</code> 471: * </td></tr> 472: * <tr><td>Type Name</td><td><code>[I</code> 473: * </td></tr> 474: * <tr><td>Description</td><td><code>1-dimension array of int</code></td></tr> 475: * <tr><td>Element Type Class Name</td><td><code>java.lang.Integer</code> 476: * </td></tr> 477: * </table> 478: * <p> 479: * This method caches its results, so that the same instance 480: * is returned from subsequent calls with the same parameters. 481: * </p> 482: * 483: * @param type the type of the new {@link ArrayType}. 484: * @throws IllegalArgumentException if the type is not a primitive 485: * array. 486: * @since 1.6 487: */ 488: @SuppressWarnings("unchecked") 489: public static <T> ArrayType<T> getPrimitiveArrayType(Class<T> type) 490: { 491: ArrayType<T> arr = (ArrayType<T>) primCache.get(type); 492: if (arr != null) 493: return arr; 494: Class<?> comType = type; 495: int dim = 0; 496: do 497: { 498: comType = comType.getComponentType(); 499: ++dim; 500: if (comType == null) 501: throw new IllegalArgumentException("The given class is " + 502: "not an array."); 503: } while (comType.isArray()); 504: try 505: { 506: arr = new ArrayType<T>(getPrimitiveType(comType), true); 507: } 508: catch (OpenDataException e) 509: { 510: throw new IllegalArgumentException("The array is not of a primitive " + 511: "type", e); 512: } 513: while (dim > 1) 514: try 515: { 516: arr = new ArrayType<T>(1, arr); 517: --dim; 518: } 519: catch (OpenDataException e) 520: { 521: throw (Error) 522: new InternalError("Couldn't generate extra dimensions").initCause(e); 523: } 524: primCache.put(type, arr); 525: return arr; 526: } 527: 528: /** 529: * Returns the number of dimensions used by arrays 530: * of this type. 531: * 532: * @return the number of dimensions. 533: */ 534: public int getDimension() 535: { 536: return dimension; 537: } 538: 539: /** 540: * Returns the open type descriptor which describes 541: * the type of the elements of this array type. 542: * 543: * @return the type of the elements. 544: */ 545: public OpenType<?> getElementOpenType() 546: { 547: return elementType; 548: } 549: 550: /** 551: * <p> 552: * Returns the hash code of the array type. 553: * This is computed as the sum of the hash code of the 554: * element type together with the number of dimensions 555: * the array has and the primitive array flag. These 556: * are the same elements of the type that are compared as 557: * part of the {@link #equals(java.lang.Object)} method, 558: * thus ensuring that the hashcode is compatible with the 559: * equality test. 560: * </p> 561: * <p> 562: * As instances of this class are immutable, the hash code 563: * is computed just once for each instance and reused 564: * throughout its life. 565: * </p> 566: * 567: * @return the hash code of this instance. 568: */ 569: public int hashCode() 570: { 571: if (hashCode == null) 572: hashCode = Integer.valueOf(dimension + 573: elementType.hashCode() + 574: Boolean.valueOf(primitiveArray).hashCode()); 575: return hashCode.intValue(); 576: } 577: 578: /** 579: * Returns true if this instance represents an array of 580: * a primitive type. 581: * 582: * @return true if the array is of a primitive type. 583: */ 584: public boolean isPrimitiveArray() 585: { 586: return primitiveArray; 587: } 588: 589: /** 590: * <p> 591: * Returns true if the specified object is a member of this 592: * array type. The object is judged to be so if it is 593: * non-null, an array and one of the following two conditions 594: * holds: 595: * </p> 596: * <ul> 597: * <li>This {@link ArrayType} instance has a {@link SimpleType} 598: * as its element type. Thus, the object must have the same 599: * class name as that returned by {@link SimpleType#getClassName()} 600: * for this class.</li> 601: * <li>This {@link ArrayType} instance has a {@link CompositeType} 602: * or a {@link TabularType} as its element type. Thus, the object 603: * must be assignable to such an array, and have elements which 604: * are either null or valid values for the element type.</li> 605: * </ul> 606: * 607: * @param obj the object to test for membership. 608: * @return true if the object is a member of this type. 609: */ 610: public boolean isValue(Object obj) 611: { 612: if (obj == null) 613: return false; 614: Class<?> objClass = obj.getClass(); 615: if (!(objClass.isArray())) 616: return false; 617: if (elementType instanceof SimpleType) 618: return getClassName().equals(objClass.getName()); 619: Class<?> elementClass = null; 620: try 621: { 622: elementClass = Class.forName(getClassName()); 623: } 624: catch (ClassNotFoundException e) 625: { 626: throw new IllegalStateException("The array type's element " + 627: "class could not be found.", e); 628: } 629: if (!(elementClass.isAssignableFrom(objClass))) 630: return false; 631: for (int a = 0; a < Array.getLength(obj); ++a) 632: { 633: Object elem = Array.get(obj, a); 634: if (elem != null && 635: (!(elementType.isValue(elem)))) 636: return false; 637: } 638: return true; 639: } 640: 641: /** 642: * <p> 643: * Returns a textual representation of this instance. This 644: * is constructed using the class name 645: * (<code>javax.management.openmbean.ArrayType</code>) 646: * and each element of the instance which is relevant to 647: * the definition of {@link equals(java.lang.Object)} and 648: * {@link hashCode()} (i.e. the type name, the number of 649: * dimensions and the element type). 650: * </p> 651: * <p> 652: * As instances of this class are immutable, the return value 653: * is computed just once for each instance and reused 654: * throughout its life. 655: * </p> 656: * 657: * @return a @link{java.lang.String} instance representing 658: * the instance in textual form. 659: */ 660: public String toString() 661: { 662: if (string == null) 663: string = getClass().getName() 664: + "[name=" + getTypeName() 665: + ", dimension=" + dimension 666: + ", elementType=" + elementType 667: + ", primitiveArray=" + primitiveArray 668: + "]"; 669: return string; 670: } 671: 672: }