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