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