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