001    /*
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  You may obtain a copy of the License at
009     *  
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *  
012     *  Unless required by applicable law or agreed to in writing,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License. 
018     *  
019     */
020    package org.apache.directory.shared.ldap.util;
021    
022    
023    import java.util.ArrayList;
024    import java.util.Comparator;
025    import java.util.HashMap;
026    import java.util.List;
027    import java.util.Map;
028    
029    
030    /**
031     * <p>
032     * Operates on classes without using reflection.
033     * </p>
034     * <p>
035     * This class handles invalid <code>null</code> inputs as best it can. Each
036     * method documents its behaviour in more detail.
037     * </p>
038     * 
039     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
040     */
041    public class ClassUtils
042    {
043        private static final String EMPTY = "";
044    
045        /**
046         * <p>
047         * The package separator character: <code>'&#x2e;' == .</code>.
048         * </p>
049         */
050        public static final char PACKAGE_SEPARATOR_CHAR = '.';
051    
052        /**
053         * <p>
054         * The package separator String: <code>"&#x2e;"</code>.
055         * </p>
056         */
057        public static final String PACKAGE_SEPARATOR = String.valueOf( PACKAGE_SEPARATOR_CHAR );
058    
059        /**
060         * <p>
061         * The inner class separator character: <code>'$' == $</code>.
062         * </p>
063         */
064        public static final char INNER_CLASS_SEPARATOR_CHAR = '$';
065    
066        /**
067         * <p>
068         * The inner class separator String: <code>"$"</code>.
069         * </p>
070         */
071        public static final String INNER_CLASS_SEPARATOR = String.valueOf( INNER_CLASS_SEPARATOR_CHAR );
072    
073        /**
074         * Maps primitive <code>Class</code>es to their corresponding wrapper
075         * <code>Class</code>.
076         */
077        private static Map<Class<?>,Class<?>> primitiveWrapperMap = new HashMap<Class<?>,Class<?>>();
078        
079        static
080        {
081            primitiveWrapperMap.put( Boolean.TYPE, Boolean.class );
082            primitiveWrapperMap.put( Byte.TYPE, Byte.class );
083            primitiveWrapperMap.put( Character.TYPE, Character.class );
084            primitiveWrapperMap.put( Short.TYPE, Short.class );
085            primitiveWrapperMap.put( Integer.TYPE, Integer.class );
086            primitiveWrapperMap.put( Long.TYPE, Long.class );
087            primitiveWrapperMap.put( Double.TYPE, Double.class );
088            primitiveWrapperMap.put( Float.TYPE, Float.class );
089        }
090    
091    
092        /**
093         * <p>
094         * ClassUtils instances should NOT be constructed in standard programming.
095         * Instead, the class should be used as
096         * <code>ClassUtils.getShortClassName(cls)</code>.
097         * </p>
098         * <p>
099         * This constructor is public to permit tools that require a JavaBean
100         * instance to operate.
101         * </p>
102         */
103        public ClassUtils()
104        {
105        }
106    
107    
108        // Short class name
109        // ----------------------------------------------------------------------
110        /**
111         * <p>
112         * Gets the class name minus the package name for an <code>Object</code>.
113         * </p>
114         * 
115         * @param object
116         *            the class to get the short name for, may be null
117         * @param valueIfNull
118         *            the value to return if null
119         * @return the class name of the object without the package name, or the
120         *         null value
121         */
122        public static String getShortClassName( Object object, String valueIfNull )
123        {
124            if ( object == null )
125            {
126                return valueIfNull;
127            }
128            return getShortClassName( object.getClass().getName() );
129        }
130    
131    
132        /**
133         * <p>
134         * Gets the class name minus the package name from a <code>Class</code>.
135         * </p>
136         * 
137         * @param cls
138         *            the class to get the short name for.
139         * @return the class name without the package name or an empty string
140         */
141        public static String getShortClassName( Class<?> cls )
142        {
143            if ( cls == null )
144            {
145                return EMPTY;
146            }
147            
148            return getShortClassName( cls.getName() );
149        }
150    
151    
152        /**
153         * <p>
154         * Gets the class name minus the package name from a String.
155         * </p>
156         * <p>
157         * The string passed in is assumed to be a class name - it is not checked.
158         * </p>
159         * 
160         * @param className
161         *            the className to get the short name for
162         * @return the class name of the class without the package name or an empty
163         *         string
164         */
165        public static String getShortClassName( String className )
166        {
167            if ( className == null )
168            {
169                return EMPTY;
170            }
171            
172            if ( className.length() == 0 )
173            {
174                return EMPTY;
175            }
176            
177            char[] chars = className.toCharArray();
178            int lastDot = 0;
179            
180            for ( int i = 0; i < chars.length; i++ )
181            {
182                if ( chars[i] == PACKAGE_SEPARATOR_CHAR )
183                {
184                    lastDot = i + 1;
185                }
186                else if ( chars[i] == INNER_CLASS_SEPARATOR_CHAR )
187                { // handle inner classes
188                    chars[i] = PACKAGE_SEPARATOR_CHAR;
189                }
190            }
191            
192            return new String( chars, lastDot, chars.length - lastDot );
193        }
194    
195    
196        // Package name
197        // ----------------------------------------------------------------------
198        /**
199         * <p>
200         * Gets the package name of an <code>Object</code>.
201         * </p>
202         * 
203         * @param object
204         *            the class to get the package name for, may be null
205         * @param valueIfNull
206         *            the value to return if null
207         * @return the package name of the object, or the null value
208         */
209        public static String getPackageName( Object object, String valueIfNull )
210        {
211            if ( object == null )
212            {
213                return valueIfNull;
214            }
215            
216            return getPackageName( object.getClass().getName() );
217        }
218    
219    
220        /**
221         * <p>
222         * Gets the package name of a <code>Class</code>.
223         * </p>
224         * 
225         * @param cls
226         *            the class to get the package name for, may be
227         *            <code>null</code>.
228         * @return the package name or an empty string
229         */
230        public static String getPackageName( Class<?> cls )
231        {
232            if ( cls == null )
233            {
234                return EMPTY;
235            }
236            
237            return getPackageName( cls.getName() );
238        }
239    
240    
241        /**
242         * <p>
243         * Gets the package name from a <code>String</code>.
244         * </p>
245         * <p>
246         * The string passed in is assumed to be a class name - it is not checked.
247         * </p>
248         * <p>
249         * If the class is unpackaged, return an empty string.
250         * </p>
251         * 
252         * @param className
253         *            the className to get the package name for, may be
254         *            <code>null</code>
255         * @return the package name or an empty string
256         */
257        public static String getPackageName( String className )
258        {
259            if ( className == null )
260            {
261                return EMPTY;
262            }
263            
264            int i = className.lastIndexOf( PACKAGE_SEPARATOR_CHAR );
265            
266            if ( i == -1 )
267            {
268                return EMPTY;
269            }
270            
271            return className.substring( 0, i );
272        }
273    
274    
275        // Superclasses/Superinterfaces
276        // ----------------------------------------------------------------------
277        /**
278         * <p>
279         * Gets a <code>List</code> of superclasses for the given class.
280         * </p>
281         * 
282         * @param cls
283         *            the class to look up, may be <code>null</code>
284         * @return the <code>List</code> of superclasses in order going up from
285         *         this one <code>null</code> if null input
286         */
287        public static List<Class<?>> getAllSuperclasses( Class<?> cls )
288        {
289            if ( cls == null )
290            {
291                return null;
292            }
293            
294            List<Class<?>> classes = new ArrayList<Class<?>>();
295            
296            Class<?> superclass = cls.getSuperclass();
297            
298            while ( superclass != null )
299            {
300                classes.add( superclass );
301                superclass = superclass.getSuperclass();
302            }
303            
304            return classes;
305        }
306    
307    
308        /**
309         * <p>
310         * Gets a <code>List</code> of all interfaces implemented by the given
311         * class and its superclasses.
312         * </p>
313         * <p>
314         * The order is determined by looking through each interface in turn as
315         * declared in the source file and following its hierarchy up. Then each
316         * superclass is considered in the same way. Later duplicates are ignored,
317         * so the order is maintained.
318         * </p>
319         * 
320         * @param cls
321         *            the class to look up, may be <code>null</code>
322         * @return the <code>List</code> of interfaces in order, <code>null</code>
323         *         if null input
324         */
325        public static List<Class<?>> getAllInterfaces( Class<?> cls )
326        {
327            if ( cls == null )
328            {
329                return null;
330            }
331            
332            List<Class<?>> list = new ArrayList<Class<?>>();
333            
334            while ( cls != null )
335            {
336                Class<?>[] interfaces = cls.getInterfaces();
337                
338                for ( Class<?> interf:interfaces )
339                {
340                    if ( list.contains( interf ) == false )
341                    {
342                        list.add( interf );
343                    }
344                    
345                    for ( Class<?> superIntf:getAllInterfaces( interf ) )
346                    {
347                        if ( list.contains( superIntf ) == false )
348                        {
349                            list.add( superIntf );
350                        }
351                    }
352                }
353                
354                cls = cls.getSuperclass();
355            }
356            
357            return list;
358        }
359    
360    
361        // Convert list
362        // ----------------------------------------------------------------------
363        /**
364         * <p>
365         * Given a <code>List</code> of class names, this method converts them
366         * into classes.
367         * </p>
368         * <p>
369         * A new <code>List</code> is returned. If the class name cannot be found,
370         * <code>null</code> is stored in the <code>List</code>. If the class
371         * name in the <code>List</code> is <code>null</code>,
372         * <code>null</code> is stored in the output <code>List</code>.
373         * </p>
374         * 
375         * @param classNames
376         *            the classNames to change
377         * @return a <code>List</code> of Class objects corresponding to the class
378         *         names, <code>null</code> if null input
379         * @throws ClassCastException
380         *             if classNames contains a non String entry
381         */
382        public static List<Class<?>> convertClassNamesToClasses( List<String> classNames )
383        {
384            if ( classNames == null )
385            {
386                return null;
387            }
388            
389            List<Class<?>> classes = new ArrayList<Class<?>>( classNames.size() );
390            
391            for ( String className:classNames )
392            {
393                try
394                {
395                    classes.add( Class.forName( className ) );
396                }
397                catch ( Exception ex )
398                {
399                    classes.add( null );
400                }
401            }
402            
403            return classes;
404        }
405    
406    
407        /**
408         * <p>
409         * Given a <code>List</code> of <code>Class</code> objects, this method
410         * converts them into class names.
411         * </p>
412         * <p>
413         * A new <code>List</code> is returned. <code>null</code> objects will
414         * be copied into the returned list as <code>null</code>.
415         * </p>
416         * 
417         * @param classes
418         *            the classes to change
419         * @return a <code>List</code> of class names corresponding to the Class
420         *         objects, <code>null</code> if null input
421         * @throws ClassCastException
422         *             if <code>classes</code> contains a non-<code>Class</code>
423         *             entry
424         */
425        public static List<String> convertClassesToClassNames( List<Class<?>> classes )
426        {
427            if ( classes == null )
428            {
429                return null;
430            }
431            
432            List<String> classNames = new ArrayList<String>( classes.size() );
433            
434            for ( Class<?> clazz:classes )
435            {
436                if ( clazz == null )
437                {
438                    classNames.add( null );
439                }
440                else
441                {
442                    classNames.add( clazz.getName() );
443                }
444            }
445            
446            return classNames;
447        }
448    
449    
450        // Is assignable
451        // ----------------------------------------------------------------------
452        /**
453         * <p>
454         * Checks if an array of Classes can be assigned to another array of
455         * Classes.
456         * </p>
457         * <p>
458         * This method calls {@link #isAssignable(Class, Class) isAssignable} for
459         * each Class pair in the input arrays. It can be used to check if a set of
460         * arguments (the first parameter) are suitably compatible with a set of
461         * method parameter types (the second parameter).
462         * </p>
463         * <p>
464         * Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this
465         * method takes into account widenings of primitive classes and
466         * <code>null</code>s.
467         * </p>
468         * <p>
469         * Primitive widenings allow an int to be assigned to a <code>long</code>,
470         * <code>float</code> or <code>double</code>. This method returns the
471         * correct result for these cases.
472         * </p>
473         * <p>
474         * <code>Null</code> may be assigned to any reference type. This method
475         * will return <code>true</code> if <code>null</code> is passed in and
476         * the toClass is non-primitive.
477         * </p>
478         * <p>
479         * Specifically, this method tests whether the type represented by the
480         * specified <code>Class</code> parameter can be converted to the type
481         * represented by this <code>Class</code> object via an identity
482         * conversion widening primitive or widening reference conversion. See
483         * <em><a href="http://java.sun.com/docs/books/jls/">The Java Language Specification</a></em>,
484         * sections 5.1.1, 5.1.2 and 5.1.4 for details.
485         * </p>
486         * 
487         * @param classArray
488         *            the array of Classes to check, may be <code>null</code>
489         * @param toClassArray
490         *            the array of Classes to try to assign into, may be
491         *            <code>null</code>
492         * @return <code>true</code> if assignment possible
493         */
494        public static boolean isAssignable( Class<?>[] classArray, Class<?>[] toClassArray )
495        {
496            if ( ArrayUtils.isSameLength( classArray, toClassArray ) == false )
497            {
498                return false;
499            }
500            
501            if ( classArray == null )
502            {
503                classArray = ArrayUtils.EMPTY_CLASS_ARRAY;
504            }
505            
506            if ( toClassArray == null )
507            {
508                toClassArray = ArrayUtils.EMPTY_CLASS_ARRAY;
509            }
510            
511            for ( int i = 0; i < classArray.length; i++ )
512            {
513                if ( isAssignable( classArray[i], toClassArray[i] ) == false )
514                {
515                    return false;
516                }
517            }
518            
519            return true;
520        }
521    
522    
523        /**
524         * <p>
525         * Checks if one <code>Class</code> can be assigned to a variable of
526         * another <code>Class</code>.
527         * </p>
528         * <p>
529         * Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this
530         * method takes into account widenings of primitive classes and
531         * <code>null</code>s.
532         * </p>
533         * <p>
534         * Primitive widenings allow an int to be assigned to a long, float or
535         * double. This method returns the correct result for these cases.
536         * </p>
537         * <p>
538         * <code>Null</code> may be assigned to any reference type. This method
539         * will return <code>true</code> if <code>null</code> is passed in and
540         * the toClass is non-primitive.
541         * </p>
542         * <p>
543         * Specifically, this method tests whether the type represented by the
544         * specified <code>Class</code> parameter can be converted to the type
545         * represented by this <code>Class</code> object via an identity
546         * conversion widening primitive or widening reference conversion. See
547         * <em><a href="http://java.sun.com/docs/books/jls/">The Java Language Specification</a></em>,
548         * sections 5.1.1, 5.1.2 and 5.1.4 for details.
549         * </p>
550         * 
551         * @param cls
552         *            the Class to check, may be null
553         * @param toClass
554         *            the Class to try to assign into, returns false if null
555         * @return <code>true</code> if assignment possible
556         */
557        public static boolean isAssignable( Class<?> cls, Class<?> toClass )
558        {
559            if ( toClass == null )
560            {
561                return false;
562            }
563            
564            // have to check for null, as isAssignableFrom doesn't
565            if ( cls == null )
566            {
567                return !( toClass.isPrimitive() );
568            }
569            
570            if ( cls.equals( toClass ) )
571            {
572                return true;
573            }
574            
575            if ( cls.isPrimitive() )
576            {
577                if ( toClass.isPrimitive() == false )
578                {
579                    return false;
580                }
581                
582                if ( Integer.TYPE.equals( cls ) )
583                {
584                    return Long.TYPE.equals( toClass ) || Float.TYPE.equals( toClass ) || Double.TYPE.equals( toClass );
585                }
586                
587                if ( Long.TYPE.equals( cls ) )
588                {
589                    return Float.TYPE.equals( toClass ) || Double.TYPE.equals( toClass );
590                }
591                
592                if ( Boolean.TYPE.equals( cls ) )
593                {
594                    return false;
595                }
596                
597                if ( Double.TYPE.equals( cls ) )
598                {
599                    return false;
600                }
601                
602                if ( Float.TYPE.equals( cls ) )
603                {
604                    return Double.TYPE.equals( toClass );
605                }
606                
607                if ( Character.TYPE.equals( cls ) )
608                {
609                    return Integer.TYPE.equals( toClass ) || Long.TYPE.equals( toClass ) || Float.TYPE.equals( toClass )
610                        || Double.TYPE.equals( toClass );
611                }
612                
613                if ( Short.TYPE.equals( cls ) )
614                {
615                    return Integer.TYPE.equals( toClass ) || Long.TYPE.equals( toClass ) || Float.TYPE.equals( toClass )
616                        || Double.TYPE.equals( toClass );
617                }
618                
619                if ( Byte.TYPE.equals( cls ) )
620                {
621                    return Short.TYPE.equals( toClass ) || Integer.TYPE.equals( toClass ) || Long.TYPE.equals( toClass )
622                        || Float.TYPE.equals( toClass ) || Double.TYPE.equals( toClass );
623                }
624                
625                // should never get here
626                return false;
627            }
628            
629            return toClass.isAssignableFrom( cls );
630        }
631    
632    
633        /**
634         * <p>
635         * Converts the specified primitive Class object to its corresponding
636         * wrapper Class object.
637         * </p>
638         * 
639         * @param cls
640         *            the class to convert, may be null
641         * @return the wrapper class for <code>cls</code> or <code>cls</code> if
642         *         <code>cls</code> is not a primitive. <code>null</code> if
643         *         null input.
644         */
645        public static Class<?> primitiveToWrapper( Class<?> cls )
646        {
647            Class<?> convertedClass = cls;
648            
649            if ( cls != null && cls.isPrimitive() )
650            {
651                convertedClass = primitiveWrapperMap.get( cls );
652            }
653            
654            return convertedClass;
655        }
656    
657    
658        /**
659         * <p>
660         * Converts the specified array of primitive Class objects to an array of
661         * its corresponding wrapper Class objects.
662         * </p>
663         * 
664         * @param classes
665         *            the class array to convert, may be null or empty
666         * @return an array which contains for each given class, the wrapper class
667         *         or the original class if class is not a primitive.
668         *         <code>null</code> if null input. Empty array if an empty array
669         *         passed in.
670         */
671        public static Class<?>[] primitivesToWrappers( Class<?>[] classes )
672        {
673            if ( classes == null )
674            {
675                return null;
676            }
677    
678            if ( classes.length == 0 )
679            {
680                return ArrayUtils.EMPTY_CLASS_ARRAY;
681            }
682    
683            Class<?>[] convertedClasses = new Class[classes.length];
684            
685            for ( int i = 0; i < classes.length; i++ )
686            {
687                convertedClasses[i] = primitiveToWrapper( classes[i] );
688            }
689            
690            return convertedClasses;
691        }
692    
693    
694        // Inner class
695        // ----------------------------------------------------------------------
696        /**
697         * <p>
698         * Is the specified class an inner class or static nested class.
699         * </p>
700         * 
701         * @param cls
702         *            the class to check, may be null
703         * @return <code>true</code> if the class is an inner or static nested
704         *         class, false if not or <code>null</code>
705         */
706        public static boolean isInnerClass( Class<?> cls )
707        {
708            if ( cls == null )
709            {
710                return false;
711            }
712            
713            return ( cls.getName().indexOf( INNER_CLASS_SEPARATOR_CHAR ) >= 0 );
714        }
715    
716        // -----------------------------------------------------------------------
717        /**
718         * Compares two <code>Class</code>s by name.
719         */
720        private static class ClassNameComparator implements Comparator<Class<?>>
721        {
722            /**
723             * Compares two <code>Class</code>s by name.
724             * 
725             * @throws ClassCastException
726             *             If <code>o1</code> or <code>o2</code> are not
727             *             <code>Class</code> instances.
728             */
729            public int compare( Class<?> class1, Class<?> class2 )
730            {
731                if ( class1 == null )
732                {
733                    return class2 == null ? 0 : -1;
734                }
735                
736                if ( class2 == null )
737                {
738                    return 1;
739                }
740                
741                return class1.getName().compareTo( class2.getName() );
742            }
743        }
744    
745        /**
746         * Compares two <code>Class</code>s by name.
747         */
748        public static final Comparator<Class<?>> CLASS_NAME_COMPARATOR = new ClassNameComparator();
749    
750        /**
751         * Compares two <code>Package</code>s by name.
752         */
753        private static class PackageNameComparator implements Comparator<Package>
754        {
755    
756            /**
757             * Compares two <code>Package</code>s by name.
758             * 
759             * @throws ClassCastException
760             *             If <code>o1</code> or <code>o2</code> are not
761             *             <code>Package</code> instances.
762             */
763            public int compare( Package package1, Package package2 )
764            {
765                if ( package1 == null )
766                {
767                    return package2 == null ? 0 : -1;
768                }
769    
770                if ( package2 == null )
771                {
772                    return 1;
773                }
774                
775                return package1.getName().compareTo( package2.getName() );
776            }
777        }
778    
779        /**
780         * Compares two <code>Package</code>s by name.
781         */
782        public static final Comparator<Package> PACKAGE_NAME_COMPARATOR = new PackageNameComparator();
783    
784    }