fr.boreal.model.functions.JavaMethodInvoker Maven / Gradle / Ivy
The newest version!
package fr.boreal.model.functions;
import fr.boreal.model.logicalElements.api.Literal;
import fr.boreal.model.logicalElements.api.Term;
import fr.boreal.model.logicalElements.factory.api.TermFactory;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Optional;
import java.util.stream.Stream;
/**
* Invoker implementation using java methods as black boxes
* @author Florent Tornil
*
*/
public class JavaMethodInvoker implements Invoker {
private final Method method;
private final int method_arg_count;
private final Class> last_arg_type;
private final TermFactory termFactory;
/////////////////////////////////////////////////
// Constructors
/////////////////////////////////////////////////
/**
* @param termFactory a term factory
* @param class_file_path path of the .class file
* @param class_full_name name of the .class file
* @param method_name name of the method to call
* @throws ClassNotFoundException iff the given class is not found
* @throws IOException iff the given class file cannot be accessed
* @throws NoSuchMethodException iff the given method is not found in the given class
*/
public JavaMethodInvoker(TermFactory termFactory, String class_file_path, String class_full_name,
String method_name) throws ClassNotFoundException, IOException, NoSuchMethodException {
this.termFactory = termFactory;
File class_file = new File(class_file_path);
URL url = class_file.toURI().toURL();
URLClassLoader loader = new URLClassLoader(new URL[] { url });
Class> invoked_class = loader.loadClass(class_full_name);
loader.close();
Optional opt_m = Stream.of(invoked_class.getMethods()).filter(m -> m.getName().equals(method_name))
.findAny();
if (opt_m.isPresent()) {
this.method = opt_m.get();
this.method_arg_count = method.getParameterCount();
this.last_arg_type = this.method.getParameterTypes()[this.method_arg_count - 1];
} else {
throw new NoSuchMethodException("Method " + method_name + " does not exist for class " + class_full_name);
}
}
/////////////////////////////////////////////////
// Public methods
/////////////////////////////////////////////////
@Override
public Optional invoke(Term... terms) {
Object[] args = new Object[terms.length];
for (int i = 0; i < terms.length; ++i) {
if (terms[i] instanceof Literal>) {
args[i] = ((Literal>)terms[i]).value();
} else {
args[i] = terms[i].label();
}
}
// If the last argument of the method is a variadic parameter,
// we need to group all the remaining arguments into a single array.
// The resulting array will be of type Object[],
// ensuring that the method can handle the arguments correctly.
if (last_arg_type.isArray()) {
Object[] final_args = Arrays.copyOf(args, this.method_arg_count);
final_args[this.method_arg_count - 1] = Arrays.copyOfRange(
args, this.method_arg_count - 1, args.length);
args = final_args;
}
try {
Object result = this.method.invoke(null, args);
return Optional.ofNullable(termFactory.createOrGetLiteral((result.getClass().cast(result))));
} catch (IllegalAccessException e) {
throw new IllegalStateException(String.format("Failed to access method %s.", method.getName()), e);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException(String.format("Method %s was called with incorrect arguments: %s.",
method.getName(), Arrays.toString(args)), e);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
throw new RuntimeException(String.format("Method %s threw an exception during execution.",
method.getName()), cause);
}
}
}