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 }