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.lang3;
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.HashSet;
024    import java.util.LinkedHashSet;
025    import java.util.List;
026    import java.util.Map;
027    
028    
029    /**
030     * <p>Operates on classes without using reflection.</p>
031     *
032     * <p>This class handles invalid <code>null</code> inputs as best it can.
033     * Each method documents its behaviour in more detail.</p>
034     *
035     * <p>The notion of a <code>canonical name</code> includes the human
036     * readable name for the type, for example <code>int[]</code>. The
037     * non-canonical method variants work with the JVM names, such as
038     * <code>[I</code>. </p>
039     *
040     * @author Apache Software Foundation
041     * @author Gary Gregory
042     * @author Norm Deane
043     * @author Alban Peignier
044     * @author Tomasz Blachowicz
045     * @since 2.0
046     * @version $Id: ClassUtils.java 919340 2010-03-05 09:05:47Z bayard $
047     */
048    public class ClassUtils {
049    
050        /**
051         * <p>The package separator character: <code>'&#x2e;' == {@value}</code>.</p>
052         */
053        public static final char PACKAGE_SEPARATOR_CHAR = '.';
054    
055        /**
056         * <p>The package separator String: <code>"&#x2e;"</code>.</p>
057         */
058        public static final String PACKAGE_SEPARATOR = String.valueOf(PACKAGE_SEPARATOR_CHAR);
059    
060        /**
061         * <p>The inner class separator character: <code>'$' == {@value}</code>.</p>
062         */
063        public static final char INNER_CLASS_SEPARATOR_CHAR = '$';
064    
065        /**
066         * <p>The inner class separator String: <code>"$"</code>.</p>
067         */
068        public static final String INNER_CLASS_SEPARATOR = String.valueOf(INNER_CLASS_SEPARATOR_CHAR);
069    
070        /**
071         * Maps primitive <code>Class</code>es to their corresponding wrapper <code>Class</code>.
072         */
073        private static final Map<Class<?>, Class<?>> primitiveWrapperMap = new HashMap<Class<?>, Class<?>>();
074        static {
075             primitiveWrapperMap.put(Boolean.TYPE, Boolean.class);
076             primitiveWrapperMap.put(Byte.TYPE, Byte.class);
077             primitiveWrapperMap.put(Character.TYPE, Character.class);
078             primitiveWrapperMap.put(Short.TYPE, Short.class);
079             primitiveWrapperMap.put(Integer.TYPE, Integer.class);
080             primitiveWrapperMap.put(Long.TYPE, Long.class);
081             primitiveWrapperMap.put(Double.TYPE, Double.class);
082             primitiveWrapperMap.put(Float.TYPE, Float.class);
083             primitiveWrapperMap.put(Void.TYPE, Void.TYPE);
084        }
085    
086        /**
087         * Maps wrapper <code>Class</code>es to their corresponding primitive types.
088         */
089        private static final Map<Class<?>, Class<?>> wrapperPrimitiveMap = new HashMap<Class<?>, Class<?>>();
090        static {
091            for (Class<?> primitiveClass : primitiveWrapperMap.keySet()) {
092                Class<?> wrapperClass = 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<String, String> abbreviationMap = new HashMap<String, String>();
103    
104        /**
105         * Maps an abbreviation used in array class names to corresponding primitive class name.
106         */
107        private static final Map<String, String> reverseAbbreviationMap = new HashMap<String, String>();
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            StringBuilder arrayPrefix = new StringBuilder();
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 = 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<Class<?>> getAllSuperclasses(Class<?> cls) {
288            if (cls == null) {
289                return null;
290            }
291            List<Class<?>> classes = new ArrayList<Class<?>>();
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<Class<?>> getAllInterfaces(Class<?> cls) {
314            if (cls == null) {
315                return null;
316            }
317    
318            LinkedHashSet<Class<?>> interfacesFound = new LinkedHashSet<Class<?>>();
319            getAllInterfaces(cls, interfacesFound);
320    
321            return new ArrayList<Class<?>>(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, HashSet<Class<?>> interfacesFound) {
331            while (cls != null) {
332                Class<?>[] interfaces = cls.getInterfaces();
333    
334                for (Class<?> i : interfaces) {
335                    if (interfacesFound.add(i)) {
336                        getAllInterfaces(i, interfacesFound);
337                    }
338                }
339    
340                cls = cls.getSuperclass();
341             }
342         }
343    
344        // Convert list
345        // ----------------------------------------------------------------------
346        /**
347         * <p>Given a <code>List</code> of class names, this method converts them into classes.</p>
348         *
349         * <p>A new <code>List</code> is returned. If the class name cannot be found, <code>null</code>
350         * is stored in the <code>List</code>. If the class name in the <code>List</code> is
351         * <code>null</code>, <code>null</code> is stored in the output <code>List</code>.</p>
352         *
353         * @param classNames  the classNames to change
354         * @return a <code>List</code> of Class objects corresponding to the class names,
355         *  <code>null</code> if null input
356         * @throws ClassCastException if classNames contains a non String entry
357         */
358        public static List<Class<?>> convertClassNamesToClasses(List<String> classNames) {
359            if (classNames == null) {
360                return null;
361            }
362            List<Class<?>> classes = new ArrayList<Class<?>>(classNames.size());
363            for (String className : classNames) {
364                try {
365                    classes.add(Class.forName(className));
366                } catch (Exception ex) {
367                    classes.add(null);
368                }
369            }
370            return classes;
371        }
372    
373        /**
374         * <p>Given a <code>List</code> of <code>Class</code> objects, this method converts
375         * them into class names.</p>
376         *
377         * <p>A new <code>List</code> is returned. <code>null</code> objects will be copied into
378         * the returned list as <code>null</code>.</p>
379         *
380         * @param classes  the classes to change
381         * @return a <code>List</code> of class names corresponding to the Class objects,
382         *  <code>null</code> if null input
383         * @throws ClassCastException if <code>classes</code> contains a non-<code>Class</code> entry
384         */
385        public static List<String> convertClassesToClassNames(List<Class<?>> classes) {
386            if (classes == null) {
387                return null;
388            }
389            List<String> classNames = new ArrayList<String>(classes.size());
390            for (Class<?> cls : classes) {
391                if (cls == null) {
392                    classNames.add(null);
393                } else {
394                    classNames.add(cls.getName());
395                }
396            }
397            return classNames;
398        }
399    
400        // Is assignable
401        // ----------------------------------------------------------------------
402        /**
403         * <p>Checks if an array of Classes can be assigned to another array of Classes.</p>
404         *
405         * <p>This method calls {@link #isAssignable(Class, Class) isAssignable} for each
406         * Class pair in the input arrays. It can be used to check if a set of arguments
407         * (the first parameter) are suitably compatible with a set of method parameter types
408         * (the second parameter).</p>
409         *
410         * <p>Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this
411         * method takes into account widenings of primitive classes and
412         * <code>null</code>s.</p>
413         *
414         * <p>Primitive widenings allow an int to be assigned to a <code>long</code>,
415         * <code>float</code> or <code>double</code>. This method returns the correct
416         * result for these cases.</p>
417         *
418         * <p><code>Null</code> may be assigned to any reference type. This method will
419         * return <code>true</code> if <code>null</code> is passed in and the toClass is
420         * non-primitive.</p>
421         *
422         * <p>Specifically, this method tests whether the type represented by the
423         * specified <code>Class</code> parameter can be converted to the type
424         * represented by this <code>Class</code> object via an identity conversion
425         * widening primitive or widening reference conversion. See
426         * <em><a href="http://java.sun.com/docs/books/jls/">The Java Language Specification</a></em>,
427         * sections 5.1.1, 5.1.2 and 5.1.4 for details.</p>
428         *
429         * <p><strong>Since Lang 3.0,</strong> this method will default behavior for
430         * calculating assignability between primitive and wrapper types <em>corresponding
431         * to the running Java version</em>; i.e. autoboxing will be the default
432         * behavior in VMs running Java versions >= 1.5.</p>
433         *
434         * @param classArray  the array of Classes to check, may be <code>null</code>
435         * @param toClassArray  the array of Classes to try to assign into, may be <code>null</code>
436         * @return <code>true</code> if assignment possible
437         */
438        public static boolean isAssignable(Class<?>[] classArray, Class<?>[] toClassArray) {
439            return isAssignable(classArray, toClassArray, SystemUtils.isJavaVersionAtLeast(1.5f));
440        }
441    
442        /**
443         * <p>Checks if an array of Classes can be assigned to another array of Classes.</p>
444         *
445         * <p>This method calls {@link #isAssignable(Class, Class) isAssignable} for each
446         * Class pair in the input arrays. It can be used to check if a set of arguments
447         * (the first parameter) are suitably compatible with a set of method parameter types
448         * (the second parameter).</p>
449         *
450         * <p>Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this
451         * method takes into account widenings of primitive classes and
452         * <code>null</code>s.</p>
453         *
454         * <p>Primitive widenings allow an int to be assigned to a <code>long</code>,
455         * <code>float</code> or <code>double</code>. This method returns the correct
456         * result for these cases.</p>
457         *
458         * <p><code>Null</code> may be assigned to any reference type. This method will
459         * return <code>true</code> if <code>null</code> is passed in and the toClass is
460         * non-primitive.</p>
461         *
462         * <p>Specifically, this method tests whether the type represented by the
463         * specified <code>Class</code> parameter can be converted to the type
464         * represented by this <code>Class</code> object via an identity conversion
465         * widening primitive or widening reference conversion. See
466         * <em><a href="http://java.sun.com/docs/books/jls/">The Java Language Specification</a></em>,
467         * sections 5.1.1, 5.1.2 and 5.1.4 for details.</p>
468         *
469         * @param classArray  the array of Classes to check, may be <code>null</code>
470         * @param toClassArray  the array of Classes to try to assign into, may be <code>null</code>
471         * @param autoboxing  whether to use implicit autoboxing/unboxing between primitives and wrappers
472         * @return <code>true</code> if assignment possible
473         */
474        public static boolean isAssignable(Class<?>[] classArray, Class<?>[] toClassArray, boolean autoboxing) {
475            if (ArrayUtils.isSameLength(classArray, toClassArray) == false) {
476                return false;
477            }
478            if (classArray == null) {
479                classArray = ArrayUtils.EMPTY_CLASS_ARRAY;
480            }
481            if (toClassArray == null) {
482                toClassArray = ArrayUtils.EMPTY_CLASS_ARRAY;
483            }
484            for (int i = 0; i < classArray.length; i++) {
485                if (isAssignable(classArray[i], toClassArray[i], autoboxing) == false) {
486                    return false;
487                }
488            }
489            return true;
490        }
491    
492        /**
493         * <p>Checks if one <code>Class</code> can be assigned to a variable of
494         * another <code>Class</code>.</p>
495         *
496         * <p>Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method,
497         * this method takes into account widenings of primitive classes and
498         * <code>null</code>s.</p>
499         *
500         * <p>Primitive widenings allow an int to be assigned to a long, float or
501         * double. This method returns the correct result for these cases.</p>
502         *
503         * <p><code>Null</code> may be assigned to any reference type. This method
504         * will return <code>true</code> if <code>null</code> is passed in and the
505         * toClass is non-primitive.</p>
506         *
507         * <p>Specifically, this method tests whether the type represented by the
508         * specified <code>Class</code> parameter can be converted to the type
509         * represented by this <code>Class</code> object via an identity conversion
510         * widening primitive or widening reference conversion. See
511         * <em><a href="http://java.sun.com/docs/books/jls/">The Java Language Specification</a></em>,
512         * sections 5.1.1, 5.1.2 and 5.1.4 for details.</p>
513         *
514         * <p><strong>Since Lang 3.0,</strong> this method will default behavior for
515         * calculating assignability between primitive and wrapper types <em>corresponding
516         * to the running Java version</em>; i.e. autoboxing will be the default
517         * behavior in VMs running Java versions >= 1.5.</p>
518         *
519         * @param cls  the Class to check, may be null
520         * @param toClass  the Class to try to assign into, returns false if null
521         * @return <code>true</code> if assignment possible
522         */
523        public static boolean isAssignable(Class<?> cls, Class<?> toClass) {
524            return isAssignable(cls, toClass, SystemUtils.isJavaVersionAtLeast(1.5f));
525        }
526    
527        /**
528         * <p>Checks if one <code>Class</code> can be assigned to a variable of
529         * another <code>Class</code>.</p>
530         *
531         * <p>Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method,
532         * this method takes into account widenings of primitive classes and
533         * <code>null</code>s.</p>
534         *
535         * <p>Primitive widenings allow an int to be assigned to a long, float or
536         * double. This method returns the correct result for these cases.</p>
537         *
538         * <p><code>Null</code> may be assigned to any reference type. This method
539         * will return <code>true</code> if <code>null</code> is passed in and the
540         * toClass is non-primitive.</p>
541         *
542         * <p>Specifically, this method tests whether the type represented by the
543         * specified <code>Class</code> parameter can be converted to the type
544         * represented by this <code>Class</code> object via an identity conversion
545         * widening primitive or widening reference conversion. See
546         * <em><a href="http://java.sun.com/docs/books/jls/">The Java Language Specification</a></em>,
547         * sections 5.1.1, 5.1.2 and 5.1.4 for details.</p>
548         *
549         * @param cls  the Class to check, may be null
550         * @param toClass  the Class to try to assign into, returns false if null
551         * @param autoboxing  whether to use implicit autoboxing/unboxing between primitives and wrappers
552         * @return <code>true</code> if assignment possible
553         */
554        public static boolean isAssignable(Class<?> cls, Class<?> toClass, boolean autoboxing) {
555            if (toClass == null) {
556                return false;
557            }
558            // have to check for null, as isAssignableFrom doesn't
559            if (cls == null) {
560                return !(toClass.isPrimitive());
561            }
562            //autoboxing:
563            if (autoboxing) {
564                if (cls.isPrimitive() && !toClass.isPrimitive()) {
565                    cls = primitiveToWrapper(cls);
566                    if (cls == null) {
567                        return false;
568                    }
569                }
570                if (toClass.isPrimitive() && !cls.isPrimitive()) {
571                    cls = wrapperToPrimitive(cls);
572                    if (cls == null) {
573                        return false;
574                    }
575                }
576            }
577            if (cls.equals(toClass)) {
578                return true;
579            }
580            if (cls.isPrimitive()) {
581                if (toClass.isPrimitive() == false) {
582                    return false;
583                }
584                if (Integer.TYPE.equals(cls)) {
585                    return Long.TYPE.equals(toClass)
586                        || Float.TYPE.equals(toClass)
587                        || Double.TYPE.equals(toClass);
588                }
589                if (Long.TYPE.equals(cls)) {
590                    return Float.TYPE.equals(toClass)
591                        || Double.TYPE.equals(toClass);
592                }
593                if (Boolean.TYPE.equals(cls)) {
594                    return false;
595                }
596                if (Double.TYPE.equals(cls)) {
597                    return false;
598                }
599                if (Float.TYPE.equals(cls)) {
600                    return Double.TYPE.equals(toClass);
601                }
602                if (Character.TYPE.equals(cls)) {
603                    return Integer.TYPE.equals(toClass)
604                        || Long.TYPE.equals(toClass)
605                        || Float.TYPE.equals(toClass)
606                        || Double.TYPE.equals(toClass);
607                }
608                if (Short.TYPE.equals(cls)) {
609                    return Integer.TYPE.equals(toClass)
610                        || Long.TYPE.equals(toClass)
611                        || Float.TYPE.equals(toClass)
612                        || Double.TYPE.equals(toClass);
613                }
614                if (Byte.TYPE.equals(cls)) {
615                    return Short.TYPE.equals(toClass)
616                        || Integer.TYPE.equals(toClass)
617                        || Long.TYPE.equals(toClass)
618                        || Float.TYPE.equals(toClass)
619                        || Double.TYPE.equals(toClass);
620                }
621                // should never get here
622                return false;
623            }
624            return toClass.isAssignableFrom(cls);
625        }
626    
627        /**
628         * <p>Converts the specified primitive Class object to its corresponding
629         * wrapper Class object.</p>
630         *
631         * <p>NOTE: From v2.2, this method handles <code>Void.TYPE</code>,
632         * returning <code>Void.TYPE</code>.</p>
633         *
634         * @param cls  the class to convert, may be null
635         * @return the wrapper class for <code>cls</code> or <code>cls</code> if
636         * <code>cls</code> is not a primitive. <code>null</code> if null input.
637         * @since 2.1
638         */
639        public static Class<?> primitiveToWrapper(Class<?> cls) {
640            Class<?> convertedClass = cls;
641            if (cls != null && cls.isPrimitive()) {
642                convertedClass = primitiveWrapperMap.get(cls);
643            }
644            return convertedClass;
645        }
646    
647        /**
648         * <p>Converts the specified array of primitive Class objects to an array of
649         * its corresponding wrapper Class objects.</p>
650         *
651         * @param classes  the class array to convert, may be null or empty
652         * @return an array which contains for each given class, the wrapper class or
653         * the original class if class is not a primitive. <code>null</code> if null input.
654         * Empty array if an empty array passed in.
655         * @since 2.1
656         */
657        public static Class<?>[] primitivesToWrappers(Class<?>[] classes) {
658            if (classes == null) {
659                return null;
660            }
661    
662            if (classes.length == 0) {
663                return classes;
664            }
665    
666            Class<?>[] convertedClasses = new Class[classes.length];
667            for (int i = 0; i < classes.length; i++) {
668                convertedClasses[i] = primitiveToWrapper(classes[i]);
669            }
670            return convertedClasses;
671        }
672    
673        /**
674         * <p>Converts the specified wrapper class to its corresponding primitive
675         * class.</p>
676         *
677         * <p>This method is the counter part of <code>primitiveToWrapper()</code>.
678         * If the passed in class is a wrapper class for a primitive type, this
679         * primitive type will be returned (e.g. <code>Integer.TYPE</code> for
680         * <code>Integer.class</code>). For other classes, or if the parameter is
681         * <b>null</b>, the return value is <b>null</b>.</p>
682         *
683         * @param cls the class to convert, may be <b>null</b>
684         * @return the corresponding primitive type if <code>cls</code> is a
685         * wrapper class, <b>null</b> otherwise
686         * @see #primitiveToWrapper(Class)
687         * @since 2.4
688         */
689        public static Class<?> wrapperToPrimitive(Class<?> cls) {
690            return wrapperPrimitiveMap.get(cls);
691        }
692    
693        /**
694         * <p>Converts the specified array of wrapper Class objects to an array of
695         * its corresponding primitive Class objects.</p>
696         *
697         * <p>This method invokes <code>wrapperToPrimitive()</code> for each element
698         * of the passed in array.</p>
699         *
700         * @param classes  the class array to convert, may be null or empty
701         * @return an array which contains for each given class, the primitive class or
702         * <b>null</b> if the original class is not a wrapper class. <code>null</code> if null input.
703         * Empty array if an empty array passed in.
704         * @see #wrapperToPrimitive(Class)
705         * @since 2.4
706         */
707        public static Class<?>[] wrappersToPrimitives(Class<?>[] classes) {
708            if (classes == null) {
709                return null;
710            }
711    
712            if (classes.length == 0) {
713                return classes;
714            }
715    
716            Class<?>[] convertedClasses = new Class[classes.length];
717            for (int i = 0; i < classes.length; i++) {
718                convertedClasses[i] = wrapperToPrimitive(classes[i]);
719            }
720            return convertedClasses;
721        }
722    
723        // Inner class
724        // ----------------------------------------------------------------------
725        /**
726         * <p>Is the specified class an inner class or static nested class.</p>
727         *
728         * @param cls  the class to check, may be null
729         * @return <code>true</code> if the class is an inner or static nested class,
730         *  false if not or <code>null</code>
731         */
732        public static boolean isInnerClass(Class<?> cls) {
733            if (cls == null) {
734                return false;
735            }
736            return cls.getName().indexOf(INNER_CLASS_SEPARATOR_CHAR) >= 0;
737        }
738    
739        // Class loading
740        // ----------------------------------------------------------------------
741        /**
742         * Returns the class represented by <code>className</code> using the
743         * <code>classLoader</code>.  This implementation supports the syntaxes
744         * "<code>java.util.Map.Entry[]</code>", "<code>java.util.Map$Entry[]</code>",
745         * "<code>[Ljava.util.Map.Entry;</code>", and "<code>[Ljava.util.Map$Entry;</code>".
746         *
747         * @param classLoader  the class loader to use to load the class
748         * @param className  the class name
749         * @param initialize  whether the class must be initialized
750         * @return the class represented by <code>className</code> using the <code>classLoader</code>
751         * @throws ClassNotFoundException if the class is not found
752         */
753        public static Class<?> getClass(
754                ClassLoader classLoader, String className, boolean initialize) throws ClassNotFoundException {
755            try {
756                Class<?> clazz;
757                if (abbreviationMap.containsKey(className)) {
758                    String clsName = "[" + abbreviationMap.get(className);
759                    clazz = Class.forName(clsName, initialize, classLoader).getComponentType();
760                } else {
761                    clazz = Class.forName(toCanonicalName(className), initialize, classLoader);
762                }
763                return clazz;
764            } catch (ClassNotFoundException ex) {
765                // allow path separators (.) as inner class name separators
766                int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR);
767    
768                if (lastDotIndex != -1) {
769                    try {
770                        return getClass(classLoader, className.substring(0, lastDotIndex) +
771                                INNER_CLASS_SEPARATOR_CHAR + className.substring(lastDotIndex + 1),
772                                initialize);
773                    } catch (ClassNotFoundException ex2) {
774                    }
775                }
776    
777                throw ex;
778            }
779        }
780    
781        /**
782         * Returns the (initialized) class represented by <code>className</code>
783         * using the <code>classLoader</code>.  This implementation supports
784         * the syntaxes "<code>java.util.Map.Entry[]</code>",
785         * "<code>java.util.Map$Entry[]</code>", "<code>[Ljava.util.Map.Entry;</code>",
786         * and "<code>[Ljava.util.Map$Entry;</code>".
787         *
788         * @param classLoader  the class loader to use to load the class
789         * @param className  the class name
790         * @return the class represented by <code>className</code> using the <code>classLoader</code>
791         * @throws ClassNotFoundException if the class is not found
792         */
793        public static Class<?> getClass(ClassLoader classLoader, String className) throws ClassNotFoundException {
794            return getClass(classLoader, className, true);
795        }
796    
797        /**
798         * Returns the (initialized) class represented by <code>className</code>
799         * using the current thread's context class loader. This implementation
800         * supports the syntaxes "<code>java.util.Map.Entry[]</code>",
801         * "<code>java.util.Map$Entry[]</code>", "<code>[Ljava.util.Map.Entry;</code>",
802         * and "<code>[Ljava.util.Map$Entry;</code>".
803         *
804         * @param className  the class name
805         * @return the class represented by <code>className</code> using the current thread's context class loader
806         * @throws ClassNotFoundException if the class is not found
807         */
808        public static Class<?> getClass(String className) throws ClassNotFoundException {
809            return getClass(className, true);
810        }
811    
812        /**
813         * Returns the class represented by <code>className</code> using the
814         * current thread's context class loader. This implementation supports the
815         * syntaxes "<code>java.util.Map.Entry[]</code>", "<code>java.util.Map$Entry[]</code>",
816         * "<code>[Ljava.util.Map.Entry;</code>", and "<code>[Ljava.util.Map$Entry;</code>".
817         *
818         * @param className  the class name
819         * @param initialize  whether the class must be initialized
820         * @return the class represented by <code>className</code> using the current thread's context class loader
821         * @throws ClassNotFoundException if the class is not found
822         */
823        public static Class<?> getClass(String className, boolean initialize) throws ClassNotFoundException {
824            ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
825            ClassLoader loader = contextCL == null ? ClassUtils.class.getClassLoader() : contextCL;
826            return getClass(loader, className, initialize );
827        }
828    
829        // Public method
830        // ----------------------------------------------------------------------
831        /**
832         * <p>Returns the desired Method much like <code>Class.getMethod</code>, however
833         * it ensures that the returned Method is from a public class or interface and not
834         * from an anonymous inner class. This means that the Method is invokable and
835         * doesn't fall foul of Java bug
836         * <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4071957">4071957</a>).
837         *
838         *  <code><pre>Set set = Collections.unmodifiableSet(...);
839         *  Method method = ClassUtils.getPublicMethod(set.getClass(), "isEmpty",  new Class[0]);
840         *  Object result = method.invoke(set, new Object[]);</pre></code>
841         * </p>
842         *
843         * @param cls  the class to check, not null
844         * @param methodName  the name of the method
845         * @param parameterTypes  the list of parameters
846         * @return the method
847         * @throws NullPointerException if the class is null
848         * @throws SecurityException if a a security violation occured
849         * @throws NoSuchMethodException if the method is not found in the given class
850         *  or if the metothod doen't conform with the requirements
851         */
852        public static Method getPublicMethod(Class<?> cls, String methodName, Class<?> parameterTypes[])
853                throws SecurityException, NoSuchMethodException {
854    
855            Method declaredMethod = cls.getMethod(methodName, parameterTypes);
856            if (Modifier.isPublic(declaredMethod.getDeclaringClass().getModifiers())) {
857                return declaredMethod;
858            }
859    
860            List<Class<?>> candidateClasses = new ArrayList<Class<?>>();
861            candidateClasses.addAll(getAllInterfaces(cls));
862            candidateClasses.addAll(getAllSuperclasses(cls));
863    
864            for (Class<?> candidateClass : candidateClasses) {
865                if (!Modifier.isPublic(candidateClass.getModifiers())) {
866                    continue;
867                }
868                Method candidateMethod;
869                try {
870                    candidateMethod = candidateClass.getMethod(methodName, parameterTypes);
871                } catch (NoSuchMethodException ex) {
872                    continue;
873                }
874                if (Modifier.isPublic(candidateMethod.getDeclaringClass().getModifiers())) {
875                    return candidateMethod;
876                }
877            }
878    
879            throw new NoSuchMethodException("Can't find a public method for " +
880                    methodName + " " + ArrayUtils.toString(parameterTypes));
881        }
882    
883        // ----------------------------------------------------------------------
884        /**
885         * Converts a class name to a JLS style class name.
886         *
887         * @param className  the class name
888         * @return the converted name
889         */
890        private static String toCanonicalName(String className) {
891            className = StringUtils.deleteWhitespace(className);
892            if (className == null) {
893                throw new NullPointerException("className must not be null.");
894            } else if (className.endsWith("[]")) {
895                StringBuilder classNameBuffer = new StringBuilder();
896                while (className.endsWith("[]")) {
897                    className = className.substring(0, className.length() - 2);
898                    classNameBuffer.append("[");
899                }
900                String abbreviation = abbreviationMap.get(className);
901                if (abbreviation != null) {
902                    classNameBuffer.append(abbreviation);
903                } else {
904                    classNameBuffer.append("L").append(className).append(";");
905                }
906                className = classNameBuffer.toString();
907            }
908            return className;
909        }
910    
911        /**
912         * <p>Converts an array of <code>Object</code> in to an array of <code>Class</code> objects.
913         * If any of these objects is null, a null element will be inserted into the array.</p>
914         *
915         * <p>This method returns <code>null</code> for a <code>null</code> input array.</p>
916         *
917         * @param array an <code>Object</code> array
918         * @return a <code>Class</code> array, <code>null</code> if null array input
919         * @since 2.4
920         */
921        public static Class<?>[] toClass(Object[] array) {
922            if (array == null) {
923                return null;
924            } else if (array.length == 0) {
925                return ArrayUtils.EMPTY_CLASS_ARRAY;
926            }
927            Class<?>[] classes = new Class[array.length];
928            for (int i = 0; i < array.length; i++) {
929                classes[i] = array[i] == null ? null : array[i].getClass();
930            }
931            return classes;
932        }
933    
934        // Short canonical name
935        // ----------------------------------------------------------------------
936        /**
937         * <p>Gets the canonical name minus the package name for an <code>Object</code>.</p>
938         *
939         * @param object  the class to get the short name for, may be null
940         * @param valueIfNull  the value to return if null
941         * @return the canonical name of the object without the package name, or the null value
942         * @since 2.4
943         */
944        public static String getShortCanonicalName(Object object, String valueIfNull) {
945            if (object == null) {
946                return valueIfNull;
947            }
948            return getShortCanonicalName(object.getClass().getName());
949        }
950    
951        /**
952         * <p>Gets the canonical name minus the package name from a <code>Class</code>.</p>
953         *
954         * @param cls  the class to get the short name for.
955         * @return the canonical name without the package name or an empty string
956         * @since 2.4
957         */
958        public static String getShortCanonicalName(Class<?> cls) {
959            if (cls == null) {
960                return StringUtils.EMPTY;
961            }
962            return getShortCanonicalName(cls.getName());
963        }
964    
965        /**
966         * <p>Gets the canonical name minus the package name from a String.</p>
967         *
968         * <p>The string passed in is assumed to be a canonical name - it is not checked.</p>
969         *
970         * @param canonicalName  the class name to get the short name for
971         * @return the canonical name of the class without the package name or an empty string
972         * @since 2.4
973         */
974        public static String getShortCanonicalName(String canonicalName) {
975            return ClassUtils.getShortClassName(getCanonicalName(canonicalName));
976        }
977    
978        // Package name
979        // ----------------------------------------------------------------------
980        /**
981         * <p>Gets the package name from the canonical name of an <code>Object</code>.</p>
982         *
983         * @param object  the class to get the package name for, may be null
984         * @param valueIfNull  the value to return if null
985         * @return the package name of the object, or the null value
986         * @since 2.4
987         */
988        public static String getPackageCanonicalName(Object object, String valueIfNull) {
989            if (object == null) {
990                return valueIfNull;
991            }
992            return getPackageCanonicalName(object.getClass().getName());
993        }
994    
995        /**
996         * <p>Gets the package name from the canonical name of a <code>Class</code>.</p>
997         *
998         * @param cls  the class to get the package name for, may be <code>null</code>.
999         * @return the package name or an empty string
1000         * @since 2.4
1001         */
1002        public static String getPackageCanonicalName(Class<?> cls) {
1003            if (cls == null) {
1004                return StringUtils.EMPTY;
1005            }
1006            return getPackageCanonicalName(cls.getName());
1007        }
1008    
1009        /**
1010         * <p>Gets the package name from the canonical name. </p>
1011         *
1012         * <p>The string passed in is assumed to be a canonical name - it is not checked.</p>
1013         * <p>If the class is unpackaged, return an empty string.</p>
1014         *
1015         * @param canonicalName  the canonical name to get the package name for, may be <code>null</code>
1016         * @return the package name or an empty string
1017         * @since 2.4
1018         */
1019        public static String getPackageCanonicalName(String canonicalName) {
1020            return ClassUtils.getPackageName(getCanonicalName(canonicalName));
1021        }
1022    
1023        /**
1024         * <p>Converts a given name of class into canonical format.
1025         * If name of class is not a name of array class it returns
1026         * unchanged name.</p>
1027         * <p>Example:
1028         * <ul>
1029         * <li><code>getCanonicalName("[I") = "int[]"</code></li>
1030         * <li><code>getCanonicalName("[Ljava.lang.String;") = "java.lang.String[]"</code></li>
1031         * <li><code>getCanonicalName("java.lang.String") = "java.lang.String"</code></li>
1032         * </ul>
1033         * </p>
1034         *
1035         * @param className the name of class
1036         * @return canonical form of class name
1037         * @since 2.4
1038         */
1039        private static String getCanonicalName(String className) {
1040            className = StringUtils.deleteWhitespace(className);
1041            if (className == null) {
1042                return null;
1043            } else {
1044                int dim = 0;
1045                while (className.startsWith("[")) {
1046                    dim++;
1047                    className = className.substring(1);
1048                }
1049                if (dim < 1) {
1050                    return className;
1051                } else {
1052                    if (className.startsWith("L")) {
1053                        className = className.substring(
1054                            1,
1055                            className.endsWith(";")
1056                                ? className.length() - 1
1057                                : className.length());
1058                    } else {
1059                        if (className.length() > 0) {
1060                            className = reverseAbbreviationMap.get(className.substring(0, 1));
1061                        }
1062                    }
1063                    StringBuilder canonicalClassNameBuffer = new StringBuilder(className);
1064                    for (int i = 0; i < dim; i++) {
1065                        canonicalClassNameBuffer.append("[]");
1066                    }
1067                    return canonicalClassNameBuffer.toString();
1068                }
1069            }
1070        }
1071    }