GNU Classpath (0.95) | |
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.Arrays; 43: import java.util.HashMap; 44: import java.util.Map; 45: 46: /** 47: * The open type descriptor for arrays of open data values. 48: * 49: * @author Andrew John Hughes (gnu_andrew@member.fsf.org) 50: * @since 1.5 51: */ 52: public class ArrayType<T> 53: extends OpenType<T> 54: { 55: 56: /** 57: * Compatible with JDK 1.5 58: */ 59: private static final long serialVersionUID = 720504429830309770L; 60: 61: /** 62: * The number of dimensions arrays of this type has. 63: */ 64: private int dimension; 65: 66: /** 67: * The element type of arrays of this type. 68: */ 69: private OpenType<?> elementType; 70: 71: /** 72: * True if this type represents a primitive array. 73: */ 74: private boolean primitiveArray; 75: 76: /** 77: * The hash code of this instance. 78: */ 79: private transient Integer hashCode; 80: 81: /** 82: * The <code>toString()</code> result of this instance. 83: */ 84: private transient String string; 85: 86: /** 87: * A cache of {@link ArrayType} instances created 88: * by {@link #getArrayType(OpenType)}. 89: */ 90: private static final Map<OpenType<?>,ArrayType<?>> cache = 91: new HashMap<OpenType<?>,ArrayType<?>>(); 92: 93: /** 94: * A cache of {@link ArrayType} instances created 95: * by {@link #getPrimitiveArrayType(Class)}. 96: */ 97: private static final Map<Class<?>,ArrayType<?>> primCache = 98: new HashMap<Class<?>,ArrayType<?>>(); 99: 100: /** 101: * Returns the class name of the array, given the element 102: * class name and its dimensions. 103: * 104: * @param elementType the type of the array's elements. 105: * @param dim the dimensions of the array. 106: * @param primitive true if this should be a primitive array. 107: * @return the array's class name. 108: * @throws OpenDataException if the class name does not reference 109: * a loadable class. 110: */ 111: private static final String getArrayClassName(OpenType<?> elementType, 112: int dim, 113: boolean primitive) 114: throws OpenDataException 115: { 116: Class<?> type; 117: if (primitive) 118: type = getPrimitiveTypeClass((SimpleType<?>) elementType); 119: else 120: { 121: String className = elementType.getClassName(); 122: try 123: { 124: type = Class.forName(className); 125: } 126: catch (ClassNotFoundException e) 127: { 128: throw new OpenDataException("The class name, " + className + 129: ", is unavailable."); 130: } 131: } 132: while (type.isArray()) 133: type = type.getComponentType(); 134: return 135: Array.newInstance(type, 136: new int[getDimensions(elementType, dim)]).getClass().getName(); 137: } 138: 139: /** 140: * Returns the dimensions of the new {@link ArrayType}, 141: * based on whether the given element type is already an 142: * {@link ArrayType} or not. 143: * 144: * @param elementType the type of the array. 145: * @param dim the proposed dimensions. 146: * @return the resultant dimensions. 147: * @throws IllegalArgumentException if <code>dim</code> is less than 1. 148: */ 149: private static final int getDimensions(OpenType<?> elementType, 150: int dim) 151: { 152: if (dim < 1) 153: throw new IllegalArgumentException("Dimensions must be greater " + 154: "than or equal to 1."); 155: if (elementType instanceof ArrayType) 156: return dim + ((ArrayType) elementType).getDimension(); 157: return dim; 158: } 159: 160: /** 161: * Returns the appropriate primitive type name, given the 162: * corresponding wrapper class. 163: * 164: * @param type the type to convert. 165: * @return the corresponding primitive type. 166: * @throws OpenDataException if {@code type} is not a valid 167: * {@link Class} for a primitive type. 168: * 169: */ 170: private static final SimpleType<?> getPrimitiveType(Class<?> type) 171: throws OpenDataException 172: { 173: if (type.equals(Boolean.TYPE)) 174: return SimpleType.BOOLEAN; 175: if (type.equals(Byte.TYPE)) 176: return SimpleType.BYTE; 177: if (type.equals(Character.TYPE)) 178: return SimpleType.CHARACTER; 179: if (type.equals(Double.TYPE)) 180: return SimpleType.DOUBLE; 181: if (type.equals(Float.TYPE)) 182: return SimpleType.FLOAT; 183: if (type.equals(Integer.TYPE)) 184: return SimpleType.INTEGER; 185: if (type.equals(Long.TYPE)) 186: return SimpleType.LONG; 187: if (type.equals(Short.TYPE)) 188: return SimpleType.SHORT; 189: if (type.equals(Void.TYPE)) 190: return SimpleType.VOID; 191: throw new OpenDataException(type + " is not a primitive type."); 192: } 193: 194: /** 195: * Returns the appropriate primitive type name, given the 196: * corresponding wrapper class. 197: * 198: * @param type the type to convert. 199: * @return the corresponding primitive type. 200: * @throws OpenDataException if {@code type} is not a valid 201: * {@link SimpleType} for a primitive type. 202: * 203: */ 204: private static final Class<?> getPrimitiveTypeClass(SimpleType<?> type) 205: throws OpenDataException 206: { 207: if (type.equals(SimpleType.BOOLEAN)) 208: return Boolean.TYPE; 209: if (type.equals(SimpleType.BYTE)) 210: return Byte.TYPE; 211: if (type.equals(SimpleType.CHARACTER)) 212: return Character.TYPE; 213: if (type.equals(SimpleType.DOUBLE)) 214: return Double.TYPE; 215: if (type.equals(SimpleType.FLOAT)) 216: return Float.TYPE; 217: if (type.equals(SimpleType.INTEGER)) 218: return Integer.TYPE; 219: if (type.equals(SimpleType.LONG)) 220: return Long.TYPE; 221: if (type.equals(SimpleType.SHORT)) 222: return Short.TYPE; 223: if (type.equals(SimpleType.VOID)) 224: return Void.TYPE; 225: throw new OpenDataException(type + " is not a primitive type."); 226: } 227: 228: /** 229: * Returns the element type that will actually be used, if the 230: * specified element type is passed to a constructor. This is 231: * necessary to ensure that a non-array type is still returned when 232: * an {@link ArrayType} is constructed from an {@link ArrayType}. 233: * 234: * @param elemType the element type that was supplied. 235: * @return the element type that will be used. 236: */ 237: private static final OpenType<?> getElementType(OpenType<?> elemType) 238: { 239: if (elemType instanceof ArrayType) 240: return ((ArrayType) elemType).getElementOpenType(); 241: return elemType; 242: } 243: 244: /** 245: * Returns the element type name that will actually be used, if the 246: * specified element type is passed to a constructor. This is 247: * necessary to ensure that a non-array type is still returned when 248: * an {@link ArrayType} is constructed from an {@link ArrayType}, 249: * and that primitive arrays are described correctly. 250: * 251: * @param elemType the element type that was supplied. 252: * @return the element type name that will be used. 253: * @throws OpenDataException if the element type is not a valid 254: * {@link SimpleType} for a primitive type. 255: */ 256: private static final String getElementTypeName(OpenType<?> elemType) 257: throws OpenDataException 258: { 259: OpenType<?> trueElemType = getElementType(elemType); 260: if (elemType instanceof ArrayType && 261: ((ArrayType) elemType).isPrimitiveArray()) 262: return getPrimitiveTypeClass((SimpleType<?>) trueElemType).getName(); 263: return trueElemType.getClassName(); 264: } 265: 266: /** 267: * <p> 268: * Constructs a new {@link ArrayType} instance for an array of the 269: * specified type with the supplied number of dimensions. The attributes 270: * used by the superclass, {@link OpenType}, are automatically defined, 271: * based on these values. Both the class name and type name are set 272: * to the value returned by the {@link java.lang.Class#getName()} of 273: * the array's class (i.e. the element type, preceded by n instances of 274: * '[' and an 'L', where n is the number of dimensions the array has). 275: * The description is based upon the template <code>n-dimension array 276: * of e</code>, where n is the number of dimensions of the array, and 277: * e is the element type. The class name of the actual elements is 278: * obtainable by calling {@link OpenType#getClassName()} on the result 279: * of {@link #getElementOpenType()}. 280: * </p> 281: * <p> 282: * As an example, the array type returned by 283: * <code>new ArrayType(6, SimpleType.INTEGER)</code> has the following 284: * values: 285: * </p> 286: * <table> 287: * <th><td>Attribute</td><td>Value</td></th> 288: * <tr><td>Class Name</td><td><code>[[[[[[Ljava.lang.Integer;</code> 289: * </td></tr> 290: * <tr><td>Type Name</td><td><code>[[[[[[Ljava.lang.Integer;</code> 291: * </td></tr> 292: * <tr><td>Description</td><td><code>6-dimension array of 293: * java.lang.Integer</code></td></tr> 294: * <tr><td>Element Type Class Name</td><td><code>java.lang.Integer</code> 295: * </td></tr> 296: * </table> 297: * <p> 298: * The dimensions of the array must be equal to or greater than 1. The 299: * element type must be an instance of {@link SimpleType}, 300: * {@link CompositeType} or {@link TabularType}. 301: * </p> 302: * 303: * @param dim the dimensions of the array. 304: * @param elementType the type of the elements of the array. 305: * @throws IllegalArgumentException if <code>dim</code> is less than 1. 306: * @throws OpenDataException if the element type is not an instance of either 307: * {@link SimpleType}, {@link CompositeType} 308: * or {@link TabularType}. 309: */ 310: public ArrayType(int dim, OpenType<?> elementType) 311: throws OpenDataException 312: { 313: super(getArrayClassName(elementType, dim, false), 314: getArrayClassName(elementType, dim, false), 315: getDimensions(elementType, dim) + "-dimension array of " 316: + getElementTypeName(elementType)); 317: if (!(elementType instanceof SimpleType || 318: elementType instanceof CompositeType || 319: elementType instanceof TabularType || 320: elementType instanceof ArrayType)) 321: throw new OpenDataException("The element type must be a simple " + 322: "type, an array type, a composite type " + 323: "or a tabular type."); 324: dimension = getDimensions(elementType, dim); 325: this.elementType = getElementType(elementType); 326: primitiveArray = (elementType instanceof ArrayType && 327: ((ArrayType) elementType).isPrimitiveArray()); 328: } 329: 330: /** 331: * <p> 332: * Constructs a new {@link ArrayType} instance for a unidimensional 333: * array of the specified {@link SimpleType}. The attributes 334: * used by the superclass, {@link OpenType}, are automatically defined, 335: * based on these values. Both the class name and type name are set 336: * to the value returned by the {@link java.lang.Class#getName()} of 337: * the array's class. If the array is of a primitive type (indicated 338: * by giving {@code primitiveArray} the value {@code true}), the 339: * name will be '[' followed by the appropriate letter for the 340: * primitive type (see {@link java.lang.Class#getName()}). If the 341: * array is not of a primitive type, then the name is formed from 342: * the element type, preceded by '[' and an 'L', in the same way 343: * as when the multi-dimensional constructor is used. 344: * </p> 345: * <p> 346: * The description is based upon the template <code>1-dimension array 347: * of e</code>, where e is either the primitive type or a class name, 348: * depending on whether the array itself is of a primitive type or not. 349: * The class name of the actual elements is obtainable by calling 350: * {@link OpenType#getClassName()} on the result of 351: * {@link #getElementOpenType()}. This will be the appropriate wrapper 352: * class for a primitive type. 353: * </p> 354: * <p> 355: * As an example, the array type returned by 356: * <code>new ArrayType(SimpleType.INTEGER, true)</code> has the following 357: * values: 358: * </p> 359: * <table> 360: * <th><td>Attribute</td><td>Value</td></th> 361: * <tr><td>Class Name</td><td><code>[I</code> 362: * </td></tr> 363: * <tr><td>Type Name</td><td><code>[I</code> 364: * </td></tr> 365: * <tr><td>Description</td><td><code>1-dimension array of int</code></td></tr> 366: * <tr><td>Element Type Class Name</td><td><code>java.lang.Integer</code> 367: * </td></tr> 368: * </table> 369: * 370: * @param elementType the type of the elements of the array. 371: * @param primitiveArray true if the array should be of a primitive type. 372: * @throws OpenDataException if {@code primitiveArray} is {@code true}, 373: * and {@link elementType} is not a valid 374: * {@link SimpleType} for a primitive type. 375: * @since 1.6 376: */ 377: public ArrayType(SimpleType<?> elementType, boolean primitiveArray) 378: throws OpenDataException 379: { 380: super(getArrayClassName(elementType, 1, primitiveArray), 381: getArrayClassName(elementType, 1, primitiveArray), 382: "1-dimension array of " + 383: (primitiveArray ? getPrimitiveTypeClass(elementType).getName() 384: : elementType.getClassName())); 385: dimension = 1; 386: this.elementType = elementType; 387: this.primitiveArray = primitiveArray; 388: } 389: 390: /** 391: * <p> 392: * Compares this array type with another object 393: * for equality. The objects are judged to be equal if: 394: * </p> 395: * <ul> 396: * <li><code>obj</code> is not null.</li> 397: * <li><code>obj</code> is an instance of 398: * {@link ArrayType}.</li> 399: * <li>The dimensions are equal.</li> 400: * <li>The element types are equal.</li> 401: * <li>The primitive array flag is set the same in both 402: * instances.</li> 403: * </ul> 404: * 405: * @param obj the object to compare with. 406: * @return true if the conditions above hold. 407: */ 408: public boolean equals(Object obj) 409: { 410: if (!(obj instanceof ArrayType)) 411: return false; 412: ArrayType atype = (ArrayType) obj; 413: return (atype.getDimension() == dimension && 414: atype.getElementOpenType().equals(elementType) && 415: atype.isPrimitiveArray() == primitiveArray); 416: } 417: 418: /** 419: * <p> 420: * Returns a new {@link ArrayType} instance in a type-safe 421: * manner, by ensuring that the type of the given {@link OpenType} 422: * matches the component type used in the type of the 423: * returned instance. If the given {@link OpenType} is a 424: * {@link SimpleType}, {@link CompositeType} or 425: * {@link TabularType}, then a 1-dimensional array of that 426: * type is returned. Otherwise, if the type is 427: * an {@link ArrayType} of n dimensions, the returned 428: * type is also an {@link ArrayType} but of n+1 dimensions. 429: * For example, 430: * {@code ArrayType.getArrayType(ArrayType.getArrayType(SimpleType.STRING))} 431: * returns a 2-dimensional array of {@link SimpleType#String}. 432: * </p> 433: * <p> 434: * This method caches its results, so that the same instance 435: * is returned from subsequent calls with the same parameters. 436: * </p> 437: * 438: * @param elementType the element type of the new array type. 439: * @throws OpenDataException if the class name of {@code elementType} 440: * is not in {@link OpenType#ALLOWED_CLASSNAMES_LIST}. 441: * @since 1.6 442: */ 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(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: public static <T> ArrayType<T> getPrimitiveArrayType(Class<T> type) 489: { 490: ArrayType<T> arr = (ArrayType<T>) primCache.get(type); 491: if (arr != null) 492: return arr; 493: Class<?> comType = type; 494: int dim = 0; 495: do 496: { 497: comType = comType.getComponentType(); 498: ++dim; 499: if (comType == null) 500: throw new IllegalArgumentException("The given class is " + 501: "not an array."); 502: } while (comType.isArray()); 503: String className = type.getName(); 504: try 505: { 506: arr = new ArrayType(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(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: }
GNU Classpath (0.95) |