1/* 2 * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package javax.management.openmbean; 27 28import java.io.ObjectStreamException; 29import java.lang.reflect.Array; 30 31/** 32 * The {@code ArrayType} class is the <i>open type</i> class whose instances describe 33 * all <i>open data</i> values which are n-dimensional arrays of <i>open data</i> values. 34 * <p> 35 * Examples of valid {@code ArrayType} instances are: 36 * <pre>{@code 37 * // 2-dimension array of java.lang.String 38 * ArrayType<String[][]> a1 = new ArrayType<String[][]>(2, SimpleType.STRING); 39 * 40 * // 1-dimension array of int 41 * ArrayType<int[]> a2 = new ArrayType<int[]>(SimpleType.INTEGER, true); 42 * 43 * // 1-dimension array of java.lang.Integer 44 * ArrayType<Integer[]> a3 = new ArrayType<Integer[]>(SimpleType.INTEGER, false); 45 * 46 * // 4-dimension array of int 47 * ArrayType<int[][][][]> a4 = new ArrayType<int[][][][]>(3, a2); 48 * 49 * // 4-dimension array of java.lang.Integer 50 * ArrayType<Integer[][][][]> a5 = new ArrayType<Integer[][][][]>(3, a3); 51 * 52 * // 1-dimension array of java.lang.String 53 * ArrayType<String[]> a6 = new ArrayType<String[]>(SimpleType.STRING, false); 54 * 55 * // 1-dimension array of long 56 * ArrayType<long[]> a7 = new ArrayType<long[]>(SimpleType.LONG, true); 57 * 58 * // 1-dimension array of java.lang.Integer 59 * ArrayType<Integer[]> a8 = ArrayType.getArrayType(SimpleType.INTEGER); 60 * 61 * // 2-dimension array of java.lang.Integer 62 * ArrayType<Integer[][]> a9 = ArrayType.getArrayType(a8); 63 * 64 * // 2-dimension array of int 65 * ArrayType<int[][]> a10 = ArrayType.getPrimitiveArrayType(int[][].class); 66 * 67 * // 3-dimension array of int 68 * ArrayType<int[][][]> a11 = ArrayType.getArrayType(a10); 69 * 70 * // 1-dimension array of float 71 * ArrayType<float[]> a12 = ArrayType.getPrimitiveArrayType(float[].class); 72 * 73 * // 2-dimension array of float 74 * ArrayType<float[][]> a13 = ArrayType.getArrayType(a12); 75 * 76 * // 1-dimension array of javax.management.ObjectName 77 * ArrayType<ObjectName[]> a14 = ArrayType.getArrayType(SimpleType.OBJECTNAME); 78 * 79 * // 2-dimension array of javax.management.ObjectName 80 * ArrayType<ObjectName[][]> a15 = ArrayType.getArrayType(a14); 81 * 82 * // 3-dimension array of java.lang.String 83 * ArrayType<String[][][]> a16 = new ArrayType<String[][][]>(3, SimpleType.STRING); 84 * 85 * // 1-dimension array of java.lang.String 86 * ArrayType<String[]> a17 = new ArrayType<String[]>(1, SimpleType.STRING); 87 * 88 * // 2-dimension array of java.lang.String 89 * ArrayType<String[][]> a18 = new ArrayType<String[][]>(1, a17); 90 * 91 * // 3-dimension array of java.lang.String 92 * ArrayType<String[][][]> a19 = new ArrayType<String[][][]>(1, a18); 93 * }</pre> 94 * 95 * 96 * @since 1.5 97 */ 98/* 99 Generification note: we could have defined a type parameter that is the 100 element type, with class ArrayType<E> extends OpenType<E[]>. However, 101 that doesn't buy us all that much. We can't say 102 public OpenType<E> getElementOpenType() 103 because this ArrayType could be a multi-dimensional array. 104 For example, if we had 105 ArrayType(2, SimpleType.INTEGER) 106 then E would have to be Integer[], while getElementOpenType() would 107 return SimpleType.INTEGER, which is an OpenType<Integer>. 108 109 Furthermore, we would like to support int[] (as well as Integer[]) as 110 an Open Type (RFE 5045358). We would want this to be an OpenType<int[]> 111 which can't be expressed as <E[]> because E can't be a primitive type 112 like int. 113 */ 114public class ArrayType<T> extends OpenType<T> { 115 116 /* Serial version */ 117 static final long serialVersionUID = 720504429830309770L; 118 119 /** 120 * @serial The dimension of arrays described by this {@link ArrayType} 121 * instance. 122 */ 123 private int dimension; 124 125 /** 126 * @serial The <i>open type</i> of element values contained in the arrays 127 * described by this {@link ArrayType} instance. 128 */ 129 private OpenType<?> elementType; 130 131 /** 132 * @serial This flag indicates whether this {@link ArrayType} 133 * describes a primitive array. 134 * 135 * @since 1.6 136 */ 137 private boolean primitiveArray; 138 139 private transient Integer myHashCode = null; // As this instance is immutable, these two values 140 private transient String myToString = null; // need only be calculated once. 141 142 // indexes refering to columns in the PRIMITIVE_ARRAY_TYPES table. 143 private static final int PRIMITIVE_WRAPPER_NAME_INDEX = 0; 144 private static final int PRIMITIVE_TYPE_NAME_INDEX = 1; 145 private static final int PRIMITIVE_TYPE_KEY_INDEX = 2; 146 private static final int PRIMITIVE_OPEN_TYPE_INDEX = 3; 147 148 private static final Object[][] PRIMITIVE_ARRAY_TYPES = { 149 { Boolean.class.getName(), boolean.class.getName(), "Z", SimpleType.BOOLEAN }, 150 { Character.class.getName(), char.class.getName(), "C", SimpleType.CHARACTER }, 151 { Byte.class.getName(), byte.class.getName(), "B", SimpleType.BYTE }, 152 { Short.class.getName(), short.class.getName(), "S", SimpleType.SHORT }, 153 { Integer.class.getName(), int.class.getName(), "I", SimpleType.INTEGER }, 154 { Long.class.getName(), long.class.getName(), "J", SimpleType.LONG }, 155 { Float.class.getName(), float.class.getName(), "F", SimpleType.FLOAT }, 156 { Double.class.getName(), double.class.getName(), "D", SimpleType.DOUBLE } 157 }; 158 159 static boolean isPrimitiveContentType(final String primitiveKey) { 160 for (Object[] typeDescr : PRIMITIVE_ARRAY_TYPES) { 161 if (typeDescr[PRIMITIVE_TYPE_KEY_INDEX].equals(primitiveKey)) { 162 return true; 163 } 164 } 165 return false; 166 } 167 168 /** 169 * Return the key used to identify the element type in 170 * arrays - e.g. "Z" for boolean, "C" for char etc... 171 * @param elementClassName the wrapper class name of the array 172 * element ("Boolean", "Character", etc...) 173 * @return the key corresponding to the given type ("Z", "C", etc...) 174 * return null if the given elementClassName is not a primitive 175 * wrapper class name. 176 **/ 177 static String getPrimitiveTypeKey(String elementClassName) { 178 for (Object[] typeDescr : PRIMITIVE_ARRAY_TYPES) { 179 if (elementClassName.equals(typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX])) 180 return (String)typeDescr[PRIMITIVE_TYPE_KEY_INDEX]; 181 } 182 return null; 183 } 184 185 /** 186 * Return the primitive type name corresponding to the given wrapper class. 187 * e.g. "boolean" for "Boolean", "char" for "Character" etc... 188 * @param elementClassName the type of the array element ("Boolean", 189 * "Character", etc...) 190 * @return the primitive type name corresponding to the given wrapper class 191 * ("boolean", "char", etc...) 192 * return null if the given elementClassName is not a primitive 193 * wrapper type name. 194 **/ 195 static String getPrimitiveTypeName(String elementClassName) { 196 for (Object[] typeDescr : PRIMITIVE_ARRAY_TYPES) { 197 if (elementClassName.equals(typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX])) 198 return (String)typeDescr[PRIMITIVE_TYPE_NAME_INDEX]; 199 } 200 return null; 201 } 202 203 /** 204 * Return the primitive open type corresponding to the given primitive type. 205 * e.g. SimpleType.BOOLEAN for "boolean", SimpleType.CHARACTER for 206 * "char", etc... 207 * @param primitiveTypeName the primitive type of the array element ("boolean", 208 * "char", etc...) 209 * @return the OpenType corresponding to the given primitive type name 210 * (SimpleType.BOOLEAN, SimpleType.CHARACTER, etc...) 211 * return null if the given elementClassName is not a primitive 212 * type name. 213 **/ 214 static SimpleType<?> getPrimitiveOpenType(String primitiveTypeName) { 215 for (Object[] typeDescr : PRIMITIVE_ARRAY_TYPES) { 216 if (primitiveTypeName.equals(typeDescr[PRIMITIVE_TYPE_NAME_INDEX])) 217 return (SimpleType<?>)typeDescr[PRIMITIVE_OPEN_TYPE_INDEX]; 218 } 219 return null; 220 } 221 222 /* *** Constructor *** */ 223 224 /** 225 * Constructs an {@code ArrayType} instance describing <i>open data</i> values which are 226 * arrays with dimension <var>dimension</var> of elements 227 * whose <i>open type</i> is <var>elementType</var>. 228 * <p> 229 * When invoked on an {@code ArrayType} instance, 230 * the {@link OpenType#getClassName() getClassName} method 231 * returns the class name of the array instances it describes 232 * (following the rules defined by the 233 * {@link Class#getName() getName} method of {@code java.lang.Class}), 234 * not the class name of the array elements 235 * (which is returned by a call to {@code getElementOpenType().getClassName()}). 236 * <p> 237 * The internal field corresponding to the type name of this 238 * {@code ArrayType} instance is also set to 239 * the class name of the array instances it describes. 240 * In other words, the methods {@code getClassName} and 241 * {@code getTypeName} return the same string value. 242 * The internal field corresponding to the description of this 243 * {@code ArrayType} instance is set to a string value 244 * which follows the following template: 245 * <ul> 246 * <li>if non-primitive array: <code><i><dimension></i>-dimension array 247 * of <i><element_class_name></i></code></li> 248 * <li>if primitive array: <code><i><dimension></i>-dimension array 249 * of <i><primitive_type_of_the_element_class_name></i></code></li> 250 * </ul> 251 * <p> 252 * As an example, the following piece of code: 253 * <pre>{@code 254 * ArrayType<String[][][]> t = new ArrayType<String[][][]>(3, SimpleType.STRING); 255 * System.out.println("array class name = " + t.getClassName()); 256 * System.out.println("element class name = " + t.getElementOpenType().getClassName()); 257 * System.out.println("array type name = " + t.getTypeName()); 258 * System.out.println("array type description = " + t.getDescription()); 259 * }</pre> 260 * would produce the following output: 261 * <pre>{@code 262 * array class name = [[[Ljava.lang.String; 263 * element class name = java.lang.String 264 * array type name = [[[Ljava.lang.String; 265 * array type description = 3-dimension array of java.lang.String 266 * }</pre> 267 * And the following piece of code which is equivalent to the one listed 268 * above would also produce the same output: 269 * <pre>{@code 270 * ArrayType<String[]> t1 = new ArrayType<String[]>(1, SimpleType.STRING); 271 * ArrayType<String[][]> t2 = new ArrayType<String[][]>(1, t1); 272 * ArrayType<String[][][]> t3 = new ArrayType<String[][][]>(1, t2); 273 * System.out.println("array class name = " + t3.getClassName()); 274 * System.out.println("element class name = " + t3.getElementOpenType().getClassName()); 275 * System.out.println("array type name = " + t3.getTypeName()); 276 * System.out.println("array type description = " + t3.getDescription()); 277 * }</pre> 278 * 279 * @param dimension the dimension of arrays described by this {@code ArrayType} instance; 280 * must be greater than or equal to 1. 281 * 282 * @param elementType the <i>open type</i> of element values contained 283 * in the arrays described by this {@code ArrayType} 284 * instance; must be an instance of either 285 * {@code SimpleType}, {@code CompositeType}, 286 * {@code TabularType} or another {@code ArrayType} 287 * with a {@code SimpleType}, {@code CompositeType} 288 * or {@code TabularType} as its {@code elementType}. 289 * 290 * @throws IllegalArgumentException if {@code dimension} is not a positive 291 * integer. 292 * @throws OpenDataException if <var>elementType's className</var> is not 293 * one of the allowed Java class names for open 294 * data. 295 */ 296 public ArrayType(int dimension, 297 OpenType<?> elementType) throws OpenDataException { 298 // Check and construct state defined by parent. 299 // We can't use the package-private OpenType constructor because 300 // we don't know if the elementType parameter is sane. 301 super(buildArrayClassName(dimension, elementType), 302 buildArrayClassName(dimension, elementType), 303 buildArrayDescription(dimension, elementType)); 304 305 // Check and construct state specific to ArrayType 306 // 307 if (elementType.isArray()) { 308 ArrayType<?> at = (ArrayType<?>) elementType; 309 this.dimension = at.getDimension() + dimension; 310 this.elementType = at.getElementOpenType(); 311 this.primitiveArray = at.isPrimitiveArray(); 312 } else { 313 this.dimension = dimension; 314 this.elementType = elementType; 315 this.primitiveArray = false; 316 } 317 } 318 319 /** 320 * Constructs a unidimensional {@code ArrayType} instance for the 321 * supplied {@code SimpleType}. 322 * <p> 323 * This constructor supports the creation of arrays of primitive 324 * types when {@code primitiveArray} is {@code true}. 325 * <p> 326 * For primitive arrays the {@link #getElementOpenType()} method 327 * returns the {@link SimpleType} corresponding to the wrapper 328 * type of the primitive type of the array. 329 * <p> 330 * When invoked on an {@code ArrayType} instance, 331 * the {@link OpenType#getClassName() getClassName} method 332 * returns the class name of the array instances it describes 333 * (following the rules defined by the 334 * {@link Class#getName() getName} method of {@code java.lang.Class}), 335 * not the class name of the array elements 336 * (which is returned by a call to {@code getElementOpenType().getClassName()}). 337 * <p> 338 * The internal field corresponding to the type name of this 339 * {@code ArrayType} instance is also set to 340 * the class name of the array instances it describes. 341 * In other words, the methods {@code getClassName} and 342 * {@code getTypeName} return the same string value. 343 * The internal field corresponding to the description 344 * of this {@code ArrayType} instance is set to a string value 345 * which follows the following template: 346 * <ul> 347 * <li>if non-primitive array: <code>1-dimension array 348 * of <i><element_class_name></i></code></li> 349 * <li>if primitive array: <code>1-dimension array 350 * of <i><primitive_type_of_the_element_class_name></i></code></li> 351 * </ul> 352 * <p> 353 * As an example, the following piece of code: 354 * <pre>{@code 355 * ArrayType<int[]> t = new ArrayType<int[]>(SimpleType.INTEGER, true); 356 * System.out.println("array class name = " + t.getClassName()); 357 * System.out.println("element class name = " + t.getElementOpenType().getClassName()); 358 * System.out.println("array type name = " + t.getTypeName()); 359 * System.out.println("array type description = " + t.getDescription()); 360 * }</pre> 361 * would produce the following output: 362 * <pre>{@code 363 * array class name = [I 364 * element class name = java.lang.Integer 365 * array type name = [I 366 * array type description = 1-dimension array of int 367 * }</pre> 368 * 369 * @param elementType the {@code SimpleType} of the element values 370 * contained in the arrays described by this 371 * {@code ArrayType} instance. 372 * 373 * @param primitiveArray {@code true} when this array describes 374 * primitive arrays. 375 * 376 * @throws IllegalArgumentException if {@code dimension} is not a positive 377 * integer. 378 * @throws OpenDataException if {@code primitiveArray} is {@code true} and 379 * {@code elementType} is not a valid {@code SimpleType} for a primitive 380 * type. 381 * 382 * @since 1.6 383 */ 384 public ArrayType(SimpleType<?> elementType, 385 boolean primitiveArray) throws OpenDataException { 386 387 // Check and construct state defined by parent. 388 // We can call the package-private OpenType constructor because the 389 // set of SimpleTypes is fixed and SimpleType can't be subclassed. 390 super(buildArrayClassName(1, elementType, primitiveArray), 391 buildArrayClassName(1, elementType, primitiveArray), 392 buildArrayDescription(1, elementType, primitiveArray), 393 true); 394 395 // Check and construct state specific to ArrayType 396 // 397 this.dimension = 1; 398 this.elementType = elementType; 399 this.primitiveArray = primitiveArray; 400 } 401 402 /* Package-private constructor for callers we trust to get it right. */ 403 ArrayType(String className, String typeName, String description, 404 int dimension, OpenType<?> elementType, 405 boolean primitiveArray) { 406 super(className, typeName, description, true); 407 this.dimension = dimension; 408 this.elementType = elementType; 409 this.primitiveArray = primitiveArray; 410 } 411 412 private static String buildArrayClassName(int dimension, 413 OpenType<?> elementType) 414 throws OpenDataException { 415 boolean isPrimitiveArray = false; 416 if (elementType.isArray()) { 417 isPrimitiveArray = ((ArrayType<?>) elementType).isPrimitiveArray(); 418 } 419 return buildArrayClassName(dimension, elementType, isPrimitiveArray); 420 } 421 422 private static String buildArrayClassName(int dimension, 423 OpenType<?> elementType, 424 boolean isPrimitiveArray) 425 throws OpenDataException { 426 if (dimension < 1) { 427 throw new IllegalArgumentException( 428 "Value of argument dimension must be greater than 0"); 429 } 430 StringBuilder result = new StringBuilder(); 431 String elementClassName = elementType.getClassName(); 432 // Add N (= dimension) additional '[' characters to the existing array 433 for (int i = 1; i <= dimension; i++) { 434 result.append('['); 435 } 436 if (elementType.isArray()) { 437 result.append(elementClassName); 438 } else { 439 if (isPrimitiveArray) { 440 final String key = getPrimitiveTypeKey(elementClassName); 441 // Ideally we should throw an IllegalArgumentException here, 442 // but for compatibility reasons we throw an OpenDataException. 443 // (used to be thrown by OpenType() constructor). 444 // 445 if (key == null) 446 throw new OpenDataException("Element type is not primitive: " 447 + elementClassName); 448 result.append(key); 449 } else { 450 result.append("L"); 451 result.append(elementClassName); 452 result.append(';'); 453 } 454 } 455 return result.toString(); 456 } 457 458 private static String buildArrayDescription(int dimension, 459 OpenType<?> elementType) 460 throws OpenDataException { 461 boolean isPrimitiveArray = false; 462 if (elementType.isArray()) { 463 isPrimitiveArray = ((ArrayType<?>) elementType).isPrimitiveArray(); 464 } 465 return buildArrayDescription(dimension, elementType, isPrimitiveArray); 466 } 467 468 private static String buildArrayDescription(int dimension, 469 OpenType<?> elementType, 470 boolean isPrimitiveArray) 471 throws OpenDataException { 472 if (elementType.isArray()) { 473 ArrayType<?> at = (ArrayType<?>) elementType; 474 dimension += at.getDimension(); 475 elementType = at.getElementOpenType(); 476 isPrimitiveArray = at.isPrimitiveArray(); 477 } 478 StringBuilder result = new StringBuilder(); 479 result.append(dimension).append("-dimension array of "); 480 final String elementClassName = elementType.getClassName(); 481 if (isPrimitiveArray) { 482 // Convert from wrapper type to primitive type 483 final String primitiveType = 484 getPrimitiveTypeName(elementClassName); 485 486 // Ideally we should throw an IllegalArgumentException here, 487 // but for compatibility reasons we throw an OpenDataException. 488 // (used to be thrown by OpenType() constructor). 489 // 490 if (primitiveType == null) 491 throw new OpenDataException("Element is not a primitive type: "+ 492 elementClassName); 493 result.append(primitiveType); 494 } else { 495 result.append(elementClassName); 496 } 497 return result.toString(); 498 } 499 500 /* *** ArrayType specific information methods *** */ 501 502 /** 503 * Returns the dimension of arrays described by this {@code ArrayType} instance. 504 * 505 * @return the dimension. 506 */ 507 public int getDimension() { 508 509 return dimension; 510 } 511 512 /** 513 * Returns the <i>open type</i> of element values contained 514 * in the arrays described by this {@code ArrayType} instance. 515 * 516 * @return the element type. 517 */ 518 public OpenType<?> getElementOpenType() { 519 520 return elementType; 521 } 522 523 /** 524 * Returns {@code true} if the open data values this open 525 * type describes are primitive arrays, {@code false} otherwise. 526 * 527 * @return true if this is a primitive array type. 528 * 529 * @since 1.6 530 */ 531 public boolean isPrimitiveArray() { 532 533 return primitiveArray; 534 } 535 536 /** 537 * Tests whether <var>obj</var> is a value for this {@code ArrayType} 538 * instance. 539 * <p> 540 * This method returns {@code true} if and only if <var>obj</var> 541 * is not null, <var>obj</var> is an array and any one of the following 542 * is {@code true}: 543 * 544 * <ul> 545 * <li>if this {@code ArrayType} instance describes an array of 546 * {@code SimpleType} elements or their corresponding primitive types, 547 * <var>obj</var>'s class name is the same as the className field defined 548 * for this {@code ArrayType} instance (i.e. the class name returned 549 * by the {@link OpenType#getClassName() getClassName} method, which 550 * includes the dimension information),<br> </li> 551 * <li>if this {@code ArrayType} instance describes an array of 552 * classes implementing the {@code TabularData} interface or the 553 * {@code CompositeData} interface, <var>obj</var> is assignable to 554 * such a declared array, and each element contained in {<var>obj</var> 555 * is either null or a valid value for the element's open type specified 556 * by this {@code ArrayType} instance.</li> 557 * </ul> 558 * 559 * @param obj the object to be tested. 560 * 561 * @return {@code true} if <var>obj</var> is a value for this 562 * {@code ArrayType} instance. 563 */ 564 public boolean isValue(Object obj) { 565 566 // if obj is null, return false 567 // 568 if (obj == null) { 569 return false; 570 } 571 572 Class<?> objClass = obj.getClass(); 573 String objClassName = objClass.getName(); 574 575 // if obj is not an array, return false 576 // 577 if ( ! objClass.isArray() ) { 578 return false; 579 } 580 581 // Test if obj's class name is the same as for the array values that this instance describes 582 // (this is fine if elements are of simple types, which are final classes) 583 // 584 if ( this.getClassName().equals(objClassName) ) { 585 return true; 586 } 587 588 // In case this ArrayType instance describes an array of classes implementing the TabularData or CompositeData interface, 589 // we first check for the assignability of obj to such an array of TabularData or CompositeData, 590 // which ensures that: 591 // . obj is of the same dimension as this ArrayType instance, 592 // . it is declared as an array of elements which are either all TabularData or all CompositeData. 593 // 594 // If the assignment check is positive, 595 // then we have to check that each element in obj is of the same TabularType or CompositeType 596 // as the one described by this ArrayType instance. 597 // 598 // [About assignment check, note that the call below returns true: ] 599 // [Class.forName("[Lpackage.CompositeData;").isAssignableFrom(Class.forName("[Lpackage.CompositeDataImpl;)")); ] 600 // 601 if ( (this.elementType.getClassName().equals(TabularData.class.getName())) || 602 (this.elementType.getClassName().equals(CompositeData.class.getName())) ) { 603 604 boolean isTabular = 605 (elementType.getClassName().equals(TabularData.class.getName())); 606 int[] dims = new int[getDimension()]; 607 Class<?> elementClass = isTabular ? TabularData.class : CompositeData.class; 608 Class<?> targetClass = Array.newInstance(elementClass, dims).getClass(); 609 610 // assignment check: return false if negative 611 if ( ! targetClass.isAssignableFrom(objClass) ) { 612 return false; 613 } 614 615 // check that all elements in obj are valid values for this ArrayType 616 if ( ! checkElementsType( (Object[]) obj, this.dimension) ) { // we know obj's dimension is this.dimension 617 return false; 618 } 619 620 return true; 621 } 622 623 // if previous tests did not return, then obj is not a value for this ArrayType instance 624 return false; 625 } 626 627 /** 628 * Returns true if and only if all elements contained in the array argument x_dim_Array of dimension dim 629 * are valid values (ie either null or of the right openType) 630 * for the element open type specified by this ArrayType instance. 631 * 632 * This method's implementation uses recursion to go down the dimensions of the array argument. 633 */ 634 private boolean checkElementsType(Object[] x_dim_Array, int dim) { 635 636 // if the elements of x_dim_Array are themselves array: go down recursively.... 637 if ( dim > 1 ) { 638 for (int i=0; i<x_dim_Array.length; i++) { 639 if ( ! checkElementsType((Object[])x_dim_Array[i], dim-1) ) { 640 return false; 641 } 642 } 643 return true; 644 } 645 // ...else, for a non-empty array, each element must be a valid value: either null or of the right openType 646 else { 647 for (int i=0; i<x_dim_Array.length; i++) { 648 if ( (x_dim_Array[i] != null) && (! this.getElementOpenType().isValue(x_dim_Array[i])) ) { 649 return false; 650 } 651 } 652 return true; 653 } 654 } 655 656 @Override 657 boolean isAssignableFrom(OpenType<?> ot) { 658 if (!(ot instanceof ArrayType<?>)) 659 return false; 660 ArrayType<?> at = (ArrayType<?>) ot; 661 return (at.getDimension() == getDimension() && 662 at.isPrimitiveArray() == isPrimitiveArray() && 663 at.getElementOpenType().isAssignableFrom(getElementOpenType())); 664 } 665 666 667 /* *** Methods overriden from class Object *** */ 668 669 /** 670 * Compares the specified {@code obj} parameter with this 671 * {@code ArrayType} instance for equality. 672 * <p> 673 * Two {@code ArrayType} instances are equal if and only if they 674 * describe array instances which have the same dimension, elements' 675 * open type and primitive array flag. 676 * 677 * @param obj the object to be compared for equality with this 678 * {@code ArrayType} instance; if <var>obj</var> 679 * is {@code null} or is not an instance of the 680 * class {@code ArrayType} this method returns 681 * {@code false}. 682 * 683 * @return {@code true} if the specified object is equal to 684 * this {@code ArrayType} instance. 685 */ 686 public boolean equals(Object obj) { 687 688 // if obj is null, return false 689 // 690 if (obj == null) { 691 return false; 692 } 693 694 // if obj is not an ArrayType, return false 695 // 696 if (!(obj instanceof ArrayType<?>)) 697 return false; 698 ArrayType<?> other = (ArrayType<?>) obj; 699 700 // if other's dimension is different than this instance's, return false 701 // 702 if (this.dimension != other.dimension) { 703 return false; 704 } 705 706 // Test if other's elementType field is the same as for this instance 707 // 708 if (!this.elementType.equals(other.elementType)) { 709 return false; 710 } 711 712 // Test if other's primitiveArray flag is the same as for this instance 713 // 714 return this.primitiveArray == other.primitiveArray; 715 } 716 717 /** 718 * Returns the hash code value for this {@code ArrayType} instance. 719 * <p> 720 * The hash code of an {@code ArrayType} instance is the sum of the 721 * hash codes of all the elements of information used in {@code equals} 722 * comparisons (i.e. dimension, elements' open type and primitive array flag). 723 * The hashcode for a primitive value is the hashcode of the corresponding boxed 724 * object (e.g. the hashcode for {@code true} is {@code Boolean.TRUE.hashCode()}). 725 * This ensures that {@code t1.equals(t2)} implies that 726 * {@code t1.hashCode()==t2.hashCode()} for any two 727 * {@code ArrayType} instances {@code t1} and {@code t2}, 728 * as required by the general contract of the method 729 * {@link Object#hashCode() Object.hashCode()}. 730 * <p> 731 * As {@code ArrayType} instances are immutable, the hash 732 * code for this instance is calculated once, on the first call 733 * to {@code hashCode}, and then the same value is returned 734 * for subsequent calls. 735 * 736 * @return the hash code value for this {@code ArrayType} instance 737 */ 738 public int hashCode() { 739 740 // Calculate the hash code value if it has not yet been done (ie 1st call to hashCode()) 741 // 742 if (myHashCode == null) { 743 int value = 0; 744 value += dimension; 745 value += elementType.hashCode(); 746 value += Boolean.valueOf(primitiveArray).hashCode(); 747 myHashCode = Integer.valueOf(value); 748 } 749 750 // return always the same hash code for this instance (immutable) 751 // 752 return myHashCode.intValue(); 753 } 754 755 /** 756 * Returns a string representation of this {@code ArrayType} instance. 757 * <p> 758 * The string representation consists of the name of this class (i.e. 759 * {@code javax.management.openmbean.ArrayType}), the type name, 760 * the dimension, the elements' open type and the primitive array flag 761 * defined for this instance. 762 * <p> 763 * As {@code ArrayType} instances are immutable, the 764 * string representation for this instance is calculated 765 * once, on the first call to {@code toString}, and 766 * then the same value is returned for subsequent calls. 767 * 768 * @return a string representation of this {@code ArrayType} instance 769 */ 770 public String toString() { 771 772 // Calculate the string representation if it has not yet been done (ie 1st call to toString()) 773 // 774 if (myToString == null) { 775 myToString = getClass().getName() + 776 "(name=" + getTypeName() + 777 ",dimension=" + dimension + 778 ",elementType=" + elementType + 779 ",primitiveArray=" + primitiveArray + ")"; 780 } 781 782 // return always the same string representation for this instance (immutable) 783 // 784 return myToString; 785 } 786 787 /** 788 * Create an {@code ArrayType} instance in a type-safe manner. 789 * <p> 790 * Multidimensional arrays can be built up by calling this method as many 791 * times as necessary. 792 * <p> 793 * Calling this method twice with the same parameters may return the same 794 * object or two equal but not identical objects. 795 * <p> 796 * As an example, the following piece of code: 797 * <pre>{@code 798 * ArrayType<String[]> t1 = ArrayType.getArrayType(SimpleType.STRING); 799 * ArrayType<String[][]> t2 = ArrayType.getArrayType(t1); 800 * ArrayType<String[][][]> t3 = ArrayType.getArrayType(t2); 801 * System.out.println("array class name = " + t3.getClassName()); 802 * System.out.println("element class name = " + t3.getElementOpenType().getClassName()); 803 * System.out.println("array type name = " + t3.getTypeName()); 804 * System.out.println("array type description = " + t3.getDescription()); 805 * }</pre> 806 * would produce the following output: 807 * <pre>{@code 808 * array class name = [[[Ljava.lang.String; 809 * element class name = java.lang.String 810 * array type name = [[[Ljava.lang.String; 811 * array type description = 3-dimension array of java.lang.String 812 * }</pre> 813 * 814 * @param <E> the Java type that described instances must have 815 * @param elementType the <i>open type</i> of element values contained 816 * in the arrays described by this {@code ArrayType} 817 * instance; must be an instance of either 818 * {@code SimpleType}, {@code CompositeType}, 819 * {@code TabularType} or another {@code ArrayType} 820 * with a {@code SimpleType}, {@code CompositeType} 821 * or {@code TabularType} as its {@code elementType}. 822 * @return an {@code ArrayType} instance 823 * @throws OpenDataException if <var>elementType's className</var> is not 824 * one of the allowed Java class names for open 825 * data. 826 * 827 * @since 1.6 828 */ 829 public static <E> ArrayType<E[]> getArrayType(OpenType<E> elementType) 830 throws OpenDataException { 831 return new ArrayType<E[]>(1, elementType); 832 } 833 834 /** 835 * Create an {@code ArrayType} instance in a type-safe manner. 836 * <p> 837 * Calling this method twice with the same parameters may return the 838 * same object or two equal but not identical objects. 839 * <p> 840 * As an example, the following piece of code: 841 * <pre>{@code 842 * ArrayType<int[][][]> t = ArrayType.getPrimitiveArrayType(int[][][].class); 843 * System.out.println("array class name = " + t.getClassName()); 844 * System.out.println("element class name = " + t.getElementOpenType().getClassName()); 845 * System.out.println("array type name = " + t.getTypeName()); 846 * System.out.println("array type description = " + t.getDescription()); 847 * }</pre> 848 * would produce the following output: 849 * <pre>{@code 850 * array class name = [[[I 851 * element class name = java.lang.Integer 852 * array type name = [[[I 853 * array type description = 3-dimension array of int 854 * }</pre> 855 * 856 * @param <T> the Java type that described instances must have 857 * @param arrayClass a primitive array class such as {@code int[].class}, 858 * {@code boolean[][].class}, etc. The {@link 859 * #getElementOpenType()} method of the returned 860 * {@code ArrayType} returns the {@link SimpleType} 861 * corresponding to the wrapper type of the primitive 862 * type of the array. 863 * @return an {@code ArrayType} instance 864 * 865 * @throws IllegalArgumentException if <var>arrayClass</var> is not 866 * a primitive array. 867 * 868 * @since 1.6 869 */ 870 @SuppressWarnings("unchecked") // can't get appropriate T for primitive array 871 public static <T> ArrayType<T> getPrimitiveArrayType(Class<T> arrayClass) { 872 // Check if the supplied parameter is an array 873 // 874 if (!arrayClass.isArray()) { 875 throw new IllegalArgumentException("arrayClass must be an array"); 876 } 877 878 // Calculate array dimension and component type name 879 // 880 int n = 1; 881 Class<?> componentType = arrayClass.getComponentType(); 882 while (componentType.isArray()) { 883 n++; 884 componentType = componentType.getComponentType(); 885 } 886 String componentTypeName = componentType.getName(); 887 888 // Check if the array's component type is a primitive type 889 // 890 if (!componentType.isPrimitive()) { 891 throw new IllegalArgumentException( 892 "component type of the array must be a primitive type"); 893 } 894 895 // Map component type name to corresponding SimpleType 896 // 897 final SimpleType<?> simpleType = 898 getPrimitiveOpenType(componentTypeName); 899 900 // Build primitive array 901 // 902 try { 903 @SuppressWarnings("rawtypes") 904 ArrayType at = new ArrayType(simpleType, true); 905 if (n > 1) 906 at = new ArrayType<T>(n - 1, at); 907 return at; 908 } catch (OpenDataException e) { 909 throw new IllegalArgumentException(e); // should not happen 910 } 911 } 912 913 /** 914 * Replace/resolve the object read from the stream before it is returned 915 * to the caller. 916 * 917 * @serialData The new serial form of this class defines a new serializable 918 * {@code boolean} field {@code primitiveArray}. In order to guarantee the 919 * interoperability with previous versions of this class the new serial 920 * form must continue to refer to primitive wrapper types even when the 921 * {@code ArrayType} instance describes a primitive type array. So when 922 * {@code primitiveArray} is {@code true} the {@code className}, 923 * {@code typeName} and {@code description} serializable fields 924 * are converted into primitive types before the deserialized 925 * {@code ArrayType} instance is return to the caller. The 926 * {@code elementType} field always returns the {@code SimpleType} 927 * corresponding to the primitive wrapper type of the array's 928 * primitive type. 929 * <p> 930 * Therefore the following serializable fields are deserialized as follows: 931 * <ul> 932 * <li>if {@code primitiveArray} is {@code true} the {@code className} 933 * field is deserialized by replacing the array's component primitive 934 * wrapper type by the corresponding array's component primitive type, 935 * e.g. {@code "[[Ljava.lang.Integer;"} will be deserialized as 936 * {@code "[[I"}.</li> 937 * <li>if {@code primitiveArray} is {@code true} the {@code typeName} 938 * field is deserialized by replacing the array's component primitive 939 * wrapper type by the corresponding array's component primitive type, 940 * e.g. {@code "[[Ljava.lang.Integer;"} will be deserialized as 941 * {@code "[[I"}.</li> 942 * <li>if {@code primitiveArray} is {@code true} the {@code description} 943 * field is deserialized by replacing the array's component primitive 944 * wrapper type by the corresponding array's component primitive type, 945 * e.g. {@code "2-dimension array of java.lang.Integer"} will be 946 * deserialized as {@code "2-dimension array of int"}.</li> 947 * </ul> 948 * 949 * @since 1.6 950 */ 951 private Object readResolve() throws ObjectStreamException { 952 if (primitiveArray) { 953 return convertFromWrapperToPrimitiveTypes(); 954 } else { 955 return this; 956 } 957 } 958 959 private <T> ArrayType<T> convertFromWrapperToPrimitiveTypes() { 960 String cn = getClassName(); 961 String tn = getTypeName(); 962 String d = getDescription(); 963 for (Object[] typeDescr : PRIMITIVE_ARRAY_TYPES) { 964 if (cn.indexOf((String)typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX]) != -1) { 965 cn = cn.replaceFirst( 966 "L" + typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX] + ";", 967 (String) typeDescr[PRIMITIVE_TYPE_KEY_INDEX]); 968 tn = tn.replaceFirst( 969 "L" + typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX] + ";", 970 (String) typeDescr[PRIMITIVE_TYPE_KEY_INDEX]); 971 d = d.replaceFirst( 972 (String) typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX], 973 (String) typeDescr[PRIMITIVE_TYPE_NAME_INDEX]); 974 break; 975 } 976 } 977 return new ArrayType<T>(cn, tn, d, 978 dimension, elementType, primitiveArray); 979 } 980 981 /** 982 * Nominate a replacement for this object in the stream before the object 983 * is written. 984 * 985 * @serialData The new serial form of this class defines a new serializable 986 * {@code boolean} field {@code primitiveArray}. In order to guarantee the 987 * interoperability with previous versions of this class the new serial 988 * form must continue to refer to primitive wrapper types even when the 989 * {@code ArrayType} instance describes a primitive type array. So when 990 * {@code primitiveArray} is {@code true} the {@code className}, 991 * {@code typeName} and {@code description} serializable fields 992 * are converted into wrapper types before the serialized 993 * {@code ArrayType} instance is written to the stream. The 994 * {@code elementType} field always returns the {@code SimpleType} 995 * corresponding to the primitive wrapper type of the array's 996 * primitive type. 997 * <p> 998 * Therefore the following serializable fields are serialized as follows: 999 * <ul> 1000 * <li>if {@code primitiveArray} is {@code true} the {@code className} 1001 * field is serialized by replacing the array's component primitive 1002 * type by the corresponding array's component primitive wrapper type, 1003 * e.g. {@code "[[I"} will be serialized as 1004 * {@code "[[Ljava.lang.Integer;"}.</li> 1005 * <li>if {@code primitiveArray} is {@code true} the {@code typeName} 1006 * field is serialized by replacing the array's component primitive 1007 * type by the corresponding array's component primitive wrapper type, 1008 * e.g. {@code "[[I"} will be serialized as 1009 * {@code "[[Ljava.lang.Integer;"}.</li> 1010 * <li>if {@code primitiveArray} is {@code true} the {@code description} 1011 * field is serialized by replacing the array's component primitive 1012 * type by the corresponding array's component primitive wrapper type, 1013 * e.g. {@code "2-dimension array of int"} will be serialized as 1014 * {@code "2-dimension array of java.lang.Integer"}.</li> 1015 * </ul> 1016 * 1017 * @since 1.6 1018 */ 1019 private Object writeReplace() throws ObjectStreamException { 1020 if (primitiveArray) { 1021 return convertFromPrimitiveToWrapperTypes(); 1022 } else { 1023 return this; 1024 } 1025 } 1026 1027 private <T> ArrayType<T> convertFromPrimitiveToWrapperTypes() { 1028 String cn = getClassName(); 1029 String tn = getTypeName(); 1030 String d = getDescription(); 1031 for (Object[] typeDescr : PRIMITIVE_ARRAY_TYPES) { 1032 if (cn.indexOf((String) typeDescr[PRIMITIVE_TYPE_KEY_INDEX]) != -1) { 1033 cn = cn.replaceFirst( 1034 (String) typeDescr[PRIMITIVE_TYPE_KEY_INDEX], 1035 "L" + typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX] + ";"); 1036 tn = tn.replaceFirst( 1037 (String) typeDescr[PRIMITIVE_TYPE_KEY_INDEX], 1038 "L" + typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX] + ";"); 1039 d = d.replaceFirst( 1040 (String) typeDescr[PRIMITIVE_TYPE_NAME_INDEX], 1041 (String) typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX]); 1042 break; 1043 } 1044 } 1045 return new ArrayType<T>(cn, tn, d, 1046 dimension, elementType, primitiveArray); 1047 } 1048} 1049