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>'.' == .</code>.
048 * </p>
049 */
050 public static final char PACKAGE_SEPARATOR_CHAR = '.';
051
052 /**
053 * <p>
054 * The package separator String: <code>"."</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 }