001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.commons.lang; 018 019 import java.lang.reflect.Method; 020 import java.lang.reflect.Modifier; 021 import java.util.ArrayList; 022 import java.util.HashMap; 023 import java.util.Iterator; 024 import java.util.List; 025 import java.util.Map; 026 027 028 /** 029 * <p>Operates on classes without using reflection.</p> 030 * 031 * <p>This class handles invalid <code>null</code> inputs as best it can. 032 * Each method documents its behaviour in more detail.</p> 033 * 034 * <p>The notion of a <code>canonical name</code> includes the human 035 * readable name for the type, for example <code>int[]</code>. The 036 * non-canonical method variants work with the JVM names, such as 037 * <code>[I</code>. </p> 038 * 039 * @author Apache Software Foundation 040 * @author Gary Gregory 041 * @author Norm Deane 042 * @author Alban Peignier 043 * @author Tomasz Blachowicz 044 * @since 2.0 045 * @version $Id: ClassUtils.java 907121 2010-02-05 22:53:21Z mbenson $ 046 */ 047 public class ClassUtils { 048 049 /** 050 * <p>The package separator character: <code>'.' == {@value}</code>.</p> 051 */ 052 public static final char PACKAGE_SEPARATOR_CHAR = '.'; 053 054 /** 055 * <p>The package separator String: <code>"."</code>.</p> 056 */ 057 public static final String PACKAGE_SEPARATOR = String.valueOf(PACKAGE_SEPARATOR_CHAR); 058 059 /** 060 * <p>The inner class separator character: <code>'$' == {@value}</code>.</p> 061 */ 062 public static final char INNER_CLASS_SEPARATOR_CHAR = '$'; 063 064 /** 065 * <p>The inner class separator String: <code>"$"</code>.</p> 066 */ 067 public static final String INNER_CLASS_SEPARATOR = String.valueOf(INNER_CLASS_SEPARATOR_CHAR); 068 069 /** 070 * Maps primitive <code>Class</code>es to their corresponding wrapper <code>Class</code>. 071 */ 072 private static final Map primitiveWrapperMap = new HashMap(); 073 static { 074 primitiveWrapperMap.put(Boolean.TYPE, Boolean.class); 075 primitiveWrapperMap.put(Byte.TYPE, Byte.class); 076 primitiveWrapperMap.put(Character.TYPE, Character.class); 077 primitiveWrapperMap.put(Short.TYPE, Short.class); 078 primitiveWrapperMap.put(Integer.TYPE, Integer.class); 079 primitiveWrapperMap.put(Long.TYPE, Long.class); 080 primitiveWrapperMap.put(Double.TYPE, Double.class); 081 primitiveWrapperMap.put(Float.TYPE, Float.class); 082 primitiveWrapperMap.put(Void.TYPE, Void.TYPE); 083 } 084 085 /** 086 * Maps wrapper <code>Class</code>es to their corresponding primitive types. 087 */ 088 private static final Map wrapperPrimitiveMap = new HashMap(); 089 static { 090 for (Iterator it = primitiveWrapperMap.keySet().iterator(); it.hasNext();) { 091 Class primitiveClass = (Class) it.next(); 092 Class wrapperClass = (Class) primitiveWrapperMap.get(primitiveClass); 093 if (!primitiveClass.equals(wrapperClass)) { 094 wrapperPrimitiveMap.put(wrapperClass, primitiveClass); 095 } 096 } 097 } 098 099 /** 100 * Maps a primitive class name to its corresponding abbreviation used in array class names. 101 */ 102 private static final Map abbreviationMap = new HashMap(); 103 104 /** 105 * Maps an abbreviation used in array class names to corresponding primitive class name. 106 */ 107 private static final Map reverseAbbreviationMap = new HashMap(); 108 109 /** 110 * Add primitive type abbreviation to maps of abbreviations. 111 * 112 * @param primitive Canonical name of primitive type 113 * @param abbreviation Corresponding abbreviation of primitive type 114 */ 115 private static void addAbbreviation(String primitive, String abbreviation) { 116 abbreviationMap.put(primitive, abbreviation); 117 reverseAbbreviationMap.put(abbreviation, primitive); 118 } 119 120 /** 121 * Feed abbreviation maps 122 */ 123 static { 124 addAbbreviation("int", "I"); 125 addAbbreviation("boolean", "Z"); 126 addAbbreviation("float", "F"); 127 addAbbreviation("long", "J"); 128 addAbbreviation("short", "S"); 129 addAbbreviation("byte", "B"); 130 addAbbreviation("double", "D"); 131 addAbbreviation("char", "C"); 132 } 133 134 /** 135 * <p>ClassUtils instances should NOT be constructed in standard programming. 136 * Instead, the class should be used as 137 * <code>ClassUtils.getShortClassName(cls)</code>.</p> 138 * 139 * <p>This constructor is public to permit tools that require a JavaBean 140 * instance to operate.</p> 141 */ 142 public ClassUtils() { 143 super(); 144 } 145 146 // Short class name 147 // ---------------------------------------------------------------------- 148 /** 149 * <p>Gets the class name minus the package name for an <code>Object</code>.</p> 150 * 151 * @param object the class to get the short name for, may be null 152 * @param valueIfNull the value to return if null 153 * @return the class name of the object without the package name, or the null value 154 */ 155 public static String getShortClassName(Object object, String valueIfNull) { 156 if (object == null) { 157 return valueIfNull; 158 } 159 return getShortClassName(object.getClass()); 160 } 161 162 /** 163 * <p>Gets the class name minus the package name from a <code>Class</code>.</p> 164 * 165 * @param cls the class to get the short name for. 166 * @return the class name without the package name or an empty string 167 */ 168 public static String getShortClassName(Class cls) { 169 if (cls == null) { 170 return StringUtils.EMPTY; 171 } 172 return getShortClassName(cls.getName()); 173 } 174 175 /** 176 * <p>Gets the class name minus the package name from a String.</p> 177 * 178 * <p>The string passed in is assumed to be a class name - it is not checked.</p> 179 * 180 * @param className the className to get the short name for 181 * @return the class name of the class without the package name or an empty string 182 */ 183 public static String getShortClassName(String className) { 184 if (className == null) { 185 return StringUtils.EMPTY; 186 } 187 if (className.length() == 0) { 188 return StringUtils.EMPTY; 189 } 190 191 StringBuffer arrayPrefix = new StringBuffer(); 192 193 // Handle array encoding 194 if (className.startsWith("[")) { 195 while (className.charAt(0) == '[') { 196 className = className.substring(1); 197 arrayPrefix.append("[]"); 198 } 199 // Strip Object type encoding 200 if (className.charAt(0) == 'L' && className.charAt(className.length() - 1) == ';') { 201 className = className.substring(1, className.length() - 1); 202 } 203 } 204 205 if (reverseAbbreviationMap.containsKey(className)) { 206 className = (String)reverseAbbreviationMap.get(className); 207 } 208 209 int lastDotIdx = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR); 210 int innerIdx = className.indexOf( 211 INNER_CLASS_SEPARATOR_CHAR, lastDotIdx == -1 ? 0 : lastDotIdx + 1); 212 String out = className.substring(lastDotIdx + 1); 213 if (innerIdx != -1) { 214 out = out.replace(INNER_CLASS_SEPARATOR_CHAR, PACKAGE_SEPARATOR_CHAR); 215 } 216 return out + arrayPrefix; 217 } 218 219 // Package name 220 // ---------------------------------------------------------------------- 221 /** 222 * <p>Gets the package name of an <code>Object</code>.</p> 223 * 224 * @param object the class to get the package name for, may be null 225 * @param valueIfNull the value to return if null 226 * @return the package name of the object, or the null value 227 */ 228 public static String getPackageName(Object object, String valueIfNull) { 229 if (object == null) { 230 return valueIfNull; 231 } 232 return getPackageName(object.getClass()); 233 } 234 235 /** 236 * <p>Gets the package name of a <code>Class</code>.</p> 237 * 238 * @param cls the class to get the package name for, may be <code>null</code>. 239 * @return the package name or an empty string 240 */ 241 public static String getPackageName(Class cls) { 242 if (cls == null) { 243 return StringUtils.EMPTY; 244 } 245 return getPackageName(cls.getName()); 246 } 247 248 /** 249 * <p>Gets the package name from a <code>String</code>.</p> 250 * 251 * <p>The string passed in is assumed to be a class name - it is not checked.</p> 252 * <p>If the class is unpackaged, return an empty string.</p> 253 * 254 * @param className the className to get the package name for, may be <code>null</code> 255 * @return the package name or an empty string 256 */ 257 public static String getPackageName(String className) { 258 if (className == null || className.length() == 0) { 259 return StringUtils.EMPTY; 260 } 261 262 // Strip array encoding 263 while (className.charAt(0) == '[') { 264 className = className.substring(1); 265 } 266 // Strip Object type encoding 267 if (className.charAt(0) == 'L' && className.charAt(className.length() - 1) == ';') { 268 className = className.substring(1); 269 } 270 271 int i = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR); 272 if (i == -1) { 273 return StringUtils.EMPTY; 274 } 275 return className.substring(0, i); 276 } 277 278 // Superclasses/Superinterfaces 279 // ---------------------------------------------------------------------- 280 /** 281 * <p>Gets a <code>List</code> of superclasses for the given class.</p> 282 * 283 * @param cls the class to look up, may be <code>null</code> 284 * @return the <code>List</code> of superclasses in order going up from this one 285 * <code>null</code> if null input 286 */ 287 public static List getAllSuperclasses(Class cls) { 288 if (cls == null) { 289 return null; 290 } 291 List classes = new ArrayList(); 292 Class superclass = cls.getSuperclass(); 293 while (superclass != null) { 294 classes.add(superclass); 295 superclass = superclass.getSuperclass(); 296 } 297 return classes; 298 } 299 300 /** 301 * <p>Gets a <code>List</code> of all interfaces implemented by the given 302 * class and its superclasses.</p> 303 * 304 * <p>The order is determined by looking through each interface in turn as 305 * declared in the source file and following its hierarchy up. Then each 306 * superclass is considered in the same way. Later duplicates are ignored, 307 * so the order is maintained.</p> 308 * 309 * @param cls the class to look up, may be <code>null</code> 310 * @return the <code>List</code> of interfaces in order, 311 * <code>null</code> if null input 312 */ 313 public static List getAllInterfaces(Class cls) { 314 if (cls == null) { 315 return null; 316 } 317 318 List interfacesFound = new ArrayList(); 319 getAllInterfaces(cls, interfacesFound); 320 321 return interfacesFound; 322 } 323 324 /** 325 * Get the interfaces for the specified class. 326 * 327 * @param cls the class to look up, may be <code>null</code> 328 * @param interfacesFound the <code>Set</code> of interfaces for the class 329 */ 330 private static void getAllInterfaces(Class cls, List interfacesFound) { 331 while (cls != null) { 332 Class[] interfaces = cls.getInterfaces(); 333 334 for (int i = 0; i < interfaces.length; i++) { 335 if (!interfacesFound.contains(interfaces[i])) { 336 interfacesFound.add(interfaces[i]); 337 getAllInterfaces(interfaces[i], interfacesFound); 338 } 339 } 340 341 cls = cls.getSuperclass(); 342 } 343 } 344 345 // Convert list 346 // ---------------------------------------------------------------------- 347 /** 348 * <p>Given a <code>List</code> of class names, this method converts them into classes.</p> 349 * 350 * <p>A new <code>List</code> is returned. If the class name cannot be found, <code>null</code> 351 * is stored in the <code>List</code>. If the class name in the <code>List</code> is 352 * <code>null</code>, <code>null</code> is stored in the output <code>List</code>.</p> 353 * 354 * @param classNames the classNames to change 355 * @return a <code>List</code> of Class objects corresponding to the class names, 356 * <code>null</code> if null input 357 * @throws ClassCastException if classNames contains a non String entry 358 */ 359 public static List convertClassNamesToClasses(List classNames) { 360 if (classNames == null) { 361 return null; 362 } 363 List classes = new ArrayList(classNames.size()); 364 for (Iterator it = classNames.iterator(); it.hasNext();) { 365 String className = (String) it.next(); 366 try { 367 classes.add(Class.forName(className)); 368 } catch (Exception ex) { 369 classes.add(null); 370 } 371 } 372 return classes; 373 } 374 375 /** 376 * <p>Given a <code>List</code> of <code>Class</code> objects, this method converts 377 * them into class names.</p> 378 * 379 * <p>A new <code>List</code> is returned. <code>null</code> objects will be copied into 380 * the returned list as <code>null</code>.</p> 381 * 382 * @param classes the classes to change 383 * @return a <code>List</code> of class names corresponding to the Class objects, 384 * <code>null</code> if null input 385 * @throws ClassCastException if <code>classes</code> contains a non-<code>Class</code> entry 386 */ 387 public static List convertClassesToClassNames(List classes) { 388 if (classes == null) { 389 return null; 390 } 391 List classNames = new ArrayList(classes.size()); 392 for (Iterator it = classes.iterator(); it.hasNext();) { 393 Class cls = (Class) it.next(); 394 if (cls == null) { 395 classNames.add(null); 396 } else { 397 classNames.add(cls.getName()); 398 } 399 } 400 return classNames; 401 } 402 403 // Is assignable 404 // ---------------------------------------------------------------------- 405 /** 406 * <p>Checks if an array of Classes can be assigned to another array of Classes.</p> 407 * 408 * <p>This method calls {@link #isAssignable(Class, Class) isAssignable} for each 409 * Class pair in the input arrays. It can be used to check if a set of arguments 410 * (the first parameter) are suitably compatible with a set of method parameter types 411 * (the second parameter).</p> 412 * 413 * <p>Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this 414 * method takes into account widenings of primitive classes and 415 * <code>null</code>s.</p> 416 * 417 * <p>Primitive widenings allow an int to be assigned to a <code>long</code>, 418 * <code>float</code> or <code>double</code>. This method returns the correct 419 * result for these cases.</p> 420 * 421 * <p><code>Null</code> may be assigned to any reference type. This method will 422 * return <code>true</code> if <code>null</code> is passed in and the toClass is 423 * non-primitive.</p> 424 * 425 * <p>Specifically, this method tests whether the type represented by the 426 * specified <code>Class</code> parameter can be converted to the type 427 * represented by this <code>Class</code> object via an identity conversion 428 * widening primitive or widening reference conversion. See 429 * <em><a href="http://java.sun.com/docs/books/jls/">The Java Language Specification</a></em>, 430 * sections 5.1.1, 5.1.2 and 5.1.4 for details.</p> 431 * 432 * @param classArray the array of Classes to check, may be <code>null</code> 433 * @param toClassArray the array of Classes to try to assign into, may be <code>null</code> 434 * @return <code>true</code> if assignment possible 435 */ 436 public static boolean isAssignable(Class[] classArray, Class[] toClassArray) { 437 return isAssignable(classArray, toClassArray, false); 438 } 439 440 /** 441 * <p>Checks if an array of Classes can be assigned to another array of Classes.</p> 442 * 443 * <p>This method calls {@link #isAssignable(Class, Class) isAssignable} for each 444 * Class pair in the input arrays. It can be used to check if a set of arguments 445 * (the first parameter) are suitably compatible with a set of method parameter types 446 * (the second parameter).</p> 447 * 448 * <p>Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this 449 * method takes into account widenings of primitive classes and 450 * <code>null</code>s.</p> 451 * 452 * <p>Primitive widenings allow an int to be assigned to a <code>long</code>, 453 * <code>float</code> or <code>double</code>. This method returns the correct 454 * result for these cases.</p> 455 * 456 * <p><code>Null</code> may be assigned to any reference type. This method will 457 * return <code>true</code> if <code>null</code> is passed in and the toClass is 458 * non-primitive.</p> 459 * 460 * <p>Specifically, this method tests whether the type represented by the 461 * specified <code>Class</code> parameter can be converted to the type 462 * represented by this <code>Class</code> object via an identity conversion 463 * widening primitive or widening reference conversion. See 464 * <em><a href="http://java.sun.com/docs/books/jls/">The Java Language Specification</a></em>, 465 * sections 5.1.1, 5.1.2 and 5.1.4 for details.</p> 466 * 467 * @param classArray the array of Classes to check, may be <code>null</code> 468 * @param toClassArray the array of Classes to try to assign into, may be <code>null</code> 469 * @param autoboxing whether to use implicit autoboxing/unboxing between primitives and wrappers 470 * @return <code>true</code> if assignment possible 471 * @since 2.5 472 */ 473 public static boolean isAssignable(Class[] classArray, Class[] toClassArray, boolean autoboxing) { 474 if (ArrayUtils.isSameLength(classArray, toClassArray) == false) { 475 return false; 476 } 477 if (classArray == null) { 478 classArray = ArrayUtils.EMPTY_CLASS_ARRAY; 479 } 480 if (toClassArray == null) { 481 toClassArray = ArrayUtils.EMPTY_CLASS_ARRAY; 482 } 483 for (int i = 0; i < classArray.length; i++) { 484 if (isAssignable(classArray[i], toClassArray[i], autoboxing) == false) { 485 return false; 486 } 487 } 488 return true; 489 } 490 491 /** 492 * <p>Checks if one <code>Class</code> can be assigned to a variable of 493 * another <code>Class</code>.</p> 494 * 495 * <p>Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, 496 * this method takes into account widenings of primitive classes and 497 * <code>null</code>s.</p> 498 * 499 * <p>Primitive widenings allow an int to be assigned to a long, float or 500 * double. This method returns the correct result for these cases.</p> 501 * 502 * <p><code>Null</code> may be assigned to any reference type. This method 503 * will return <code>true</code> if <code>null</code> is passed in and the 504 * toClass is non-primitive.</p> 505 * 506 * <p>Specifically, this method tests whether the type represented by the 507 * specified <code>Class</code> parameter can be converted to the type 508 * represented by this <code>Class</code> object via an identity conversion 509 * widening primitive or widening reference conversion. See 510 * <em><a href="http://java.sun.com/docs/books/jls/">The Java Language Specification</a></em>, 511 * sections 5.1.1, 5.1.2 and 5.1.4 for details.</p> 512 * 513 * @param cls the Class to check, may be null 514 * @param toClass the Class to try to assign into, returns false if null 515 * @return <code>true</code> if assignment possible 516 */ 517 public static boolean isAssignable(Class cls, Class toClass) { 518 return isAssignable(cls, toClass, false); 519 } 520 521 /** 522 * <p>Checks if one <code>Class</code> can be assigned to a variable of 523 * another <code>Class</code>.</p> 524 * 525 * <p>Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, 526 * this method takes into account widenings of primitive classes and 527 * <code>null</code>s.</p> 528 * 529 * <p>Primitive widenings allow an int to be assigned to a long, float or 530 * double. This method returns the correct result for these cases.</p> 531 * 532 * <p><code>Null</code> may be assigned to any reference type. This method 533 * will return <code>true</code> if <code>null</code> is passed in and the 534 * toClass is non-primitive.</p> 535 * 536 * <p>Specifically, this method tests whether the type represented by the 537 * specified <code>Class</code> parameter can be converted to the type 538 * represented by this <code>Class</code> object via an identity conversion 539 * widening primitive or widening reference conversion. See 540 * <em><a href="http://java.sun.com/docs/books/jls/">The Java Language Specification</a></em>, 541 * sections 5.1.1, 5.1.2 and 5.1.4 for details.</p> 542 * 543 * @param cls the Class to check, may be null 544 * @param toClass the Class to try to assign into, returns false if null 545 * @param autoboxing whether to use implicit autoboxing/unboxing between primitives and wrappers 546 * @return <code>true</code> if assignment possible 547 * @since 2.5 548 */ 549 public static boolean isAssignable(Class cls, Class toClass, boolean autoboxing) { 550 if (toClass == null) { 551 return false; 552 } 553 // have to check for null, as isAssignableFrom doesn't 554 if (cls == null) { 555 return !(toClass.isPrimitive()); 556 } 557 //autoboxing: 558 if (autoboxing) { 559 if (cls.isPrimitive() && !toClass.isPrimitive()) { 560 cls = primitiveToWrapper(cls); 561 if (cls == null) { 562 return false; 563 } 564 } 565 if (toClass.isPrimitive() && !cls.isPrimitive()) { 566 cls = wrapperToPrimitive(cls); 567 if (cls == null) { 568 return false; 569 } 570 } 571 } 572 if (cls.equals(toClass)) { 573 return true; 574 } 575 if (cls.isPrimitive()) { 576 if (toClass.isPrimitive() == false) { 577 return false; 578 } 579 if (Integer.TYPE.equals(cls)) { 580 return Long.TYPE.equals(toClass) 581 || Float.TYPE.equals(toClass) 582 || Double.TYPE.equals(toClass); 583 } 584 if (Long.TYPE.equals(cls)) { 585 return Float.TYPE.equals(toClass) 586 || Double.TYPE.equals(toClass); 587 } 588 if (Boolean.TYPE.equals(cls)) { 589 return false; 590 } 591 if (Double.TYPE.equals(cls)) { 592 return false; 593 } 594 if (Float.TYPE.equals(cls)) { 595 return Double.TYPE.equals(toClass); 596 } 597 if (Character.TYPE.equals(cls)) { 598 return Integer.TYPE.equals(toClass) 599 || Long.TYPE.equals(toClass) 600 || Float.TYPE.equals(toClass) 601 || Double.TYPE.equals(toClass); 602 } 603 if (Short.TYPE.equals(cls)) { 604 return Integer.TYPE.equals(toClass) 605 || Long.TYPE.equals(toClass) 606 || Float.TYPE.equals(toClass) 607 || Double.TYPE.equals(toClass); 608 } 609 if (Byte.TYPE.equals(cls)) { 610 return Short.TYPE.equals(toClass) 611 || Integer.TYPE.equals(toClass) 612 || Long.TYPE.equals(toClass) 613 || Float.TYPE.equals(toClass) 614 || Double.TYPE.equals(toClass); 615 } 616 // should never get here 617 return false; 618 } 619 return toClass.isAssignableFrom(cls); 620 } 621 622 /** 623 * <p>Converts the specified primitive Class object to its corresponding 624 * wrapper Class object.</p> 625 * 626 * <p>NOTE: From v2.2, this method handles <code>Void.TYPE</code>, 627 * returning <code>Void.TYPE</code>.</p> 628 * 629 * @param cls the class to convert, may be null 630 * @return the wrapper class for <code>cls</code> or <code>cls</code> if 631 * <code>cls</code> is not a primitive. <code>null</code> if null input. 632 * @since 2.1 633 */ 634 public static Class primitiveToWrapper(Class cls) { 635 Class convertedClass = cls; 636 if (cls != null && cls.isPrimitive()) { 637 convertedClass = (Class) primitiveWrapperMap.get(cls); 638 } 639 return convertedClass; 640 } 641 642 /** 643 * <p>Converts the specified array of primitive Class objects to an array of 644 * its corresponding wrapper Class objects.</p> 645 * 646 * @param classes the class array to convert, may be null or empty 647 * @return an array which contains for each given class, the wrapper class or 648 * the original class if class is not a primitive. <code>null</code> if null input. 649 * Empty array if an empty array passed in. 650 * @since 2.1 651 */ 652 public static Class[] primitivesToWrappers(Class[] classes) { 653 if (classes == null) { 654 return null; 655 } 656 657 if (classes.length == 0) { 658 return classes; 659 } 660 661 Class[] convertedClasses = new Class[classes.length]; 662 for (int i = 0; i < classes.length; i++) { 663 convertedClasses[i] = primitiveToWrapper(classes[i]); 664 } 665 return convertedClasses; 666 } 667 668 /** 669 * <p>Converts the specified wrapper class to its corresponding primitive 670 * class.</p> 671 * 672 * <p>This method is the counter part of <code>primitiveToWrapper()</code>. 673 * If the passed in class is a wrapper class for a primitive type, this 674 * primitive type will be returned (e.g. <code>Integer.TYPE</code> for 675 * <code>Integer.class</code>). For other classes, or if the parameter is 676 * <b>null</b>, the return value is <b>null</b>.</p> 677 * 678 * @param cls the class to convert, may be <b>null</b> 679 * @return the corresponding primitive type if <code>cls</code> is a 680 * wrapper class, <b>null</b> otherwise 681 * @see #primitiveToWrapper(Class) 682 * @since 2.4 683 */ 684 public static Class wrapperToPrimitive(Class cls) { 685 return (Class) wrapperPrimitiveMap.get(cls); 686 } 687 688 /** 689 * <p>Converts the specified array of wrapper Class objects to an array of 690 * its corresponding primitive Class objects.</p> 691 * 692 * <p>This method invokes <code>wrapperToPrimitive()</code> for each element 693 * of the passed in array.</p> 694 * 695 * @param classes the class array to convert, may be null or empty 696 * @return an array which contains for each given class, the primitive class or 697 * <b>null</b> if the original class is not a wrapper class. <code>null</code> if null input. 698 * Empty array if an empty array passed in. 699 * @see #wrapperToPrimitive(Class) 700 * @since 2.4 701 */ 702 public static Class[] wrappersToPrimitives(Class[] classes) { 703 if (classes == null) { 704 return null; 705 } 706 707 if (classes.length == 0) { 708 return classes; 709 } 710 711 Class[] convertedClasses = new Class[classes.length]; 712 for (int i = 0; i < classes.length; i++) { 713 convertedClasses[i] = wrapperToPrimitive(classes[i]); 714 } 715 return convertedClasses; 716 } 717 718 // Inner class 719 // ---------------------------------------------------------------------- 720 /** 721 * <p>Is the specified class an inner class or static nested class.</p> 722 * 723 * @param cls the class to check, may be null 724 * @return <code>true</code> if the class is an inner or static nested class, 725 * false if not or <code>null</code> 726 */ 727 public static boolean isInnerClass(Class cls) { 728 if (cls == null) { 729 return false; 730 } 731 return cls.getName().indexOf(INNER_CLASS_SEPARATOR_CHAR) >= 0; 732 } 733 734 // Class loading 735 // ---------------------------------------------------------------------- 736 /** 737 * Returns the class represented by <code>className</code> using the 738 * <code>classLoader</code>. This implementation supports names like 739 * "<code>java.lang.String[]</code>" as well as "<code>[Ljava.lang.String;</code>". 740 * 741 * @param classLoader the class loader to use to load the class 742 * @param className the class name 743 * @param initialize whether the class must be initialized 744 * @return the class represented by <code>className</code> using the <code>classLoader</code> 745 * @throws ClassNotFoundException if the class is not found 746 */ 747 public static Class getClass( 748 ClassLoader classLoader, String className, boolean initialize) throws ClassNotFoundException { 749 Class clazz; 750 if (abbreviationMap.containsKey(className)) { 751 String clsName = "[" + abbreviationMap.get(className); 752 clazz = Class.forName(clsName, initialize, classLoader).getComponentType(); 753 } else { 754 clazz = Class.forName(toCanonicalName(className), initialize, classLoader); 755 } 756 return clazz; 757 } 758 759 /** 760 * Returns the (initialized) class represented by <code>className</code> 761 * using the <code>classLoader</code>. This implementation supports names 762 * like "<code>java.lang.String[]</code>" as well as 763 * "<code>[Ljava.lang.String;</code>". 764 * 765 * @param classLoader the class loader to use to load the class 766 * @param className the class name 767 * @return the class represented by <code>className</code> using the <code>classLoader</code> 768 * @throws ClassNotFoundException if the class is not found 769 */ 770 public static Class getClass(ClassLoader classLoader, String className) throws ClassNotFoundException { 771 return getClass(classLoader, className, true); 772 } 773 774 /** 775 * Returns the (initialized) class represented by <code>className</code> 776 * using the current thread's context class loader. This implementation 777 * supports names like "<code>java.lang.String[]</code>" as well as 778 * "<code>[Ljava.lang.String;</code>". 779 * 780 * @param className the class name 781 * @return the class represented by <code>className</code> using the current thread's context class loader 782 * @throws ClassNotFoundException if the class is not found 783 */ 784 public static Class getClass(String className) throws ClassNotFoundException { 785 return getClass(className, true); 786 } 787 788 /** 789 * Returns the class represented by <code>className</code> using the 790 * current thread's context class loader. This implementation supports 791 * names like "<code>java.lang.String[]</code>" as well as 792 * "<code>[Ljava.lang.String;</code>". 793 * 794 * @param className the class name 795 * @param initialize whether the class must be initialized 796 * @return the class represented by <code>className</code> using the current thread's context class loader 797 * @throws ClassNotFoundException if the class is not found 798 */ 799 public static Class getClass(String className, boolean initialize) throws ClassNotFoundException { 800 ClassLoader contextCL = Thread.currentThread().getContextClassLoader(); 801 ClassLoader loader = contextCL == null ? ClassUtils.class.getClassLoader() : contextCL; 802 return getClass(loader, className, initialize ); 803 } 804 805 // Public method 806 // ---------------------------------------------------------------------- 807 /** 808 * <p>Returns the desired Method much like <code>Class.getMethod</code>, however 809 * it ensures that the returned Method is from a public class or interface and not 810 * from an anonymous inner class. This means that the Method is invokable and 811 * doesn't fall foul of Java bug 812 * <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4071957">4071957</a>). 813 * 814 * <code><pre>Set set = Collections.unmodifiableSet(...); 815 * Method method = ClassUtils.getPublicMethod(set.getClass(), "isEmpty", new Class[0]); 816 * Object result = method.invoke(set, new Object[]);</pre></code> 817 * </p> 818 * 819 * @param cls the class to check, not null 820 * @param methodName the name of the method 821 * @param parameterTypes the list of parameters 822 * @return the method 823 * @throws NullPointerException if the class is null 824 * @throws SecurityException if a a security violation occured 825 * @throws NoSuchMethodException if the method is not found in the given class 826 * or if the metothod doen't conform with the requirements 827 */ 828 public static Method getPublicMethod(Class cls, String methodName, Class parameterTypes[]) 829 throws SecurityException, NoSuchMethodException { 830 831 Method declaredMethod = cls.getMethod(methodName, parameterTypes); 832 if (Modifier.isPublic(declaredMethod.getDeclaringClass().getModifiers())) { 833 return declaredMethod; 834 } 835 836 List candidateClasses = new ArrayList(); 837 candidateClasses.addAll(getAllInterfaces(cls)); 838 candidateClasses.addAll(getAllSuperclasses(cls)); 839 840 for (Iterator it = candidateClasses.iterator(); it.hasNext(); ) { 841 Class candidateClass = (Class) it.next(); 842 if (!Modifier.isPublic(candidateClass.getModifiers())) { 843 continue; 844 } 845 Method candidateMethod; 846 try { 847 candidateMethod = candidateClass.getMethod(methodName, parameterTypes); 848 } catch (NoSuchMethodException ex) { 849 continue; 850 } 851 if (Modifier.isPublic(candidateMethod.getDeclaringClass().getModifiers())) { 852 return candidateMethod; 853 } 854 } 855 856 throw new NoSuchMethodException("Can't find a public method for " + 857 methodName + " " + ArrayUtils.toString(parameterTypes)); 858 } 859 860 // ---------------------------------------------------------------------- 861 /** 862 * Converts a class name to a JLS style class name. 863 * 864 * @param className the class name 865 * @return the converted name 866 */ 867 private static String toCanonicalName(String className) { 868 className = StringUtils.deleteWhitespace(className); 869 if (className == null) { 870 throw new NullArgumentException("className"); 871 } else if (className.endsWith("[]")) { 872 StringBuffer classNameBuffer = new StringBuffer(); 873 while (className.endsWith("[]")) { 874 className = className.substring(0, className.length() - 2); 875 classNameBuffer.append("["); 876 } 877 String abbreviation = (String) abbreviationMap.get(className); 878 if (abbreviation != null) { 879 classNameBuffer.append(abbreviation); 880 } else { 881 classNameBuffer.append("L").append(className).append(";"); 882 } 883 className = classNameBuffer.toString(); 884 } 885 return className; 886 } 887 888 /** 889 * <p>Converts an array of <code>Object</code> in to an array of <code>Class</code> objects. 890 * If any of these objects is null, a null element will be inserted into the array.</p> 891 * 892 * <p>This method returns <code>null</code> for a <code>null</code> input array.</p> 893 * 894 * @param array an <code>Object</code> array 895 * @return a <code>Class</code> array, <code>null</code> if null array input 896 * @since 2.4 897 */ 898 public static Class[] toClass(Object[] array) { 899 if (array == null) { 900 return null; 901 } else if (array.length == 0) { 902 return ArrayUtils.EMPTY_CLASS_ARRAY; 903 } 904 Class[] classes = new Class[array.length]; 905 for (int i = 0; i < array.length; i++) { 906 classes[i] = array[i] == null ? null : array[i].getClass(); 907 } 908 return classes; 909 } 910 911 // Short canonical name 912 // ---------------------------------------------------------------------- 913 /** 914 * <p>Gets the canonical name minus the package name for an <code>Object</code>.</p> 915 * 916 * @param object the class to get the short name for, may be null 917 * @param valueIfNull the value to return if null 918 * @return the canonical name of the object without the package name, or the null value 919 * @since 2.4 920 */ 921 public static String getShortCanonicalName(Object object, String valueIfNull) { 922 if (object == null) { 923 return valueIfNull; 924 } 925 return getShortCanonicalName(object.getClass().getName()); 926 } 927 928 /** 929 * <p>Gets the canonical name minus the package name from a <code>Class</code>.</p> 930 * 931 * @param cls the class to get the short name for. 932 * @return the canonical name without the package name or an empty string 933 * @since 2.4 934 */ 935 public static String getShortCanonicalName(Class cls) { 936 if (cls == null) { 937 return StringUtils.EMPTY; 938 } 939 return getShortCanonicalName(cls.getName()); 940 } 941 942 /** 943 * <p>Gets the canonical name minus the package name from a String.</p> 944 * 945 * <p>The string passed in is assumed to be a canonical name - it is not checked.</p> 946 * 947 * @param canonicalName the class name to get the short name for 948 * @return the canonical name of the class without the package name or an empty string 949 * @since 2.4 950 */ 951 public static String getShortCanonicalName(String canonicalName) { 952 return ClassUtils.getShortClassName(getCanonicalName(canonicalName)); 953 } 954 955 // Package name 956 // ---------------------------------------------------------------------- 957 /** 958 * <p>Gets the package name from the canonical name of an <code>Object</code>.</p> 959 * 960 * @param object the class to get the package name for, may be null 961 * @param valueIfNull the value to return if null 962 * @return the package name of the object, or the null value 963 * @since 2.4 964 */ 965 public static String getPackageCanonicalName(Object object, String valueIfNull) { 966 if (object == null) { 967 return valueIfNull; 968 } 969 return getPackageCanonicalName(object.getClass().getName()); 970 } 971 972 /** 973 * <p>Gets the package name from the canonical name of a <code>Class</code>.</p> 974 * 975 * @param cls the class to get the package name for, may be <code>null</code>. 976 * @return the package name or an empty string 977 * @since 2.4 978 */ 979 public static String getPackageCanonicalName(Class cls) { 980 if (cls == null) { 981 return StringUtils.EMPTY; 982 } 983 return getPackageCanonicalName(cls.getName()); 984 } 985 986 /** 987 * <p>Gets the package name from the canonical name. </p> 988 * 989 * <p>The string passed in is assumed to be a canonical name - it is not checked.</p> 990 * <p>If the class is unpackaged, return an empty string.</p> 991 * 992 * @param canonicalName the canonical name to get the package name for, may be <code>null</code> 993 * @return the package name or an empty string 994 * @since 2.4 995 */ 996 public static String getPackageCanonicalName(String canonicalName) { 997 return ClassUtils.getPackageName(getCanonicalName(canonicalName)); 998 } 999 1000 /** 1001 * <p>Converts a given name of class into canonical format. 1002 * If name of class is not a name of array class it returns 1003 * unchanged name.</p> 1004 * <p>Example: 1005 * <ul> 1006 * <li><code>getCanonicalName("[I") = "int[]"</code></li> 1007 * <li><code>getCanonicalName("[Ljava.lang.String;") = "java.lang.String[]"</code></li> 1008 * <li><code>getCanonicalName("java.lang.String") = "java.lang.String"</code></li> 1009 * </ul> 1010 * </p> 1011 * 1012 * @param className the name of class 1013 * @return canonical form of class name 1014 * @since 2.4 1015 */ 1016 private static String getCanonicalName(String className) { 1017 className = StringUtils.deleteWhitespace(className); 1018 if (className == null) { 1019 return null; 1020 } else { 1021 int dim = 0; 1022 while (className.startsWith("[")) { 1023 dim++; 1024 className = className.substring(1); 1025 } 1026 if (dim < 1) { 1027 return className; 1028 } else { 1029 if (className.startsWith("L")) { 1030 className = className.substring( 1031 1, 1032 className.endsWith(";") 1033 ? className.length() - 1 1034 : className.length()); 1035 } else { 1036 if (className.length() > 0) { 1037 className = (String) reverseAbbreviationMap.get( 1038 className.substring(0, 1)); 1039 } 1040 } 1041 StringBuffer canonicalClassNameBuffer = new StringBuffer(className); 1042 for (int i = 0; i < dim; i++) { 1043 canonicalClassNameBuffer.append("[]"); 1044 } 1045 return canonicalClassNameBuffer.toString(); 1046 } 1047 } 1048 } 1049 }