    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    }