View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.lang;
18  
19  import java.lang.reflect.Method;
20  import java.lang.reflect.Modifier;
21  import java.util.ArrayList;
22  import java.util.HashMap;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Map;
26  
27  
28  /**
29   * <p>Operates on classes without using reflection.</p>
30   *
31   * <p>This class handles invalid <code>null</code> inputs as best it can.
32   * Each method documents its behaviour in more detail.</p>
33   *
34   * @author Stephen Colebourne
35   * @author Gary Gregory
36   * @author Norm Deane
37   * @author Alban Peignier
38   * @author Tomasz Blachowicz
39   * @since 2.0
40   * @version $Id: ClassUtils.java 633621 2008-03-04 20:20:23Z mbenson $
41   */
42  public class ClassUtils {
43  
44      /**
45       * <p>The package separator character: <code>'&#x2e;' == {@value}</code>.</p>
46       */
47      public static final char PACKAGE_SEPARATOR_CHAR = '.';
48  
49      /**
50       * <p>The package separator String: <code>"&#x2e;"</code>.</p>
51       */
52      public static final String PACKAGE_SEPARATOR = String.valueOf(PACKAGE_SEPARATOR_CHAR);
53  
54      /**
55       * <p>The inner class separator character: <code>'$' == {@value}</code>.</p>
56       */
57      public static final char INNER_CLASS_SEPARATOR_CHAR = '$';
58  
59      /**
60       * <p>The inner class separator String: <code>"$"</code>.</p>
61       */
62      public static final String INNER_CLASS_SEPARATOR = String.valueOf(INNER_CLASS_SEPARATOR_CHAR);
63  
64      /**
65       * Maps primitive <code>Class</code>es to their corresponding wrapper <code>Class</code>.
66       */
67      private static Map primitiveWrapperMap = new HashMap();
68      static {
69           primitiveWrapperMap.put(Boolean.TYPE, Boolean.class);
70           primitiveWrapperMap.put(Byte.TYPE, Byte.class);
71           primitiveWrapperMap.put(Character.TYPE, Character.class);
72           primitiveWrapperMap.put(Short.TYPE, Short.class);
73           primitiveWrapperMap.put(Integer.TYPE, Integer.class);
74           primitiveWrapperMap.put(Long.TYPE, Long.class);
75           primitiveWrapperMap.put(Double.TYPE, Double.class);
76           primitiveWrapperMap.put(Float.TYPE, Float.class);
77           primitiveWrapperMap.put(Void.TYPE, Void.TYPE);
78      }
79  
80      /**
81       * Maps wrapper <code>Class</code>es to their corresponding primitive types.
82       */
83      private static Map wrapperPrimitiveMap = new HashMap();
84      static {
85          for (Iterator it = primitiveWrapperMap.keySet().iterator(); it.hasNext();) {
86              Class primitiveClass = (Class) it.next();
87              Class wrapperClass = (Class) primitiveWrapperMap.get(primitiveClass);
88              if (!primitiveClass.equals(wrapperClass)) {
89                  wrapperPrimitiveMap.put(wrapperClass, primitiveClass);
90              }
91          }
92      }
93  
94      /**
95       * Maps a primitive class name to its corresponding abbreviation used in array class names.
96       */
97      private static Map abbreviationMap = new HashMap();
98  
99      /**
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 }