/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang3;
import java.lang.reflect.
Method;
import java.lang.reflect.
Modifier;
import java.util.
ArrayList;
import java.util.
Collections;
import java.util.
HashMap;
import java.util.
HashSet;
import java.util.
Iterator;
import java.util.
LinkedHashSet;
import java.util.
List;
import java.util.
Map;
import java.util.
Set;
import org.apache.commons.lang3.mutable.
MutableObject;
/**
* <p>Operates on classes without using reflection.</p>
*
* <p>This class handles invalid {@code null} inputs as best it can.
* Each method documents its behaviour in more detail.</p>
*
* <p>The notion of a {@code canonical name} includes the human
* readable name for the type, for example {@code int[]}. The
* non-canonical method variants work with the JVM names, such as
* {@code [I}. </p>
*
* @since 2.0
*/
public class
ClassUtils {
/**
* Inclusivity literals for {@link #hierarchy(Class, Interfaces)}.
* @since 3.2
*/
public enum
Interfaces {
INCLUDE, EXCLUDE
}
/**
* The package separator character: <code>'.' == {@value}</code>.
*/
public static final char
PACKAGE_SEPARATOR_CHAR = '.';
/**
* The package separator String: <code>"."</code>.
*/
public static final
String PACKAGE_SEPARATOR =
String.
valueOf(
PACKAGE_SEPARATOR_CHAR);
/**
* The inner class separator character: <code>'$' == {@value}</code>.
*/
public static final char
INNER_CLASS_SEPARATOR_CHAR = '$';
/**
* The inner class separator String: {@code "$"}.
*/
public static final
String INNER_CLASS_SEPARATOR =
String.
valueOf(
INNER_CLASS_SEPARATOR_CHAR);
/**
* Maps names of primitives to their corresponding primitive {@code Class}es.
*/
private static final
Map<
String,
Class<?>>
namePrimitiveMap = new
HashMap<
String,
Class<?>>();
static {
namePrimitiveMap.
put("boolean",
Boolean.
TYPE);
namePrimitiveMap.
put("byte",
Byte.
TYPE);
namePrimitiveMap.
put("char",
Character.
TYPE);
namePrimitiveMap.
put("short",
Short.
TYPE);
namePrimitiveMap.
put("int",
Integer.
TYPE);
namePrimitiveMap.
put("long",
Long.
TYPE);
namePrimitiveMap.
put("double",
Double.
TYPE);
namePrimitiveMap.
put("float",
Float.
TYPE);
namePrimitiveMap.
put("void",
Void.
TYPE);
}
/**
* Maps primitive {@code Class}es to their corresponding wrapper {@code Class}.
*/
private static final
Map<
Class<?>,
Class<?>>
primitiveWrapperMap = new
HashMap<
Class<?>,
Class<?>>();
static {
primitiveWrapperMap.
put(
Boolean.
TYPE,
Boolean.class);
primitiveWrapperMap.
put(
Byte.
TYPE,
Byte.class);
primitiveWrapperMap.
put(
Character.
TYPE,
Character.class);
primitiveWrapperMap.
put(
Short.
TYPE,
Short.class);
primitiveWrapperMap.
put(
Integer.
TYPE,
Integer.class);
primitiveWrapperMap.
put(
Long.
TYPE,
Long.class);
primitiveWrapperMap.
put(
Double.
TYPE,
Double.class);
primitiveWrapperMap.
put(
Float.
TYPE,
Float.class);
primitiveWrapperMap.
put(
Void.
TYPE,
Void.
TYPE);
}
/**
* Maps wrapper {@code Class}es to their corresponding primitive types.
*/
private static final
Map<
Class<?>,
Class<?>>
wrapperPrimitiveMap = new
HashMap<
Class<?>,
Class<?>>();
static {
for (final
Map.
Entry<
Class<?>,
Class<?>>
entry :
primitiveWrapperMap.
entrySet()) {
final
Class<?>
primitiveClass =
entry.
getKey();
final
Class<?>
wrapperClass =
entry.
getValue();
if (!
primitiveClass.
equals(
wrapperClass)) {
wrapperPrimitiveMap.
put(
wrapperClass,
primitiveClass);
}
}
}
/**
* Maps a primitive class name to its corresponding abbreviation used in array class names.
*/
private static final
Map<
String,
String>
abbreviationMap;
/**
* Maps an abbreviation used in array class names to corresponding primitive class name.
*/
private static final
Map<
String,
String>
reverseAbbreviationMap;
/**
* Feed abbreviation maps
*/
static {
final
Map<
String,
String>
m = new
HashMap<
String,
String>();
m.
put("int", "I");
m.
put("boolean", "Z");
m.
put("float", "F");
m.
put("long", "J");
m.
put("short", "S");
m.
put("byte", "B");
m.
put("double", "D");
m.
put("char", "C");
final
Map<
String,
String>
r = new
HashMap<
String,
String>();
for (final
Map.
Entry<
String,
String>
e :
m.
entrySet()) {
r.
put(
e.
getValue(),
e.
getKey());
}
abbreviationMap =
Collections.
unmodifiableMap(
m);
reverseAbbreviationMap =
Collections.
unmodifiableMap(
r);
}
/**
* <p>ClassUtils instances should NOT be constructed in standard programming.
* Instead, the class should be used as
* {@code ClassUtils.getShortClassName(cls)}.</p>
*
* <p>This constructor is public to permit tools that require a JavaBean
* instance to operate.</p>
*/
public
ClassUtils() {
super();
}
// Short class name
// ----------------------------------------------------------------------
/**
* <p>Gets the class name minus the package name for an {@code Object}.</p>
*
* @param object the class to get the short name for, may be null
* @param valueIfNull the value to return if null
* @return the class name of the object without the package name, or the null value
*/
public static
String getShortClassName(final
Object object, final
String valueIfNull) {
if (
object == null) {
return
valueIfNull;
}
return
getShortClassName(
object.
getClass());
}
/**
* <p>Gets the class name minus the package name from a {@code Class}.</p>
*
* <p>Consider using the Java 5 API {@link Class#getSimpleName()} instead.
* The one known difference is that this code will return {@code "Map.Entry"} while
* the {@code java.lang.Class} variant will simply return {@code "Entry"}. </p>
*
* @param cls the class to get the short name for.
* @return the class name without the package name or an empty string
*/
public static
String getShortClassName(final
Class<?>
cls) {
if (
cls == null) {
return
StringUtils.
EMPTY;
}
return
getShortClassName(
cls.
getName());
}
/**
* <p>Gets the class name minus the package name from a String.</p>
*
* <p>The string passed in is assumed to be a class name - it is not checked.</p>
* <p>Note that this method differs from Class.getSimpleName() in that this will
* return {@code "Map.Entry"} whilst the {@code java.lang.Class} variant will simply
* return {@code "Entry"}. </p>
*
* @param className the className to get the short name for
* @return the class name of the class without the package name or an empty string
*/
public static
String getShortClassName(
String className) {
if (
StringUtils.
isEmpty(
className)) {
return
StringUtils.
EMPTY;
}
final
StringBuilder arrayPrefix = new
StringBuilder();
// Handle array encoding
if (
className.
startsWith("[")) {
while (
className.
charAt(0) == '[') {
className =
className.
substring(1);
arrayPrefix.
append("[]");
}
// Strip Object type encoding
if (
className.
charAt(0) == 'L' &&
className.
charAt(
className.
length() - 1) == ';') {
className =
className.
substring(1,
className.
length() - 1);
}
if (
reverseAbbreviationMap.
containsKey(
className)) {
className =
reverseAbbreviationMap.
get(
className);
}
}
final int
lastDotIdx =
className.
lastIndexOf(
PACKAGE_SEPARATOR_CHAR);
final int
innerIdx =
className.
indexOf(
INNER_CLASS_SEPARATOR_CHAR,
lastDotIdx == -1 ? 0 :
lastDotIdx + 1);
String out =
className.
substring(
lastDotIdx + 1);
if (
innerIdx != -1) {
out =
out.
replace(
INNER_CLASS_SEPARATOR_CHAR,
PACKAGE_SEPARATOR_CHAR);
}
return
out +
arrayPrefix;
}
/**
* <p>Null-safe version of <code>aClass.getSimpleName()</code></p>
*
* @param cls the class for which to get the simple name.
* @return the simple class name.
* @since 3.0
* @see Class#getSimpleName()
*/
public static
String getSimpleName(final
Class<?>
cls) {
if (
cls == null) {
return
StringUtils.
EMPTY;
}
return
cls.
getSimpleName();
}
/**
* <p>Null-safe version of <code>aClass.getSimpleName()</code></p>
*
* @param object the object for which to get the simple class name.
* @param valueIfNull the value to return if <code>object</code> is <code>null</code>
* @return the simple class name.
* @since 3.0
* @see Class#getSimpleName()
*/
public static
String getSimpleName(final
Object object, final
String valueIfNull) {
if (
object == null) {
return
valueIfNull;
}
return
getSimpleName(
object.
getClass());
}
// Package name
// ----------------------------------------------------------------------
/**
* <p>Gets the package name of an {@code Object}.</p>
*
* @param object the class to get the package name for, may be null
* @param valueIfNull the value to return if null
* @return the package name of the object, or the null value
*/
public static
String getPackageName(final
Object object, final
String valueIfNull) {
if (
object == null) {
return
valueIfNull;
}
return
getPackageName(
object.
getClass());
}
/**
* <p>Gets the package name of a {@code Class}.</p>
*
* @param cls the class to get the package name for, may be {@code null}.
* @return the package name or an empty string
*/
public static
String getPackageName(final
Class<?>
cls) {
if (
cls == null) {
return
StringUtils.
EMPTY;
}
return
getPackageName(
cls.
getName());
}
/**
* <p>Gets the package name from a {@code String}.</p>
*
* <p>The string passed in is assumed to be a class name - it is not checked.</p>
* <p>If the class is unpackaged, return an empty string.</p>
*
* @param className the className to get the package name for, may be {@code null}
* @return the package name or an empty string
*/
public static
String getPackageName(
String className) {
if (
StringUtils.
isEmpty(
className)) {
return
StringUtils.
EMPTY;
}
// Strip array encoding
while (
className.
charAt(0) == '[') {
className =
className.
substring(1);
}
// Strip Object type encoding
if (
className.
charAt(0) == 'L' &&
className.
charAt(
className.
length() - 1) == ';') {
className =
className.
substring(1);
}
final int
i =
className.
lastIndexOf(
PACKAGE_SEPARATOR_CHAR);
if (
i == -1) {
return
StringUtils.
EMPTY;
}
return
className.
substring(0,
i);
}
// Abbreviated name
// ----------------------------------------------------------------------
/**
* <p>Gets the abbreviated name of a {@code Class}.</p>
*
* @param cls the class to get the abbreviated name for, may be {@code null}
* @param len the desired length of the abbreviated name
* @return the abbreviated name or an empty string
* @throws IllegalArgumentException if len <= 0
* @see #getAbbreviatedName(String, int)
* @since 3.4
*/
public static
String getAbbreviatedName(final
Class<?>
cls, int
len) {
if (
cls == null) {
return
StringUtils.
EMPTY;
}
return
getAbbreviatedName(
cls.
getName(),
len);
}
/**
* <p>Gets the abbreviated class name from a {@code String}.</p>
*
* <p>The string passed in is assumed to be a class name - it is not checked.</p>
*
* <p>The abbreviation algorithm will shorten the class name, usually without
* significant loss of meaning.</p>
* <p>The abbreviated class name will always include the complete package hierarchy.
* If enough space is available, rightmost sub-packages will be displayed in full
* length.</p>
*
* <p>The following table illustrates the algorithm:</p>
* <table summary="abbreviation examples">
* <tr><td>className</td><td>len</td><td>return</td></tr>
* <tr><td> null</td><td> 1</td><td>""</td></tr>
* <tr><td>"java.lang.String"</td><td> 5</td><td>"j.l.String"</td></tr>
* <tr><td>"java.lang.String"</td><td>15</td><td>"j.lang.String"</td></tr>
* <tr><td>"java.lang.String"</td><td>30</td><td>"java.lang.String"</td></tr>
* </table>
* @param className the className to get the abbreviated name for, may be {@code null}
* @param len the desired length of the abbreviated name
* @return the abbreviated name or an empty string
* @throws IllegalArgumentException if len <= 0
* @since 3.4
*/
public static
String getAbbreviatedName(
String className, int
len) {
if (
len <= 0) {
throw new
IllegalArgumentException("len must be > 0");
}
if (
className == null) {
return
StringUtils.
EMPTY;
}
int
availableSpace =
len;
int
packageLevels =
StringUtils.
countMatches(
className, '.');
String[]
output = new
String[
packageLevels + 1];
int
endIndex =
className.
length() - 1;
for (int
level =
packageLevels;
level >= 0;
level--) {
int
startIndex =
className.
lastIndexOf('.',
endIndex);
String part =
className.
substring(
startIndex + 1,
endIndex + 1);
availableSpace -=
part.
length();
if (
level > 0) {
// all elements except top level require an additional char space
availableSpace--;
}
if (
level ==
packageLevels) {
// ClassName is always complete
output[
level] =
part;
} else {
if (
availableSpace > 0) {
output[
level] =
part;
} else {
// if no space is left still the first char is used
output[
level] =
part.
substring(0, 1);
}
}
endIndex =
startIndex - 1;
}
return
StringUtils.
join(
output, '.');
}
// Superclasses/Superinterfaces
// ----------------------------------------------------------------------
/**
* <p>Gets a {@code List} of superclasses for the given class.</p>
*
* @param cls the class to look up, may be {@code null}
* @return the {@code List} of superclasses in order going up from this one
* {@code null} if null input
*/
public static
List<
Class<?>>
getAllSuperclasses(final
Class<?>
cls) {
if (
cls == null) {
return null;
}
final
List<
Class<?>>
classes = new
ArrayList<
Class<?>>();
Class<?>
superclass =
cls.
getSuperclass();
while (
superclass != null) {
classes.
add(
superclass);
superclass =
superclass.
getSuperclass();
}
return
classes;
}
/**
* <p>Gets a {@code List} of all interfaces implemented by the given
* class and its superclasses.</p>
*
* <p>The order is determined by looking through each interface in turn as
* declared in the source file and following its hierarchy up. Then each
* superclass is considered in the same way. Later duplicates are ignored,
* so the order is maintained.</p>
*
* @param cls the class to look up, may be {@code null}
* @return the {@code List} of interfaces in order,
* {@code null} if null input
*/
public static
List<
Class<?>>
getAllInterfaces(final
Class<?>
cls) {
if (
cls == null) {
return null;
}
final
LinkedHashSet<
Class<?>>
interfacesFound = new
LinkedHashSet<
Class<?>>();
getAllInterfaces(
cls,
interfacesFound);
return new
ArrayList<
Class<?>>(
interfacesFound);
}
/**
* Get the interfaces for the specified class.
*
* @param cls the class to look up, may be {@code null}
* @param interfacesFound the {@code Set} of interfaces for the class
*/
private static void
getAllInterfaces(
Class<?>
cls, final
HashSet<
Class<?>>
interfacesFound) {
while (
cls != null) {
final
Class<?>[]
interfaces =
cls.
getInterfaces();
for (final
Class<?>
i :
interfaces) {
if (
interfacesFound.
add(
i)) {
getAllInterfaces(
i,
interfacesFound);
}
}
cls =
cls.
getSuperclass();
}
}
// Convert list
// ----------------------------------------------------------------------
/**
* <p>Given a {@code List} of class names, this method converts them into classes.</p>
*
* <p>A new {@code List} is returned. If the class name cannot be found, {@code null}
* is stored in the {@code List}. If the class name in the {@code List} is
* {@code null}, {@code null} is stored in the output {@code List}.</p>
*
* @param classNames the classNames to change
* @return a {@code List} of Class objects corresponding to the class names,
* {@code null} if null input
* @throws ClassCastException if classNames contains a non String entry
*/
public static
List<
Class<?>>
convertClassNamesToClasses(final
List<
String>
classNames) {
if (
classNames == null) {
return null;
}
final
List<
Class<?>>
classes = new
ArrayList<
Class<?>>(
classNames.
size());
for (final
String className :
classNames) {
try {
classes.
add(
Class.
forName(
className));
} catch (final
Exception ex) {
classes.
add(null);
}
}
return
classes;
}
/**
* <p>Given a {@code List} of {@code Class} objects, this method converts
* them into class names.</p>
*
* <p>A new {@code List} is returned. {@code null} objects will be copied into
* the returned list as {@code null}.</p>
*
* @param classes the classes to change
* @return a {@code List} of class names corresponding to the Class objects,
* {@code null} if null input
* @throws ClassCastException if {@code classes} contains a non-{@code Class} entry
*/
public static
List<
String>
convertClassesToClassNames(final
List<
Class<?>>
classes) {
if (
classes == null) {
return null;
}
final
List<
String>
classNames = new
ArrayList<
String>(
classes.
size());
for (final
Class<?>
cls :
classes) {
if (
cls == null) {
classNames.
add(null);
} else {
classNames.
add(
cls.
getName());
}
}
return
classNames;
}
// Is assignable
// ----------------------------------------------------------------------
/**
* <p>Checks if an array of Classes can be assigned to another array of Classes.</p>
*
* <p>This method calls {@link #isAssignable(Class, Class) isAssignable} for each
* Class pair in the input arrays. It can be used to check if a set of arguments
* (the first parameter) are suitably compatible with a set of method parameter types
* (the second parameter).</p>
*
* <p>Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this
* method takes into account widenings of primitive classes and
* {@code null}s.</p>
*
* <p>Primitive widenings allow an int to be assigned to a {@code long},
* {@code float} or {@code double}. This method returns the correct
* result for these cases.</p>
*
* <p>{@code Null} may be assigned to any reference type. This method will
* return {@code true} if {@code null} is passed in and the toClass is
* non-primitive.</p>
*
* <p>Specifically, this method tests whether the type represented by the
* specified {@code Class} parameter can be converted to the type
* represented by this {@code Class} object via an identity conversion
* widening primitive or widening reference conversion. See
* <em><a href="http://docs.oracle.com/javase/specs/">The Java Language Specification</a></em>,
* sections 5.1.1, 5.1.2 and 5.1.4 for details.</p>
*
* <p><strong>Since Lang 3.0,</strong> this method will default behavior for
* calculating assignability between primitive and wrapper types <em>corresponding
* to the running Java version</em>; i.e. autoboxing will be the default
* behavior in VMs running Java versions > 1.5.</p>
*
* @param classArray the array of Classes to check, may be {@code null}
* @param toClassArray the array of Classes to try to assign into, may be {@code null}
* @return {@code true} if assignment possible
*/
public static boolean
isAssignable(final
Class<?>[]
classArray, final
Class<?>...
toClassArray) {
return
isAssignable(
classArray,
toClassArray,
SystemUtils.
isJavaVersionAtLeast(
JavaVersion.
JAVA_1_5));
}
/**
* <p>Checks if an array of Classes can be assigned to another array of Classes.</p>
*
* <p>This method calls {@link #isAssignable(Class, Class) isAssignable} for each
* Class pair in the input arrays. It can be used to check if a set of arguments
* (the first parameter) are suitably compatible with a set of method parameter types
* (the second parameter).</p>
*
* <p>Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this
* method takes into account widenings of primitive classes and
* {@code null}s.</p>
*
* <p>Primitive widenings allow an int to be assigned to a {@code long},
* {@code float} or {@code double}. This method returns the correct
* result for these cases.</p>
*
* <p>{@code Null} may be assigned to any reference type. This method will
* return {@code true} if {@code null} is passed in and the toClass is
* non-primitive.</p>
*
* <p>Specifically, this method tests whether the type represented by the
* specified {@code Class} parameter can be converted to the type
* represented by this {@code Class} object via an identity conversion
* widening primitive or widening reference conversion. See
* <em><a href="http://docs.oracle.com/javase/specs/">The Java Language Specification</a></em>,
* sections 5.1.1, 5.1.2 and 5.1.4 for details.</p>
*
* @param classArray the array of Classes to check, may be {@code null}
* @param toClassArray the array of Classes to try to assign into, may be {@code null}
* @param autoboxing whether to use implicit autoboxing/unboxing between primitives and wrappers
* @return {@code true} if assignment possible
*/
public static boolean
isAssignable(
Class<?>[]
classArray,
Class<?>[]
toClassArray, final boolean
autoboxing) {
if (
ArrayUtils.
isSameLength(
classArray,
toClassArray) == false) {
return false;
}
if (
classArray == null) {
classArray =
ArrayUtils.
EMPTY_CLASS_ARRAY;
}
if (
toClassArray == null) {
toClassArray =
ArrayUtils.
EMPTY_CLASS_ARRAY;
}
for (int
i = 0;
i <
classArray.length;
i++) {
if (
isAssignable(
classArray[
i],
toClassArray[
i],
autoboxing) == false) {
return false;
}
}
return true;
}
/**
* Returns whether the given {@code type} is a primitive or primitive wrapper ({@link Boolean}, {@link Byte}, {@link Character},
* {@link Short}, {@link Integer}, {@link Long}, {@link Double}, {@link Float}).
*
* @param type
* The class to query or null.
* @return true if the given {@code type} is a primitive or primitive wrapper ({@link Boolean}, {@link Byte}, {@link Character},
* {@link Short}, {@link Integer}, {@link Long}, {@link Double}, {@link Float}).
* @since 3.1
*/
public static boolean
isPrimitiveOrWrapper(final
Class<?>
type) {
if (
type == null) {
return false;
}
return
type.
isPrimitive() ||
isPrimitiveWrapper(
type);
}
/**
* Returns whether the given {@code type} is a primitive wrapper ({@link Boolean}, {@link Byte}, {@link Character}, {@link Short},
* {@link Integer}, {@link Long}, {@link Double}, {@link Float}).
*
* @param type
* The class to query or null.
* @return true if the given {@code type} is a primitive wrapper ({@link Boolean}, {@link Byte}, {@link Character}, {@link Short},
* {@link Integer}, {@link Long}, {@link Double}, {@link Float}).
* @since 3.1
*/
public static boolean
isPrimitiveWrapper(final
Class<?>
type) {
return
wrapperPrimitiveMap.
containsKey(
type);
}
/**
* <p>Checks if one {@code Class} can be assigned to a variable of
* another {@code Class}.</p>
*
* <p>Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method,
* this method takes into account widenings of primitive classes and
* {@code null}s.</p>
*
* <p>Primitive widenings allow an int to be assigned to a long, float or
* double. This method returns the correct result for these cases.</p>
*
* <p>{@code Null} may be assigned to any reference type. This method
* will return {@code true} if {@code null} is passed in and the
* toClass is non-primitive.</p>
*
* <p>Specifically, this method tests whether the type represented by the
* specified {@code Class} parameter can be converted to the type
* represented by this {@code Class} object via an identity conversion
* widening primitive or widening reference conversion. See
* <em><a href="http://docs.oracle.com/javase/specs/">The Java Language Specification</a></em>,
* sections 5.1.1, 5.1.2 and 5.1.4 for details.</p>
*
* <p><strong>Since Lang 3.0,</strong> this method will default behavior for
* calculating assignability between primitive and wrapper types <em>corresponding
* to the running Java version</em>; i.e. autoboxing will be the default
* behavior in VMs running Java versions > 1.5.</p>
*
* @param cls the Class to check, may be null
* @param toClass the Class to try to assign into, returns false if null
* @return {@code true} if assignment possible
*/
public static boolean
isAssignable(final
Class<?>
cls, final
Class<?>
toClass) {
return
isAssignable(
cls,
toClass,
SystemUtils.
isJavaVersionAtLeast(
JavaVersion.
JAVA_1_5));
}
/**
* <p>Checks if one {@code Class} can be assigned to a variable of
* another {@code Class}.</p>
*
* <p>Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method,
* this method takes into account widenings of primitive classes and
* {@code null}s.</p>
*
* <p>Primitive widenings allow an int to be assigned to a long, float or
* double. This method returns the correct result for these cases.</p>
*
* <p>{@code Null} may be assigned to any reference type. This method
* will return {@code true} if {@code null} is passed in and the
* toClass is non-primitive.</p>
*
* <p>Specifically, this method tests whether the type represented by the
* specified {@code Class} parameter can be converted to the type
* represented by this {@code Class} object via an identity conversion
* widening primitive or widening reference conversion. See
* <em><a href="http://docs.oracle.com/javase/specs/">The Java Language Specification</a></em>,
* sections 5.1.1, 5.1.2 and 5.1.4 for details.</p>
*
* @param cls the Class to check, may be null
* @param toClass the Class to try to assign into, returns false if null
* @param autoboxing whether to use implicit autoboxing/unboxing between primitives and wrappers
* @return {@code true} if assignment possible
*/
public static boolean
isAssignable(
Class<?>
cls, final
Class<?>
toClass, final boolean
autoboxing) {
if (
toClass == null) {
return false;
}
// have to check for null, as isAssignableFrom doesn't
if (
cls == null) {
return !
toClass.
isPrimitive();
}
//autoboxing:
if (
autoboxing) {
if (
cls.
isPrimitive() && !
toClass.
isPrimitive()) {
cls =
primitiveToWrapper(
cls);
if (
cls == null) {
return false;
}
}
if (
toClass.
isPrimitive() && !
cls.
isPrimitive()) {
cls =
wrapperToPrimitive(
cls);
if (
cls == null) {
return false;
}
}
}
if (
cls.
equals(
toClass)) {
return true;
}
if (
cls.
isPrimitive()) {
if (
toClass.
isPrimitive() == false) {
return false;
}
if (
Integer.
TYPE.
equals(
cls)) {
return
Long.
TYPE.
equals(
toClass)
||
Float.
TYPE.
equals(
toClass)
||
Double.
TYPE.
equals(
toClass);
}
if (
Long.
TYPE.
equals(
cls)) {
return
Float.
TYPE.
equals(
toClass)
||
Double.
TYPE.
equals(
toClass);
}
if (
Boolean.
TYPE.
equals(
cls)) {
return false;
}
if (
Double.
TYPE.
equals(
cls)) {
return false;
}
if (
Float.
TYPE.
equals(
cls)) {
return
Double.
TYPE.
equals(
toClass);
}
if (
Character.
TYPE.
equals(
cls)) {
return
Integer.
TYPE.
equals(
toClass)
||
Long.
TYPE.
equals(
toClass)
||
Float.
TYPE.
equals(
toClass)
||
Double.
TYPE.
equals(
toClass);
}
if (
Short.
TYPE.
equals(
cls)) {
return
Integer.
TYPE.
equals(
toClass)
||
Long.
TYPE.
equals(
toClass)
||
Float.
TYPE.
equals(
toClass)
||
Double.
TYPE.
equals(
toClass);
}
if (
Byte.
TYPE.
equals(
cls)) {
return
Short.
TYPE.
equals(
toClass)
||
Integer.
TYPE.
equals(
toClass)
||
Long.
TYPE.
equals(
toClass)
||
Float.
TYPE.
equals(
toClass)
||
Double.
TYPE.
equals(
toClass);
}
// should never get here
return false;
}
return
toClass.
isAssignableFrom(
cls);
}
/**
* <p>Converts the specified primitive Class object to its corresponding
* wrapper Class object.</p>
*
* <p>NOTE: From v2.2, this method handles {@code Void.TYPE},
* returning {@code Void.TYPE}.</p>
*
* @param cls the class to convert, may be null
* @return the wrapper class for {@code cls} or {@code cls} if
* {@code cls} is not a primitive. {@code null} if null input.
* @since 2.1
*/
public static
Class<?>
primitiveToWrapper(final
Class<?>
cls) {
Class<?>
convertedClass =
cls;
if (
cls != null &&
cls.
isPrimitive()) {
convertedClass =
primitiveWrapperMap.
get(
cls);
}
return
convertedClass;
}
/**
* <p>Converts the specified array of primitive Class objects to an array of
* its corresponding wrapper Class objects.</p>
*
* @param classes the class array to convert, may be null or empty
* @return an array which contains for each given class, the wrapper class or
* the original class if class is not a primitive. {@code null} if null input.
* Empty array if an empty array passed in.
* @since 2.1
*/
public static
Class<?>[]
primitivesToWrappers(final
Class<?>...
classes) {
if (
classes == null) {
return null;
}
if (
classes.length == 0) {
return
classes;
}
final
Class<?>[]
convertedClasses = new
Class[
classes.length];
for (int
i = 0;
i <
classes.length;
i++) {
convertedClasses[
i] =
primitiveToWrapper(
classes[
i]);
}
return
convertedClasses;
}
/**
* <p>Converts the specified wrapper class to its corresponding primitive
* class.</p>
*
* <p>This method is the counter part of {@code primitiveToWrapper()}.
* If the passed in class is a wrapper class for a primitive type, this
* primitive type will be returned (e.g. {@code Integer.TYPE} for
* {@code Integer.class}). For other classes, or if the parameter is
* <b>null</b>, the return value is <b>null</b>.</p>
*
* @param cls the class to convert, may be <b>null</b>
* @return the corresponding primitive type if {@code cls} is a
* wrapper class, <b>null</b> otherwise
* @see #primitiveToWrapper(Class)
* @since 2.4
*/
public static
Class<?>
wrapperToPrimitive(final
Class<?>
cls) {
return
wrapperPrimitiveMap.
get(
cls);
}
/**
* <p>Converts the specified array of wrapper Class objects to an array of
* its corresponding primitive Class objects.</p>
*
* <p>This method invokes {@code wrapperToPrimitive()} for each element
* of the passed in array.</p>
*
* @param classes the class array to convert, may be null or empty
* @return an array which contains for each given class, the primitive class or
* <b>null</b> if the original class is not a wrapper class. {@code null} if null input.
* Empty array if an empty array passed in.
* @see #wrapperToPrimitive(Class)
* @since 2.4
*/
public static
Class<?>[]
wrappersToPrimitives(final
Class<?>...
classes) {
if (
classes == null) {
return null;
}
if (
classes.length == 0) {
return
classes;
}
final
Class<?>[]
convertedClasses = new
Class[
classes.length];
for (int
i = 0;
i <
classes.length;
i++) {
convertedClasses[
i] =
wrapperToPrimitive(
classes[
i]);
}
return
convertedClasses;
}
// Inner class
// ----------------------------------------------------------------------
/**
* <p>Is the specified class an inner class or static nested class.</p>
*
* @param cls the class to check, may be null
* @return {@code true} if the class is an inner or static nested class,
* false if not or {@code null}
*/
public static boolean
isInnerClass(final
Class<?>
cls) {
return
cls != null &&
cls.
getEnclosingClass() != null;
}
// Class loading
// ----------------------------------------------------------------------
/**
* Returns the class represented by {@code className} using the
* {@code classLoader}. This implementation supports the syntaxes
* "{@code java.util.Map.Entry[]}", "{@code java.util.Map$Entry[]}",
* "{@code [Ljava.util.Map.Entry;}", and "{@code [Ljava.util.Map$Entry;}".
*
* @param classLoader the class loader to use to load the class
* @param className the class name
* @param initialize whether the class must be initialized
* @return the class represented by {@code className} using the {@code classLoader}
* @throws ClassNotFoundException if the class is not found
*/
public static
Class<?>
getClass(
final
ClassLoader classLoader, final
String className, final boolean
initialize) throws
ClassNotFoundException {
try {
Class<?>
clazz;
if (
namePrimitiveMap.
containsKey(
className)) {
clazz =
namePrimitiveMap.
get(
className);
} else {
clazz =
Class.
forName(
toCanonicalName(
className),
initialize,
classLoader);
}
return
clazz;
} catch (final
ClassNotFoundException ex) {
// allow path separators (.) as inner class name separators
final int
lastDotIndex =
className.
lastIndexOf(
PACKAGE_SEPARATOR_CHAR);
if (
lastDotIndex != -1) {
try {
return
getClass(
classLoader,
className.
substring(0,
lastDotIndex) +
INNER_CLASS_SEPARATOR_CHAR +
className.
substring(
lastDotIndex + 1),
initialize);
} catch (final
ClassNotFoundException ex2) { // NOPMD
// ignore exception
}
}
throw
ex;
}
}
/**
* Returns the (initialized) class represented by {@code className}
* using the {@code classLoader}. This implementation supports
* the syntaxes "{@code java.util.Map.Entry[]}",
* "{@code java.util.Map$Entry[]}", "{@code [Ljava.util.Map.Entry;}",
* and "{@code [Ljava.util.Map$Entry;}".
*
* @param classLoader the class loader to use to load the class
* @param className the class name
* @return the class represented by {@code className} using the {@code classLoader}
* @throws ClassNotFoundException if the class is not found
*/
public static
Class<?>
getClass(final
ClassLoader classLoader, final
String className) throws
ClassNotFoundException {
return
getClass(
classLoader,
className, true);
}
/**
* Returns the (initialized) class represented by {@code className}
* using the current thread's context class loader. This implementation
* supports the syntaxes "{@code java.util.Map.Entry[]}",
* "{@code java.util.Map$Entry[]}", "{@code [Ljava.util.Map.Entry;}",
* and "{@code [Ljava.util.Map$Entry;}".
*
* @param className the class name
* @return the class represented by {@code className} using the current thread's context class loader
* @throws ClassNotFoundException if the class is not found
*/
public static
Class<?>
getClass(final
String className) throws
ClassNotFoundException {
return
getClass(
className, true);
}
/**
* Returns the class represented by {@code className} using the
* current thread's context class loader. This implementation supports the
* syntaxes "{@code java.util.Map.Entry[]}", "{@code java.util.Map$Entry[]}",
* "{@code [Ljava.util.Map.Entry;}", and "{@code [Ljava.util.Map$Entry;}".
*
* @param className the class name
* @param initialize whether the class must be initialized
* @return the class represented by {@code className} using the current thread's context class loader
* @throws ClassNotFoundException if the class is not found
*/
public static
Class<?>
getClass(final
String className, final boolean
initialize) throws
ClassNotFoundException {
final
ClassLoader contextCL =
Thread.
currentThread().
getContextClassLoader();
final
ClassLoader loader =
contextCL == null ?
ClassUtils.class.
getClassLoader() :
contextCL;
return
getClass(
loader,
className,
initialize);
}
// Public method
// ----------------------------------------------------------------------
/**
* <p>Returns the desired Method much like {@code Class.getMethod}, however
* it ensures that the returned Method is from a public class or interface and not
* from an anonymous inner class. This means that the Method is invokable and
* doesn't fall foul of Java bug
* <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4071957">4071957</a>).</p>
*
* <pre>
* <code>Set set = Collections.unmodifiableSet(...);
* Method method = ClassUtils.getPublicMethod(set.getClass(), "isEmpty", new Class[0]);
* Object result = method.invoke(set, new Object[]);</code>
* </pre>
*
* @param cls the class to check, not null
* @param methodName the name of the method
* @param parameterTypes the list of parameters
* @return the method
* @throws NullPointerException if the class is null
* @throws SecurityException if a security violation occurred
* @throws NoSuchMethodException if the method is not found in the given class
* or if the method doesn't conform with the requirements
*/
public static
Method getPublicMethod(final
Class<?>
cls, final
String methodName, final
Class<?>...
parameterTypes)
throws
SecurityException,
NoSuchMethodException {
final
Method declaredMethod =
cls.
getMethod(
methodName,
parameterTypes);
if (
Modifier.
isPublic(
declaredMethod.
getDeclaringClass().
getModifiers())) {
return
declaredMethod;
}
final
List<
Class<?>>
candidateClasses = new
ArrayList<
Class<?>>();
candidateClasses.
addAll(
getAllInterfaces(
cls));
candidateClasses.
addAll(
getAllSuperclasses(
cls));
for (final
Class<?>
candidateClass :
candidateClasses) {
if (!
Modifier.
isPublic(
candidateClass.
getModifiers())) {
continue;
}
Method candidateMethod;
try {
candidateMethod =
candidateClass.
getMethod(
methodName,
parameterTypes);
} catch (final
NoSuchMethodException ex) {
continue;
}
if (
Modifier.
isPublic(
candidateMethod.
getDeclaringClass().
getModifiers())) {
return
candidateMethod;
}
}
throw new
NoSuchMethodException("Can't find a public method for " +
methodName + " " +
ArrayUtils.
toString(
parameterTypes));
}
// ----------------------------------------------------------------------
/**
* Converts a class name to a JLS style class name.
*
* @param className the class name
* @return the converted name
*/
private static
String toCanonicalName(
String className) {
className =
StringUtils.
deleteWhitespace(
className);
if (
className == null) {
throw new
NullPointerException("className must not be null.");
} else if (
className.
endsWith("[]")) {
final
StringBuilder classNameBuffer = new
StringBuilder();
while (
className.
endsWith("[]")) {
className =
className.
substring(0,
className.
length() - 2);
classNameBuffer.
append("[");
}
final
String abbreviation =
abbreviationMap.
get(
className);
if (
abbreviation != null) {
classNameBuffer.
append(
abbreviation);
} else {
classNameBuffer.
append("L").
append(
className).
append(";");
}
className =
classNameBuffer.
toString();
}
return
className;
}
/**
* <p>Converts an array of {@code Object} in to an array of {@code Class} objects.
* If any of these objects is null, a null element will be inserted into the array.</p>
*
* <p>This method returns {@code null} for a {@code null} input array.</p>
*
* @param array an {@code Object} array
* @return a {@code Class} array, {@code null} if null array input
* @since 2.4
*/
public static
Class<?>[]
toClass(final
Object...
array) {
if (
array == null) {
return null;
} else if (
array.length == 0) {
return
ArrayUtils.
EMPTY_CLASS_ARRAY;
}
final
Class<?>[]
classes = new
Class[
array.length];
for (int
i = 0;
i <
array.length;
i++) {
classes[
i] =
array[
i] == null ? null :
array[
i].
getClass();
}
return
classes;
}
// Short canonical name
// ----------------------------------------------------------------------
/**
* <p>Gets the canonical name minus the package name for an {@code Object}.</p>
*
* @param object the class to get the short name for, may be null
* @param valueIfNull the value to return if null
* @return the canonical name of the object without the package name, or the null value
* @since 2.4
*/
public static
String getShortCanonicalName(final
Object object, final
String valueIfNull) {
if (
object == null) {
return
valueIfNull;
}
return
getShortCanonicalName(
object.
getClass().
getName());
}
/**
* <p>Gets the canonical name minus the package name from a {@code Class}.</p>
*
* @param cls the class to get the short name for.
* @return the canonical name without the package name or an empty string
* @since 2.4
*/
public static
String getShortCanonicalName(final
Class<?>
cls) {
if (
cls == null) {
return
StringUtils.
EMPTY;
}
return
getShortCanonicalName(
cls.
getName());
}
/**
* <p>Gets the canonical name minus the package name from a String.</p>
*
* <p>The string passed in is assumed to be a canonical name - it is not checked.</p>
*
* @param canonicalName the class name to get the short name for
* @return the canonical name of the class without the package name or an empty string
* @since 2.4
*/
public static
String getShortCanonicalName(final
String canonicalName) {
return
ClassUtils.
getShortClassName(
getCanonicalName(
canonicalName));
}
// Package name
// ----------------------------------------------------------------------
/**
* <p>Gets the package name from the canonical name of an {@code Object}.</p>
*
* @param object the class to get the package name for, may be null
* @param valueIfNull the value to return if null
* @return the package name of the object, or the null value
* @since 2.4
*/
public static
String getPackageCanonicalName(final
Object object, final
String valueIfNull) {
if (
object == null) {
return
valueIfNull;
}
return
getPackageCanonicalName(
object.
getClass().
getName());
}
/**
* <p>Gets the package name from the canonical name of a {@code Class}.</p>
*
* @param cls the class to get the package name for, may be {@code null}.
* @return the package name or an empty string
* @since 2.4
*/
public static
String getPackageCanonicalName(final
Class<?>
cls) {
if (
cls == null) {
return
StringUtils.
EMPTY;
}
return
getPackageCanonicalName(
cls.
getName());
}
/**
* <p>Gets the package name from the canonical name. </p>
*
* <p>The string passed in is assumed to be a canonical name - it is not checked.</p>
* <p>If the class is unpackaged, return an empty string.</p>
*
* @param canonicalName the canonical name to get the package name for, may be {@code null}
* @return the package name or an empty string
* @since 2.4
*/
public static
String getPackageCanonicalName(final
String canonicalName) {
return
ClassUtils.
getPackageName(
getCanonicalName(
canonicalName));
}
/**
* <p>Converts a given name of class into canonical format.
* If name of class is not a name of array class it returns
* unchanged name.</p>
* <p>Example:
* <ul>
* <li>{@code getCanonicalName("[I") = "int[]"}</li>
* <li>{@code getCanonicalName("[Ljava.lang.String;") = "java.lang.String[]"}</li>
* <li>{@code getCanonicalName("java.lang.String") = "java.lang.String"}</li>
* </ul>
* </p>
*
* @param className the name of class
* @return canonical form of class name
* @since 2.4
*/
private static
String getCanonicalName(
String className) {
className =
StringUtils.
deleteWhitespace(
className);
if (
className == null) {
return null;
}
int
dim = 0;
while (
className.
startsWith("[")) {
dim++;
className =
className.
substring(1);
}
if (
dim < 1) {
return
className;
}
if (
className.
startsWith("L")) {
className =
className.
substring(
1,
className.
endsWith(";")
?
className.
length() - 1
:
className.
length());
} else {
if (
className.
length() > 0) {
className =
reverseAbbreviationMap.
get(
className.
substring(0, 1));
}
}
final
StringBuilder canonicalClassNameBuffer = new
StringBuilder(
className);
for (int
i = 0;
i <
dim;
i++) {
canonicalClassNameBuffer.
append("[]");
}
return
canonicalClassNameBuffer.
toString();
}
/**
* Get an {@link Iterable} that can iterate over a class hierarchy in ascending (subclass to superclass) order,
* excluding interfaces.
*
* @param type the type to get the class hierarchy from
* @return Iterable an Iterable over the class hierarchy of the given class
* @since 3.2
*/
public static
Iterable<
Class<?>>
hierarchy(final
Class<?>
type) {
return
hierarchy(
type,
Interfaces.
EXCLUDE);
}
/**
* Get an {@link Iterable} that can iterate over a class hierarchy in ascending (subclass to superclass) order.
*
* @param type the type to get the class hierarchy from
* @param interfacesBehavior switch indicating whether to include or exclude interfaces
* @return Iterable an Iterable over the class hierarchy of the given class
* @since 3.2
*/
public static
Iterable<
Class<?>>
hierarchy(final
Class<?>
type, final
Interfaces interfacesBehavior) {
final
Iterable<
Class<?>>
classes = new
Iterable<
Class<?>>() {
@
Override
public
Iterator<
Class<?>>
iterator() {
final
MutableObject<
Class<?>>
next = new
MutableObject<
Class<?>>(
type);
return new
Iterator<
Class<?>>() {
@
Override
public boolean
hasNext() {
return
next.
getValue() != null;
}
@
Override
public
Class<?>
next() {
final
Class<?>
result =
next.
getValue();
next.
setValue(
result.
getSuperclass());
return
result;
}
@
Override
public void
remove() {
throw new
UnsupportedOperationException();
}
};
}
};
if (
interfacesBehavior !=
Interfaces.
INCLUDE) {
return
classes;
}
return new
Iterable<
Class<?>>() {
@
Override
public
Iterator<
Class<?>>
iterator() {
final
Set<
Class<?>>
seenInterfaces = new
HashSet<
Class<?>>();
final
Iterator<
Class<?>>
wrapped =
classes.
iterator();
return new
Iterator<
Class<?>>() {
Iterator<
Class<?>>
interfaces =
Collections.<
Class<?>>
emptySet().
iterator();
@
Override
public boolean
hasNext() {
return
interfaces.
hasNext() ||
wrapped.
hasNext();
}
@
Override
public
Class<?>
next() {
if (
interfaces.
hasNext()) {
final
Class<?>
nextInterface =
interfaces.
next();
seenInterfaces.
add(
nextInterface);
return
nextInterface;
}
final
Class<?>
nextSuperclass =
wrapped.
next();
final
Set<
Class<?>>
currentInterfaces = new
LinkedHashSet<
Class<?>>();
walkInterfaces(
currentInterfaces,
nextSuperclass);
interfaces =
currentInterfaces.
iterator();
return
nextSuperclass;
}
private void
walkInterfaces(final
Set<
Class<?>>
addTo, final
Class<?>
c) {
for (final
Class<?>
iface :
c.
getInterfaces()) {
if (!
seenInterfaces.
contains(
iface)) {
addTo.
add(
iface);
}
walkInterfaces(
addTo,
iface);
}
}
@
Override
public void
remove() {
throw new
UnsupportedOperationException();
}
};
}
};
}
}