/*******************************************************************************
* $Id$
* $Author$
* $Date$
*
* Copyright 2002-2003 YAJUL Developers, Joshua Davis, Kent Vogel.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
******************************************************************************/
package org.yajul.util;
import org.yajul.collections.CollectionUtil;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Reflection utilities.
* User: josh
* Date: Nov 16, 2003
* Time: 4:45:36 PM
*/
public class ReflectionUtil {
private static final Logger log = Logger.getLogger(ReflectionUtil.class.getName());
/**
* Returns a map of (Integer->String) from the values of
* any static integer constants in the class.
*
* @param c The class to get the constants from.
* @return Map - A map of the constant integer values to their names.
* @noinspection EmptyCatchBlock
*/
public static Map<Integer, String> getConstantNameMap(Class c) {
return getConstantNameMap(c, Integer.class);
}
/**
* Returns a map of (Integer->String) from the values of
* any static integer constants in the class.
*
* @param c The class to get the constants from.
* @param valueType the value type to look for
* @return Map - A map of the constant values to their names.
* @noinspection EmptyCatchBlock
*/
public static <T> Map<T, String> getConstantNameMap(Class<?> c, Class<T> valueType) {
Field[] fields = c.getFields();
Map<T, String> map = CollectionUtil.newHashMap();
for (Field field : fields) {
if (Modifier.isStatic(field.getModifiers())) {
Object value;
try {
value = field.get(null);
if (valueType.isAssignableFrom(value.getClass())) {
T val = valueType.cast(value);
map.put(val, field.getName());
}
}
catch (IllegalArgumentException ignore) {
}
catch (IllegalAccessException ignore) {
}
}
} // for
return map;
}
/**
* Wraps java.lang.Class.getMethod(String, Class[]) so it returns null instead of throwing
* NoSuchMethodException.
*
* @param clazz the class to get the method from
* @param name the name of the method
* @param parameterTypes the parameter types
* @return the method, or null if the method was not found.
* @see java.lang.Class#getMethod(String, Class[])
*/
public static Method getMethodOrNull(Class<?> clazz, String name, Class<?>... parameterTypes) {
try {
return clazz.getMethod(name, parameterTypes);
}
catch (NoSuchMethodException e) {
return null;
}
}
/**
* Returns true if the method is an implementation of a method in the specified class.
* @param method the method, usually from an implementing class.
* @param clazz the class (usually an interface)
* @return true if the method is an implementation of a method in the specified class.
*/
public static boolean isDefinedIn(Method method, Class<?> clazz) {
// Don't bother if the declaring class doesn't even implement the interface.
if (!clazz.isAssignableFrom(method.getDeclaringClass()))
return false;
final String name = method.getName();
final Class<?>[] parameterTypes = method.getParameterTypes();
return getMethodOrNull(clazz, name, parameterTypes) != null;
}
/**
* Returns true if the method is a property getter.
*
* @param method The method.
* @return true if the method is a property getter.
*/
public static boolean isPropertyGetter(Method method) {
return getterPropertyName(method) != null;
}
/**
* Returns the name of the property IFF the method is a property getter, or null if the
* method is not a getter.
*
* @param method The method.
* @return the name of the property IFF the method is a property getter, or null if the
* method is not a getter.
*/
public static String getterPropertyName(Method method) {
// It can't be a getter if it has a void return type.
Class returnType = method.getReturnType();
if (returnType.equals(Void.TYPE))
return null;
// It can't be a getter if it has parameters.
if (method.getParameterTypes().length != 0)
return null;
// It can't be a getter if it's static.
if (Modifier.isStatic(method.getModifiers()))
return null;
String name = method.getName();
if (name.startsWith("get") && (!name.equals("getClass")))
return methodNameAsPropertyName(name, 3);
else if (name.startsWith("is") && (returnType.equals(Boolean.TYPE) || returnType.equals(Boolean.class)))
return methodNameAsPropertyName(name, 2);
else
return null;
}
/**
* Returns the name of the property IFF the method is a property setter, or null if the
* method is not a setter.
*
* @param method The method.
* @return the name of the property IFF the method is a property setter, or null if the
* method is not a setter.
*/
public static String setterPropertyName(Method method) {
// It can't be a setter if it has no parameters.
if (method.getParameterTypes().length == 0)
return null;
// It can't be a setter if it's static.
if (Modifier.isStatic(method.getModifiers()))
return null;
String name = method.getName();
if (name.startsWith("set"))
return methodNameAsPropertyName(name, 3);
else
return null;
}
private static String methodNameAsPropertyName(String name, int prefixLength) {
return Character.toLowerCase(name.charAt(prefixLength)) + name.substring(prefixLength + 1);
}
public static ClassLoader getCurrentClassLoader() {
// lets get a class loader. By using the Thread's class loader, we allow
// for more flexability.
ClassLoader classLoader;
try {
classLoader = Thread.currentThread().getContextClassLoader();
}
catch (SecurityException se) {
classLoader = ReflectionUtil.class.getClassLoader();
if (log.isLoggable(Level.FINE))
log.log(Level.FINE,"Unable to use context class loader, using system ClassLoader " + classLoader);
}
return classLoader;
}
/**
* Creates a new instance of the class using the specified loader.
*
* @param className The class to instatiate - if null a default will be used
* @param loader The class loader to use when obtaining the instance
* @return An Object which is an instance of the class
*/
public static Object createInstance(String className, ClassLoader loader) {
try {
Class c;
if (loader == null)
loader = getCurrentClassLoader();
c = loader.loadClass(className);
return c.newInstance();
}
catch (ClassNotFoundException x) {
throw new InitializationError("Class " + className
+ " not found - " + "please check your classpath", x);
}
catch (ExceptionInInitializerError eiie) {
throw new InitializationError("Class " + className
+ " failed to initialize due to: " + eiie.getMessage(), eiie);
}
catch (InstantiationException ie) {
throw new InitializationError("Class " + className
+ " could not be instantiated - "
+ "is it abstract, an interface, an array, or does it not have an "
+ "empty constructor?", ie);
}
catch (IllegalAccessException iae) {
throw new InitializationError("Class " + className
+ " could not be accessed - "
+ "is it private or is the empty constructor private?",
iae);
}
}
/**
* Creates a new instance of the class, throwing an initialization error, if there
* was a problem.
*
* @param className The class name
* @return Object - The instance.
*/
public static Object createInstance(String className) {
return createInstance(className, getCurrentClassLoader());
}
/**
* Type checked version of create instance using the default class loader.
*
* @param className the class name
* @param classLoader the class loader
* @param componentType the type of the instance (e.g. an interface)
* @return an instance of the object cast to 'componentType'
*/
public static <T> T createInstance(String className, ClassLoader classLoader, Class<T> componentType) {
return componentType.cast(createInstance(className, classLoader));
}
/**
* Type checked version of create instance using the default class loader.
*
* @param className the class name
* @param componentType the type of the instance (e.g. an interface)
* @return an instance of the object cast to 'componentType'
*/
public static <T> T createInstance(String className, Class<T> componentType) {
return createInstance(className, getCurrentClassLoader(), componentType);
}
/**
* Returns the resource name for the specified class.
*
* @param c The class.
* @return String - The resource name for the class.
*/
public static String getClassResourceName(Class c) {
String s = c.getName();
return s.replace('.', '/') + ".class";
}
/**
* Returns the URL where the specified class is located in the current classpath.
*
* @param c The class to look for.
* @return URL - The URL where the class was found, using the current class loader.
*/
public static URL findClassURL(Class c) {
String resourceName = getClassResourceName(c);
ClassLoader loader = getCurrentClassLoader();
return loader.getResource(resourceName);
}
/**
* Turns a class file name into a class name.
*
* @param filename the file name
* @return the class name.
*/
public static String filenameToClassname(String filename) {
return filename.substring(0, filename.lastIndexOf(".class"))
.replace('/', '.').replace('\\', '.');
}
/**
* Loads the class with the current class loader, throws InitializationError if there was a problem.
*
* @param className the class name
* @return the class
*/
public static Class loadClass(String className) {
try {
return getCurrentClassLoader().loadClass(className);
}
catch (ClassNotFoundException e) {
throw new InitializationError(e);
}
}
/**
* Creates an insance of a class, throws InitializationError if there is a problem.
*
* @param implementationClass the class
* @param <T> the class
* @return an instance of the class
*/
public static <T> T createInstance(Class<T> implementationClass) {
try {
return implementationClass.newInstance();
}
catch (IllegalAccessException e) {
throw new InitializationError(e);
}
catch (InstantiationException e) {
throw new InitializationError(e);
}
}
/**
* Create an instance, return null if it doesn't work.
*
* @param implementationClass the class to instantiate
* @param <T> the type
* @return an instance, or null if it doesn't work
*/
public static <T> T createInstanceNoThrow(
Class<T> implementationClass) {
try {
return implementationClass.newInstance();
}
catch (Exception e) {
return null;
}
}
/**
* Create an instance, return null if it doesn't work.
*
* @param implementationClassName the class name to instantiate
* @param type the type, usually an interface
* @param <T> the type
* @return an instance, or null if it doesn't work
*/
public static <T> T createInstanceNoThrow(String implementationClassName, Class<T> type) {
try {
ClassLoader loader = getCurrentClassLoader();
Class implementationClass = loader.loadClass(implementationClassName);
return type.cast(implementationClass.newInstance());
}
catch (Exception e) {
// Don't log anything. Just return null.
return null;
}
}
}