/**
* Copyright (c) 2010 committers of YAKINDU and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* committers of YAKINDU - initial API and implementation
*/
package org.yakindu.sct.simulation.core.sexec.interpreter;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.eclipse.emf.ecore.EObject;
/**
*
* @author axel terfloth
* @author andreas muelder
*
*/
public class Function {
/**
* Indicates that a method is a function.
*
* @author terfloth@itemis.de
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface FunctionMethod {
String value();
}
/**
* Sorts a {@link Collection} of {@link Method} . The most specialized
* parameter types are put first.
*
* @author muelder
*
*/
public static class PolymorphicComparator implements Comparator<Method> {
public int compare(Method method1, Method method2) {
Class<?>[] parameterTypes1 = method1.getParameterTypes();
Class<?>[] parameterTypes2 = method2.getParameterTypes();
if (parameterTypes1.length > parameterTypes2.length) {
return -1;
}
if (parameterTypes1.length < parameterTypes2.length) {
return 1;
}
for (int i = 0; i < parameterTypes1.length; i++) {
final Class<?> class1 = parameterTypes1[i];
final Class<?> class2 = parameterTypes2[i];
if (class1.equals(class2))
continue;
if (class1.isAssignableFrom(class2) || Void.class.equals(class2)) {
return 1;
}
if (class2.isAssignableFrom(class1) || Void.class.equals(class1)) {
return -1;
}
}
return 0;
}
}
protected Method functionMethod;
/**
* Looks up the appropriate function for the given parameter types.
*
* @param name
* @param paramTypes
* @return
*/
public Function lookup(Class<?> functionClass, String name, Class<?>... paramTypes) {
List<Method> functionMethods = new ArrayList<Method>();
addFunctionMethods(functionClass, functionMethods);
Collections.sort(functionMethods, new PolymorphicComparator());
for (Method fMethod : functionMethods) {
FunctionMethod fAnno = fMethod.getAnnotation(FunctionMethod.class);
if ((name.equals(fMethod.getName())) || name.equals(fAnno.value())) {
if (isCallable(paramTypes, fMethod.getParameterTypes())) {
return createFunction(functionClass, fMethod);
}
}
}
return null;
}
private void addFunctionMethods(Class<?> functionClass, List<Method> methodList) {
List<Method> result = new ArrayList<Method>();
Method[] methods = functionClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
Method fMethod = methods[i];
FunctionMethod fAnno = fMethod.getAnnotation(FunctionMethod.class);
if (fAnno != null) {
result.add(fMethod);
}
}
methodList.addAll(result);
if (functionClass.getSuperclass() != null) {
addFunctionMethods(functionClass.getSuperclass(), methodList);
}
}
private static boolean isCallable(Class<?>[] paramTypes, Class<?>[] parameterTypes) {
if (paramTypes.length != parameterTypes.length)
return false;
for (int i = 0; i < paramTypes.length; i++) {
Class<?> class1 = paramTypes[i];
Class<?> class2 = parameterTypes[i];
if (!class2.isAssignableFrom(class1))
return false;
}
return true;
}
public Function lookup(Class<?> functionClass, String name, Object... params) {
Class<?>[] paramTypes = toParamTypes(params);
return lookup(functionClass, name, paramTypes);
}
protected Class<?>[] toParamTypes(Object... params) {
Class<?>[] paramTypes = new Class<?>[params.length];
for (int i = 0; i < params.length; i++) {
if (params[i] == null)
paramTypes[i] = Object.class;
else if (params[i] instanceof EObject) {
paramTypes[i] = EObject.class; // enumerators and complex types
} else {
paramTypes[i] = params[i].getClass(); // primitive values (boxed
// java types)
}
}
return paramTypes;
}
protected static Function createFunction(Class<?> functionClass, Method functionMethod) {
if (functionClass == null || functionMethod == null)
return null;
try {
Constructor<?> constr;
try {
constr = (Constructor<?>) functionClass.getConstructor(new Class<?>[0]);
} catch (NoSuchMethodException e) {
throw new RuntimeException("Missing default constructor in class " + functionClass.getName()
+ " while loading function " + functionMethod.getName() + " !");
}
Function func = (Function) constr.newInstance(new Object[0]);
func.setFunctionMethod(functionMethod);
return func;
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException("Error loading function " + functionMethod.getName() + " from function class "
+ functionClass.getName() + " !", e);
}
}
public Function() {
super();
}
public Method getFunctionMethod() {
return functionMethod;
}
public void setFunctionMethod(Method functionMethod) {
this.functionMethod = functionMethod;
}
public Object execute(Object[] params) {
try {
return getFunctionMethod().invoke(this, params);
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e.getCause());
}
}
}