com.oracle.tools.util.ReflectionHelper Maven / Gradle / Ivy
/*
* File: ReflectionHelper.java
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* The contents of this file are subject to the terms and conditions of
* the Common Development and Distribution License 1.0 (the "License").
*
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the License by consulting the LICENSE.txt file
* distributed with this file, or by consulting https://oss.oracle.com/licenses/CDDL
*
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file LICENSE.txt.
*
* MODIFICATIONS:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*/
package com.oracle.tools.util;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import org.objenesis.Objenesis;
import org.objenesis.ObjenesisStd;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* A collection of utilities to assist in using Reflection to create objects.
*
* Copyright (c) 2010. All Rights Reserved. Oracle Corporation.
* Oracle is a registered trademark of Oracle Corporation and/or its affiliates.
*
* @author Christer Fahlgren
* @author Brian Oliver
*/
public class ReflectionHelper
{
/**
* Get a compatible constructor to the supplied parameter types.
*
* @param clazz the class which we want to construct
* @param argumentTypes the types required of the constructor
*
* @return a compatible constructor or null if none exists
*/
public static Constructor> getCompatibleConstructor(Class> clazz,
Class>[] argumentTypes)
{
// first attempt to find a constructor with the exact types
try
{
return clazz.getConstructor(argumentTypes);
}
catch (NoSuchMethodException e)
{
}
// try to find a constructor with compatible types
for (Constructor constructor : clazz.getConstructors())
{
Class>[] formalParameterTypes = constructor.getParameterTypes();
if (argumentTypes.length == formalParameterTypes.length)
{
boolean parametersMatch = true;
// check compatibility of each formal and actual parameter
for (int i = 0; i < argumentTypes.length && parametersMatch; i++)
{
Class> argumentType = argumentTypes[i];
Class> formalParameterType = formalParameterTypes[i];
if (argumentType == null)
{
// a null argument cannot be assigned to a primitive parameter
parametersMatch = !formalParameterType.isPrimitive();
}
else if (argumentType.isPrimitive())
{
parametersMatch = isAssignablePrimitive(argumentType, formalParameterType);
}
else
{
parametersMatch = formalParameterType.isAssignableFrom(argumentType);
}
}
if (parametersMatch)
{
return constructor;
}
}
}
return null;
}
/**
* Determines if a primitive type is assignable to a wrapper type.
*
* @param clzPrimitive a primitive class type
* @param clzWrapper a wrapper class type
*
* @return true if primitive and wrapper are assignment compatible
*/
public static boolean isAssignablePrimitive(Class> clzPrimitive,
Class> clzWrapper)
{
return (clzPrimitive.equals(java.lang.Boolean.TYPE) && clzWrapper.equals(java.lang.Boolean.class))
|| (clzPrimitive.equals(java.lang.Byte.TYPE) && clzWrapper.equals(java.lang.Byte.class))
|| (clzPrimitive.equals(java.lang.Character.TYPE) && clzWrapper.equals(java.lang.Character.class))
|| (clzPrimitive.equals(java.lang.Double.TYPE) && clzWrapper.equals(java.lang.Double.class))
|| (clzPrimitive.equals(java.lang.Float.TYPE) && clzWrapper.equals(java.lang.Float.class))
|| (clzPrimitive.equals(java.lang.Integer.TYPE) && clzWrapper.equals(java.lang.Integer.class))
|| (clzPrimitive.equals(java.lang.Long.TYPE) && clzWrapper.equals(java.lang.Long.class))
|| (clzPrimitive.equals(java.lang.Short.TYPE) && clzWrapper.equals(java.lang.Short.class));
}
/**
* Obtains the {@link Method} that is compatible to the supplied parameter types.
*
* @param clazz the {@link Class} on which to find the {@link Method}
* @param actualArguments the actual arguments for the {@link Method}
*
* @return a compatible {@link Method} or null
if one can't be found
*/
public static Method getCompatibleMethod(Class> clazz,
String methodName,
Object... actualArguments)
{
// determine the types of the arguments
Class>[] argumentTypes = new Class>[actualArguments.length];
for (int i = 0; i < actualArguments.length; i++)
{
argumentTypes[i] = actualArguments[i] == null ? null : actualArguments[i].getClass();
}
// first attempt to find a method with the exact types
try
{
return clazz.getMethod(methodName, argumentTypes);
}
catch (NoSuchMethodException e)
{
}
// try to find a method with compatible types
for (Method method : clazz.getMethods())
{
if (methodName.equals(method.getName()))
{
Class>[] formalParameterTypes = method.getParameterTypes();
if (argumentTypes.length == formalParameterTypes.length)
{
boolean parametersMatch = true;
// check compatibility of each formal and actual parameter
for (int i = 0; i < argumentTypes.length && parametersMatch; i++)
{
Class> argumentType = argumentTypes[i];
Class> formalParameterType = formalParameterTypes[i];
if (argumentType == null)
{
// a null argument cannot be assigned to a primitive parameter
parametersMatch = !formalParameterType.isPrimitive();
}
else if (argumentType.isPrimitive())
{
parametersMatch = isAssignablePrimitive(argumentType, formalParameterType);
}
else if (formalParameterType.isPrimitive())
{
parametersMatch = isAssignablePrimitive(formalParameterType, argumentType);
}
else
{
parametersMatch = formalParameterType.isAssignableFrom(argumentType);
}
}
if (parametersMatch)
{
return method;
}
}
}
}
return null;
}
/**
* Create an Object via reflection (using the specified {@link ClassLoader}).
*
* @param className The name of the class to instantiate.
* @param classLoader The {@link ClassLoader} to use to load the class.
*
* @return A new instance of the class specified by the className
*
* @throws ClassNotFoundException if the class is not found
* @throws NoSuchMethodException if there is no such constructor
* @throws InstantiationException if it failed to instantiate
* @throws IllegalAccessException if security doesn't allow the call
* @throws InvocationTargetException if the constructor failed
*/
public static Object createObject(String className,
ClassLoader classLoader)
throws ClassNotFoundException, NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException
{
Class> clazz = Class.forName(className, true, classLoader);
Constructor> con = clazz.getDeclaredConstructor((Class[]) null);
return con.newInstance((Object[]) null);
}
/**
* Create an Object via reflection (using the specified {@link ClassLoader}).
*
* @param className The name of the class to instantiate.
* @param classLoader The {@link ClassLoader} to use to load the class.
* @param constructorParameterList The set of parameters to pass to the constructor
*
* @return A new instance of the class specified by the className
*
* @throws ClassNotFoundException if the class is not found
* @throws NoSuchMethodException if there is no such constructor
* @throws InstantiationException if it failed to instantiate
* @throws IllegalAccessException if security doesn't allow the call
* @throws InvocationTargetException if the constructor failed
*/
public static Object createObject(String className,
ClassLoader classLoader,
Object... constructorParameterList)
throws ClassNotFoundException, NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException
{
Class> clazz = Class.forName(className, true, classLoader);
Class>[] parameterTypes = getClassArrayFromObjectArray(constructorParameterList);
Constructor> con = ReflectionHelper.getCompatibleConstructor(clazz, parameterTypes);
return con.newInstance(constructorParameterList);
}
/**
* Returns an array of Class objects representing the class of the objects in the parameter.
*
* @param objectArray the array of Objects
*
* @return an array of Classes representing the class of the Objects
*/
protected static Class>[] getClassArrayFromObjectArray(Object[] objectArray)
{
Class>[] parameterTypes = null;
if (objectArray != null)
{
parameterTypes = new Class[objectArray.length];
for (int i = 0; i < objectArray.length; i++)
{
parameterTypes[i] = objectArray[i].getClass();
}
}
return parameterTypes;
}
/**
* Creates a dynamic proxy of the specified {@link Object} routing all
* method calls to the specified {@link MethodInterceptor}.
*
* @param object the {@link Object} to proxy
* @param interceptor the {@link MethodInterceptor}
*
* @return a dynamic proxy of the specified {@link Object}
*/
@SuppressWarnings("unchecked")
public static T createProxyOf(T object,
MethodInterceptor interceptor)
{
return (T) createProxyOf(object.getClass(), interceptor);
}
/**
* Creates a dynamic proxy of the specified {@link Class} routing all
* method calls to the specified {@link MethodInterceptor}.
*
* @param clazz the {@link Class} to proxy
* @param interceptor the {@link MethodInterceptor}
*
* @return a dynamic proxy of the specified {@link Class}
*/
@SuppressWarnings("unchecked")
public static T createProxyOf(Class clazz,
MethodInterceptor interceptor)
{
// use a cglib enhancer to create the proxy
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallbackType(interceptor.getClass());
Class proxiedClass = (Class) enhancer.createClass();
Enhancer.registerCallbacks(proxiedClass, new Callback[] {interceptor});
Objenesis objenesis = new ObjenesisStd();
T proxy = (T) objenesis.newInstance(proxiedClass);
return proxy;
}
}