/*
Copyright 1996-2009 Ariba, Inc.
Licensed 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.
$Id: //ariba/platform/util/core/ariba/util/core/ClassUtil.java#23 $
*/
package ariba.util.core;
import java.util.Map;
import ariba.util.log.Log;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
ClassUtil. A set of helpers for dealing with java classes.
@aribaapi documented
*/
public final class ClassUtil
{
public static String NativeInteger = "int";
public static String NativeBoolean = "boolean";
public static String NativeDouble = "double";
public static String NativeFloat = "float";
public static String NativeLong = "long";
public static String NativeByte = "byte";
public static String NativeShort = "short";
public static String NativeChar = "char";
/* prevent people from creating this class */
private ClassUtil ()
{
}
/**
Make sure a class' static inits have run. This method loads
the specified class to make sure that it's static methods have
run. If the class is not found, no warning will be printed.
@param name the name of the class to load.
@aribaapi documented
*/
public static void classTouch (String name)
{
classForName(name, Object.class, false);
}
public static Class classForNativeType (String typeName)
{
if (NativeInteger.equals(typeName)) {
return Integer.TYPE;
}
if (NativeBoolean.equals(typeName)) {
return Boolean.TYPE;
}
if (NativeDouble.equals(typeName)) {
return Double.TYPE;
}
if (NativeFloat.equals(typeName)) {
return Float.TYPE;
}
if (NativeLong.equals(typeName)) {
return Long.TYPE;
}
if (NativeByte.equals(typeName)) {
return Byte.TYPE;
}
if (NativeShort.equals(typeName)) {
return Short.TYPE;
}
if (NativeChar.equals(typeName)) {
return Character.TYPE;
}
return null;
}
/**
Find a Class for the specified class name. A warning will be
printed if the class was not found.
@param className the name of the class to find
@return the Class for the given <B>className</B>, or null if
the Class doesn't exist.
@see #newInstance
@aribaapi documented
*/
public static Class classForName (String className)
{
return classForName(className, Object.class, true);
}
/**
Find a Class for the specified class name. A warning will be
printed if the class was not found.
@param className the name of the class to find
@param supposedSuperclass The class of the required
superclass for the class specified by className
@return the Class for the given <B>className</B>, or null if
the Class doesn't exist.
@see #newInstance
@aribaapi documented
*/
public static Class classForName (String className,
Class supposedSuperclass)
{
return classForName(className, supposedSuperclass, true);
}
private static final GrowOnlyHashtable ClassForNameCache =
new GrowOnlyHashtable();
private static final Object NoCachedClassFound = Constants.NullObject;
private static final GrowOnlyHashtable LocaleCache =
new GrowOnlyHashtable();
private static ClassFactory classFactory = null;
/**
@aribaapi ariba
set the ClassFactory to be used by classForName and
classTouch. This is for development mode only, it is *not*
supported for production.
*/
public static ClassFactory setClassFactory (ClassFactory cf)
{
ClassFactory oldcf = classFactory;
classFactory = cf;
return oldcf;
}
/**
@aribaapi ariba
get the ClassFactory to be used by classForName and
classTouch. This is for development mode only, it is *not*
supported for production.
*/
public static ClassFactory getClassFactory ()
{
return classFactory;
}
/**
Find a Class for the specified class name. Prints a warning
message if the class can't be found and <B>warning</B> is
true.
@param className the name of the class to find
@param warning if <b>true</b> and the class can not be found,
a warning will be printed
@return the Class for the given <B>className</B>, or null if
the Class doesn't exist.
@see #newInstance
@aribaapi documented
*/
public static Class classForName (String className,
boolean warning)
{
return classForName(className, Object.class, warning);
}
/**
Find a Class for the specified class name. Prints a warning
message if the class can't be found and <B>warning</B> is
true.
@param className the name of the class to find
@param supposedSuperclass The required superclass for the class
@param warning if <b>true</b> and the class can not be found,
or it is not assignable to the supposedSuperclass, a warning
will be printed
@return the Class for the given <B>className</B>, or null if
the Class doesn't exist.
@see #newInstance
@aribaapi documented
*/
public static Class classForName (String className,
Class supposedSuperclass,
boolean warning)
{
if (classFactory != null) {
return classFactory.forName(className);
}
if (className == null) {
return null;
}
if (useContextClassLoader) {
return classForNameUsingContextClassLoader(className,
supposedSuperclass,
warning);
}
// Check for Generics as they are irrelevant and will cause null to be returned.
// So we turn "java.util.List<String>" into "java.util.List"
int leftGenericIndex = className.indexOf('<');
if (leftGenericIndex > 0) {
if (className.charAt(className.length() - 1) == '>') {
className = className.substring(0, leftGenericIndex);
}
else {
Log.util.debug("Getting malformed Generics className: %s", className);
}
}
Object cachedClass = ClassForNameCache.get(className);
if (cachedClass == NoCachedClassFound) {
if (warning) {
Log.util.error(2764, className);
}
return null;
}
if (cachedClass != null) {
// any cached value other than noCachedClassFound is a
// class that can be returned
return checkInstanceOf((Class)cachedClass,
supposedSuperclass,
warning);
}
// if it was not found in the cache, check for the real
// class object
try {
Class classObj = Class.forName(className); // OK
// save the value
ClassForNameCache.put(className, classObj);
return checkInstanceOf(classObj, supposedSuperclass, warning);
}
catch (ClassNotFoundException e) {
// if there was an exception store that object was not
// found and call again. (This lets there be a single path
// for the log error message.)
if (Log.util.isDebugEnabled()) {
Log.util.debug("classForName: %s", SystemUtil.stackTrace(e));
}
ClassForNameCache.put(className, NoCachedClassFound);
return classForName(className, warning);
}
catch (NoClassDefFoundError e) {
// Supposed you as for class foo.bar.Bazqux, and on NT
// it finds the class file foo/bar/BazQux.class. On
// JDK11, this was just a ClassNotFoundException. On
// JDK12, this throws a NoClassDefFoundError. We
// always report the error in this case, because
// someone probing for the class probably really wants
// to know about the typo.
if (Log.util.isDebugEnabled()) {
Log.util.debug("classForName: %s", SystemUtil.stackTrace(e));
}
ClassForNameCache.put(className, NoCachedClassFound);
return classForName(className, warning);
}
catch (SecurityException e) {
// Netscape 4.x Browser VM throws a
// netscape.security.AppletSecurityException which
// extends java.lang.SecurityException. This does not
// conform to the Java API for Class.forName()
if (Log.util.isDebugEnabled()) {
Log.util.debug("classForName: %s", SystemUtil.stackTrace(e));
}
ClassForNameCache.put(className, NoCachedClassFound);
return classForName(className, warning);
}
}
/**
Find a Class for the specified class name. Throws an exception
if the class is not found.
<p>
The java spec does not define behavior with a null
className. If a null is passed in a ClassNotFoundException
will be thrown.
@param className the name of the class to find
@return the Class for the given <B>className</B>
@exception ClassNotFoundException if the class can not be
found.
@see #newInstance
@aribaapi documented
*/
public static Class classForNameWithException (String className)
throws ClassNotFoundException
{
Class returnVal = classForName(className, Object.class, false);
if (returnVal == null) {
throw new ClassNotFoundException(Fmt.S("Could not find class %s",
className));
}
return returnVal;
}
/**
Find a Class for the specified class name. Throws an exception
if the class is not found.
<p>
Each call to classForNameNonCaching reloads the byte codes into
the VM. This is very useful debugging programs that have long
start-up times because classes loaded with classForNameNonCaching
can be recompiled and reloaded into the VM without restarting.
<p>
The pattern parameter allows you to extend the dynamic type of
class loading to other classes that are instantiated by the
class identified in className. If you provide null for this
parameter, then the only class that gets dynamically reloaded
is the className class. Any classes it instantiates will
be based on byte codes cached in the VM. Note the className
needs to conform to the pattern.
<p>
The java spec does not define behavior with a null
className. If a null is passed in a ClassNotFoundException
will be thrown.
This will dynamically load RequisitionTester plus any classes that
RequisitionTester instantiates that also belong to packages that
begin with the string "test".
@param className the name of the class to find - null not allowed
@param pattern the classname pattern for other classes to reload
@param warning if <b>true</b> and the class can not be found,
a warning will be printed
@return the Class for the given <B>className</B>
@see #newInstance
@see ariba.util.core.StringUtil#stringMatchesPattern
@aribaapi private
*/
public static Class classForNameNonCaching (String className,
String pattern,
boolean warning)
{
Assert.that(className != null, "className should not be null");
// See other classForName for explanations on each of
// these exception types.
try {
NonCachingClassLoader nccl = new NonCachingClassLoader(pattern);
return nccl.loadClass(className);
}
catch (ClassNotFoundException e) {
// dealt with below
}
catch (NoClassDefFoundError e) {
// dealt with below
}
catch (SecurityException e) {
// dealt with below
}
if (warning) {
Log.util.error(2764, className);
}
return null;
}
/**
Check if an object is an instance of a class identified by
it's name.
@param object the object to test the instance of
@param className the name of the class being tested for
@return true if <B>object</B> is an instance the class
specified by <B>className</B>, including subclasses;
<b>false</b> otherwise
@aribaapi documented
*/
public static boolean instanceOf (Object object, String className)
{
if (object == null) {
return false;
}
return instanceOf(object.getClass(), classForName(className));
}
/**
Check if one class inherits from another class.
@param instance the class to check the inheritance tree of, must not be null.
@param target the class to check against. If null, will return false.
@return <b>true</b> if the class <B>instance</B> is, or
inherits from, the class <B>target</B>; <b>false</b> otherwise
@aribaapi documented
*/
public static boolean instanceOf (Class instance, Class target)
{
if (target == null) {
return false;
}
return target.isAssignableFrom(instance);
}
/**
Returns the name of the class of the specified object. It is
shorthand for o.getClass().getName()
@param o the object to find the class name of
@return the name as returned by o.getClass().getName()
@aribaapi documented
*/
public static String getClassNameOfObject (Object o)
{
if (o == null) {
return "null";
}
return o.getClass().getName();
}
/**
Creates a new instance of the specified class. If the class
<B>className</B> is derived from BaseObject and you want the
new instance to be properly initialized, you should call
BaseObject.New() instead. If the class can not be found a
warning will be printed.
@param className the name of class to create a new instance of
@return a new instance of the class specified by
<B>className</B>. If the class can not be found, <b>null</b>
will be returned.
@see #classForName
@aribaapi documented
*/
public static Object newInstance (String className)
{
return newInstance(className, true);
}
/**
Creates a new instance of the specified class. If the class
<B>className</B> is derived from BaseObject and you want the
new instance to be properly initialized, you should call
BaseObject.New() instead. If the class can not be found an
optional warning may be printed.
@param className the name of class to create a new instance of
@param warning if <b>true</b> and the class can not be found,
a warning will be printed
@return a new instance of the class specified by
<B>className</B>. If the class can not be found, <b>null</b>
will be returned.
@see #classForName
@aribaapi documented
*/
public static Object newInstance (String className, boolean warning)
{
return newInstance(classForName(className, warning));
}
/**
Creates a new Instance of the specified class with error
checking.
Use this when the new object created should be a subclass of
some known class. Prints an error message if the class cannot
be created.
@param className the class to create a new instance of
@param supposedSuperclassName The name of the required
superclass for the new class
@return a new instance of the class <b>theClass</b>. If there
is an error, <b>null</b> will be returned.
@aribaapi documented
*/
public static Object newInstance (String className,
String supposedSuperclassName)
{
return newInstance(className, supposedSuperclassName, true);
}
/**
Creates a new Instance of the specified class with error
checking.
Use this when the new object created should be a subclass of
some known class.
@param className the class to create a new instance of
@param supposedSuperclassName The name of the required
superclass for the new class
@param warning if <b>true</b> and the class can not be found,
a warning will be printed
@return a new instance of the class <b>theClass</b>. If there
is an error, <b>null</b> will be returned.
@aribaapi documented
*/
public static Object newInstance (String className,
String supposedSuperclassName,
boolean warning)
{
return newInstance(className,
classForName(supposedSuperclassName, warning),
warning);
}
/**
Creates a new Instance of the specified class with error
checking.
Use this when the new object created should be a subclass of
some known class.
@param className the class to create a new instance of
@param supposedSuperclass The class of the required
superclass for the new class
@param warning if <b>true</b> and the class can not be found,
a warning will be printed
@return a new instance of the class <b>theClass</b>. If there
is an error, <b>null</b> will be returned.
@aribaapi documented
*/
public static Object newInstance (String className,
Class supposedSuperclass,
boolean warning)
{
return newInstance(classForName(className, warning),
supposedSuperclass,
warning);
}
/**
Creates a new Instance of the specified class with error
checking.
Use this when the new object created should be a subclass of
some known class.
@param classObj the class to create a new instance of
@param supposedSuperclass The class of the required
superclass for the new class
@param warning if <b>true</b> and the class can not be found,
a warning will be printed
@return a new instance of the class <b>theClass</b>. If there
is an error, <b>null</b> will be returned.
@aribaapi documented
*/
public static Object newInstance (Class classObj,
Class supposedSuperclass,
boolean warning)
{
if (classObj == null) {
return null;
}
if (supposedSuperclass == null) {
return null;
}
Class clazz = checkInstanceOf(classObj, supposedSuperclass, warning);
if (clazz == null) {
return null;
}
return newInstance(clazz);
}
/**
Creates a new instance of the specified class.
If the class <B>theClass</B> is derived from BaseObject and
you want the new instance to be properly initialized, you
should call BaseObject.New() instead. If the instance can not
be created, a warning will be printed.
@param theClass the class to create a new instance of
@return a new instance of the class <b>theClass</b>. If there
is an error, <b>null</b> will be returned.
@see #classForName
@aribaapi documented
*/
public static Object newInstance (Class theClass)
{
if (theClass == null) {
return null;
}
try {
return theClass.newInstance();
}
catch (InstantiationException e) {
Log.util.error(2765, theClass.getName(), e);
}
catch (IllegalAccessException e) {
Log.util.error(2766, theClass.getName(), e);
}
return null;
}
/**
Strips any package specifiers from the given class name. For
instance, "java.util.List" will become "List".
@param className class name to strip
@return the class name without the package prefix
@aribaapi documented
*/
public static String stripPackageFromClassName (String className)
{
int pos = className.lastIndexOf('.');
if (pos > 0) {
return className.substring(pos + 1);
}
return className;
}
/**
Find the package specifier for a given class name. For
instance, "java.util.List" will become "java.util".
@param className class name to strip
@return the package specifier for the given <B>className</B>
@aribaapi documented
*/
public static String stripClassFromClassName (String className)
{
int pos = className.lastIndexOf('.');
if (pos > 0) {
return className.substring(0, pos);
}
return "";
}
/**
Invokes the specified static method of the specified class. If
thrown, NoSuchMethodException, ClassNotFoundException,
InvocationTargetException, and IllegalAccessException are
silently caught and null is returned.
@param className the name of the class to invoke the static
method on
@param methodName the name of the method to call
@return the result of the method invocation, or if there was
an exception while trying to find or invoke the method, null
is returned.
@see java.lang.reflect.Method#invoke
@aribaapi documented
*/
public static Object invokeStaticMethod (String className,
String methodName)
{
try {
Class c = ClassUtil.classForName(className);
if (c != null) {
Method m = c.getMethod(methodName);
return m.invoke(null);
}
}
catch (NoSuchMethodException e) {
}
catch (InvocationTargetException e) {
}
catch (IllegalAccessException e) {
}
return null;
}
/**
Invokes the specified static method of the specified class.
@param className the name of the class to invoke the static
method on
@param methodName the name of the method to call
@param paramTypes an array of Class types that *exactly* match
the signature of the method being invoked.
@param args an array of Object arguments to the method
@return the result of the method invocation, or if there was
an exception while trying to find or invoke the method, null
is returned.
@see java.lang.reflect.Method#invoke
@aribaapi documented
*/
public static Object invokeStaticMethod (String className,
String methodName,
Class[] paramTypes,
Object[] args)
{
try {
Class c = ClassUtil.classForName(className);
if (c != null) {
Method m = c.getMethod(methodName, paramTypes);
return m.invoke(null, args);
}
}
catch (NoSuchMethodException e) {
Assert.that(false, "NoSuchMethod :%s", SystemUtil.stackTrace(e));
}
catch (InvocationTargetException e) {
Assert.that(false, "InvocationTargetException :%s", SystemUtil.stackTrace(e));
}
catch (IllegalAccessException e) {
Assert.that(false, "IllegalAccessException :%s", SystemUtil.stackTrace(e));
}
return null;
}
/**
ClassUtil.getDeclaredFields returns all the declared fields
for the class and it's superclasses.
The standard Class.getFields only returns public fields for
the class and it's superclases, and the
Class.getDeclaredFields returns all fields, public and
private, but doesn't return superclass fields.
This method does both private fields and superclass fields.
@param clazz the Class to discover the fields of
@return an array of all public and private fields accessable
from this class
@aribaapi documented
*/
public static Field[] getDeclaredFields (Class clazz)
{
// Also count fields to allocate array size
int fieldCount = 0;
Class c = clazz;
while (c != null) {
fieldCount += c.getDeclaredFields().length;
c = c.getSuperclass();
}
Field[] fields = new Field[fieldCount];
c = clazz;
while (c != null) {
Field[] declaredFields = c.getDeclaredFields();
int length = declaredFields.length;
fieldCount -= length;
System.arraycopy(declaredFields, 0,
fields, fieldCount,
length);
c = c.getSuperclass();
}
return fields;
}
private static final Map typeToVMMap = MapUtil.map();
static {
typeToVMMap.put(Constants.CharPrimitiveType,
Constants.JavaCharAbbreviation);
typeToVMMap.put(Constants.BytePrimitiveType,
Constants.JavaByteAbbreviation);
typeToVMMap.put(Constants.ShortPrimitiveType,
Constants.JavaShortAbbreviation);
typeToVMMap.put(Constants.IntPrimitiveType,
Constants.JavaIntAbbreviation);
typeToVMMap.put(Constants.LongPrimitiveType,
Constants.JavaLongAbbreviation);
typeToVMMap.put(Constants.FloatPrimitiveType,
Constants.JavaFloatAbbreviation);
typeToVMMap.put(Constants.DoublePrimitiveType,
Constants.JavaDoubleAbbreviation);
typeToVMMap.put(Constants.BooleanPrimitiveType,
Constants.JavaBooleanAbbreviation);
}
/**
Convert from a class name into an internal java representation
of that class String
int -> I
java.lang.String -> Ljava.lang.String;
@aribaapi private
*/
public static String typeToVMType (String type)
{
String abbrev = (String)typeToVMMap.get(type);
if (abbrev != null) {
return abbrev;
}
return Fmt.S("L%s;", type);
}
/**
Find a Class for the specified class name using the current thread's
context class loader. Prints a warning message if the class can't be found
and <B>warning</B> is true.
@param className the name of the class to find
@param warning if <b>true</b> and the class can not be found,
a warning will be printed
@return the Class for the given <B>className</B>, or null if
the Class doesn't exist.
@see #newInstance
*/
private static Class classForNameUsingContextClassLoader (
String className,
Class supposedSuperclass,
boolean warning)
{
try {
ClassLoader loader =
Thread.currentThread().getContextClassLoader();
return checkInstanceOf(Class.forName(className, true, loader), // OK
supposedSuperclass,
warning);
}
catch (ClassNotFoundException e) {
// dealt with below
}
catch(NoClassDefFoundError e) {
// dealt with below
}
catch (SecurityException e) {
// dealt with below
}
if (warning) {
Log.util.error(2764, className);
}
return null;
}
/**
If the classObj is assignable to the supposedSuperclass,
return it. Otherwise optionally print a warning and return
null.
@param classObj the class to test
@param supposedSuperclass The required superclass for the new class
@param warning if <b>true</b> and the class is not assigned to
the supposed superclass, a warning will be printed
@aribaapi private
*/
private static Class checkInstanceOf (Class classObj,
Class supposedSuperclass,
boolean warning)
{
if (instanceOf(classObj, supposedSuperclass)) {
return classObj;
}
if (warning) {
Log.util.error(4803, classObj.getName(), supposedSuperclass.getName());
}
return null;
}
/**
This system property if true allows the use of the current thread's
context class loader. Some application (like Sourcing) needs to
use this class loader.
*/
private static final String UseContextClassLoaderProperty =
"ariba.util.core.ClassUtil.useContextClassLoader";
/**
This boolean allows the use of the context class loader via
the use of UseContextClassLoaderProperty.
*/
private static final boolean useContextClassLoader;
static {
boolean b = Boolean.getBoolean(UseContextClassLoaderProperty);
useContextClassLoader = b;
}
private static final Class[] cloneArgType = new Class[0];
private static final Object[] cloneArgValues = new Object[0];
/**
clone java.lang.Object.
@aribaapi private
*/
static Object clone (Object o)
{
Assert.that(o instanceof Cloneable, "Object is not cloneable: %s",
o.getClass().getName());
try {
Class thisClass = o.getClass();
Method m = thisClass.getMethod("clone", cloneArgType);
return m.invoke(o, cloneArgValues);
}
catch (NoSuchMethodException e) {
Assert.that(false, "NoSuchMethod :%s", SystemUtil.stackTrace(e));
}
catch (InvocationTargetException e) {
Assert.that(false, "InvocationTargetException :%s", SystemUtil.stackTrace(e));
}
catch (IllegalAccessException e) {
Assert.that(false, "IllegalAccessException :%s", SystemUtil.stackTrace(e));
}
return null;
}
}