blob: 9af485764ff5575c8dd74589a18e4d7d51556cc7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008 The University of York.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* Contributors:
* Dimitrios Kolovos - initial API and implementation
******************************************************************************/
package org.eclipse.epsilon.eol.util;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.epsilon.common.module.ModuleElement;
import org.eclipse.epsilon.eol.exceptions.EolInternalException;
import org.eclipse.epsilon.eol.exceptions.EolRuntimeException;
import org.eclipse.epsilon.eol.types.EolNativeType;
public class ReflectionUtil {
public static boolean hasMethods(Object obj, String methodName) {
if (obj == null) return false;
for (Method method : obj.getClass().getMethods()) {
if (getMethodName(method).equals(methodName)) {
return true;
}
}
return false;
}
public static Set<String> getMethodNames(Object obj, boolean includeInheritedMethods) {
HashSet<String> methodNames = new HashSet<String>();
if (obj == null) return methodNames;
Method[] methods = null;
if (includeInheritedMethods) {
methods = obj.getClass().getMethods();
}
else {
methods = obj.getClass().getDeclaredMethods();
}
for (int i=0;i<methods.length;i++) {
methodNames.add(getMethodName(methods[i]));
}
return methodNames;
}
protected static String getMethodName(Method method) {
String methodName = method.getName();
if (methodName.startsWith("_")) methodName = methodName.substring(1);
return methodName;
}
/**
* @param allowContravariantConversionForParameters
* when false, parameters will have exactly the same class as the arguments to the returned method
* when true, parameters may have a type that is more specific than the arguments to the returned method
*/
public static Method getMethodFor(Object obj, String methodName, Object[] parameters, boolean includeInheritedMethods, boolean allowContravariantConversionForParameters){
if (obj == null)
return null;
Method instanceMethod = getInstanceMethodFor(obj, methodName, parameters, includeInheritedMethods, allowContravariantConversionForParameters);
if (instanceMethod != null)
return instanceMethod;
Method staticMethod = getStaticMethodFor(obj, methodName, parameters, allowContravariantConversionForParameters);
if (staticMethod != null)
return staticMethod;
return null;
}
private static Method getInstanceMethodFor(Object obj, String methodName, Object[] parameters, boolean includeInheritedMethods, boolean allowContravariantConversionForParameters) {
Method[] methods = null;
if (includeInheritedMethods) {
methods = obj.getClass().getMethods();
}
else {
methods = obj.getClass().getDeclaredMethods();
}
return searchMethodsFor(methods, methodName, parameters, allowContravariantConversionForParameters);
}
private static Method getStaticMethodFor(Object obj, String methodName, Object[] parameters, boolean allowContravariantConversionForParameters) {
Method staticMethod = null;
Class<?> javaClass = null;
if (obj instanceof EolNativeType) {
javaClass = ((EolNativeType) obj).getJavaClass();
}
if (obj instanceof Class) {
javaClass = (Class<?>) obj;
}
if (javaClass != null) {
staticMethod = searchMethodsFor(javaClass.getMethods(), methodName, parameters, allowContravariantConversionForParameters);
}
return staticMethod;
}
private static Method searchMethodsFor(Method[] methods, String methodName, Object[] parameters, boolean allowContravariantConversionForParameters) {
// Antonio: according to the Java Language Specification, Sections 15.12.2.2 to 15.12.2.4,
// method resolution is done in three stages: in the first one, no autoboxing is used. In
// the second one, autoboxing (like that in our isInstance static method) is used. In the
// third one, varargs are used. We should do the same if we want to tell apart remove(Object)
// from remove(int) like Java normally would.
for (int stage=0; stage < 2; ++stage) {
for (int i=0;i<methods.length;i++){
boolean namesMatch = false;
namesMatch = getMethodName(methods[i]).equalsIgnoreCase(methodName);
if (namesMatch){
Method method = methods[i];
Class<?>[] parameterTypes = method.getParameterTypes();
boolean parametersMatch = parameterTypes.length == parameters.length;
if (parametersMatch){
//TODO: See why parameter type checking does not work with EolSequence
for (int j=0;j<parameterTypes.length && parametersMatch; j++){
Class<?> parameterType = parameterTypes[j];
Object parameter = parameters[j];
if (allowContravariantConversionForParameters) {
parametersMatch = parametersMatch && (stage == 0 ? parameterType.isInstance(parameter) : isInstance(parameterType,parameter));
} else {
parametersMatch = parametersMatch && parameterType.equals(parameter.getClass());
}
}
if (parametersMatch){
return method;
}
}
}
}
}
return null;
}
public static Object executeMethod(Object obj, Method method, Object[] parameters, ModuleElement ast) throws EolRuntimeException{
try {
return executeMethod(method, obj, parameters);
} catch (Throwable t) {
throw new EolInternalException(t, ast);
}
}
public static Object executeMethod(Object obj, String methodName, Object[] parameters) throws Throwable {
Method method = getMethodFor(obj, methodName, parameters, true, true);
try {
if (!method.isAccessible()) {
method.setAccessible(true);
}
return method.invoke(obj, parameters);
}
catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
public static Object executeMethod(Method method, Object obj, Object[] parameters) throws Throwable {
try {
if (!method.isAccessible()) {
method.setAccessible(true);
}
return method.invoke(obj, parameters);
}
catch (InvocationTargetException iex) {
throw iex.getCause();
}
}
/**
* Returns a string representation
* of the method
* @param method
* @return
*/
public static String methodToString(Method method){
String str = getMethodName(method);
str += "(";
for (int i=0;i<method.getParameterTypes().length;i++){
Class<?> parameterType = method.getParameterTypes()[i];
str += parameterType.getName();
if (i < method.getParameterTypes().length - 1) {
str += " ,";
}
}
str += ")";
return str;
}
/**
* Returns the value of a field of an object
* @param object
* @param fieldName
* @return
*/
public static Object getFieldValue(Object object, String fieldName){
if (object == null) return null;
Field field = getField(object.getClass(), fieldName);
if (field == null) return null;
field.setAccessible(true);
try {
return field.get(object);
}
catch (Exception ex){
return null;
}
}
/**
* Gets a field of a class using reflection
* by introspecting the class and its supertype(s)
* @param clazz
* @param fieldName
* @return
*/
public static Field getField(Class<?> clazz, String fieldName){
Field[] fields = clazz.getDeclaredFields();
for (int i=0;i<fields.length;i++){
if (fields[i].getName().equals(fieldName))
return fields[i];
}
if (clazz.getSuperclass() != Object.class) return getField(clazz.getSuperclass(), fieldName);
return null;
}
/**
* Checks if the instance is an instance of clazz
* Necessary because in Java, int.class != Integer.class etc
* @param clazz
* @param instance
* @return
*/
public static boolean isInstance(Class<?> clazz, Object instance){
if (instance == null) return true;
else if (clazz == int.class) return Integer.class.isInstance(instance);
else if (clazz == float.class) return Float.class.isInstance(instance);
else if (clazz == double.class) return Double.class.isInstance(instance);
else if (clazz == boolean.class) return Boolean.class.isInstance(instance);
else if (clazz == long.class) return Long.class.isInstance(instance);
else if (clazz == char.class) return Character.class.isInstance(instance);
else return clazz.isInstance(instance);
}
public static List<Field> getAllInheritedInstanceFields(Class<?> klazz) {
final List<Field> fields = new ArrayList<Field>();
for (Field f : klazz.getDeclaredFields()) {
if (Modifier.isStatic(f.getModifiers())) {
continue;
}
fields.add(f);
}
if (klazz.getSuperclass() != null) {
fields.addAll(getAllInheritedInstanceFields(klazz.getSuperclass()));
}
return fields;
}
}