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