package interaction;
import StevensLevel.HasInteractionReactor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import static java.util.concurrent.Executors.*;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* @author Tristan Goffman(tgoffman@gmail.com) Sep 8, 2011
*/
public class InteractionReactorReflection {
private static ExecutorService serv; //Since interaction can go off causing chaing reactions
static {
resetExecutor();
}
public static boolean reactAll(Set<InteractionReactor> blacklist, List<InteractionReactor> reactors, ExperimentInteraction e) {
return reactAll(blacklist, reactors, e, null);
}
/**
*NOTE: This method should never be used, shuts down the thread pool. Causing all work to be completed first
*/
public static void finishWork() {
serv.shutdown();
}
public static void resetExecutor() {
serv = newFixedThreadPool(10);
}
public static boolean reactAll(Set<InteractionReactor> blacklist, List<InteractionReactor> reactors, ExperimentInteraction e, Object payload) {
HashSet bools = new HashSet<Boolean>();
if (blacklist != null) {
reactors.removeAll(blacklist); //Ensure original 'caller' is not in subsequent calls to avoid infinite recursion
}
if (reactors != null) {
for (InteractionReactor interactionReactor : reactors) {
blacklist.add(interactionReactor);
bools.add(reactSingle(blacklist, interactionReactor, e, payload));
}
}
return bools.contains(true);
}
/**
* Given an object and an ExperimentInteraction (and payload) check the obj to see whether it can respond to such events and then invoke the method
* that deals with such methods if it is available. The method should be able to tell what to do with the payload given.
* @param blacklist, set of objects that have already been invoked upon or tried, usually includes the original calling object (to stop infinite loops)
* @param obj, Object to have method invoked upon it with the ExperiementInteraction, and payload
* @param e, the ExperimenInteraction which occurred
* @param payload, some object containing additional data corresponding to the ExperimentInteraction
* @return
*/
public static boolean reactSingle(Set<InteractionReactor> blacklist, final InteractionReactor obj, final ExperimentInteraction e, final Object payload) {
boolean hadAtleastOneMethod = false;
if (HasInteractionReactor.class.isAssignableFrom(obj.getClass()) && ((HasInteractionReactor) obj).hasInteractionReactor()) { //Walk down InteractionReactor hierarchy (hopefully quite shallow) Caution: Should think about those reactions that might be triggered from a reaction
hadAtleastOneMethod = reactAll(blacklist, ((HasInteractionReactor) obj).getInteractionReactors(), e, payload);
}
if (!hadAtleastOneMethod) { //If lower objects didn't respond to the ExperimentInteraction class in question
List<Method> methods = retrieveReactTos(obj.getClass(), e.getClass());
for (final Method method : methods) {
Runnable worker = new Runnable() {
@Override
public void run() {
try {
method.invoke(obj, e, payload);
} catch (Exception ex) {
Logger.getLogger(InteractionReactorReflection.class.getName()).log(Level.SEVERE, null, ex);
}
}
};
serv.submit(worker); //execute doesn't work, but submit certainly does
//If the annotatoin has the specic class that this is Enum is set with then invoke the method with the supplied arguments
hadAtleastOneMethod = true;
}
}
return hadAtleastOneMethod;
}
/**
* Helper testing whether the Method given is of correct type to be used for 'reaction' inflection.
* @param meth, the method to be tested
* @param enumClass, the type of ExperimentInteraction extending class which is expected by a Method wishing to react
*/
private static boolean isValidReactToMethod(Method meth, Class<? extends ExperimentInteraction> enumClass) {
ReactTo anno = meth.getAnnotation(ReactTo.class);
Class<?>[] par_types = meth.getParameterTypes();
boolean has_two_pars = par_types.length == 2;
return anno != null && anno.value() == enumClass && has_two_pars && Enum.class.isAssignableFrom(par_types[0]) && par_types[1] == Object.class;
}
/**
* Given class of an InteractionReactor and a specific ExperimentInteraction class, retrieve all methods that would be invoked by a 'sendReaction' call using an object
* of this InterationReactor using the ExperimentInteraction given.
* @param reactor
* @param intClass
* @return
*/
public static List<Method> retrieveReactTos(Class<? extends InteractionReactor> reactor, Class<? extends ExperimentInteraction> intClass) {
List<Method> meths = Arrays.asList(reactor.getMethods());
List<Method> keptMeths = new ArrayList<Method>();
for (Method method : meths) {
if (isValidReactToMethod(method, intClass)) {
keptMeths.add(method);
}
}
return keptMeths;
}
}