package eis.eis2java.handlers;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import eis.eis2java.annotation.AsPercept;
import eis.eis2java.exception.TranslationException;
import eis.eis2java.translation.Filter;
import eis.eis2java.translation.Translator;
import eis.exceptions.PerceiveException;
import eis.iilang.Function;
import eis.iilang.Parameter;
import eis.iilang.ParameterList;
import eis.iilang.Percept;
/**
* Abstract handler for percepts. Provides access to the entity, the translation
* and unpacking phases of EIS2java.
*
* @author mpkorstanje
*
*/
public abstract class AbstractPerceptHandler extends PerceptHandler {
/**
* The entity associated with this handler.
*/
protected final Object entity;
public AbstractPerceptHandler(Object entity) {
assert entity != null;
this.entity = entity;
}
/**
* map of previous percepts that we got for each method.
*/
protected final Map<Method, List<Object>> previousPercepts = new HashMap<Method, List<Object>>();
/**
* Translates the percept objects and applies filtering as described by
* {@link AsPercept#filter()}.
*
* @param entity
* the entity which produced the percepts
* @param method
* the method which produced the percepts
* @param perceptObjects
* the java percept objects that need to be translated into
* {@link Percept}s. Each object is converted into one percept.
* @return list of {@link Percept} objects.
* @throws PerceiveException
*/
protected final List<Percept> translatePercepts(Method method,
List<Object> perceptObjects) throws PerceiveException {
// the add and delete list based on the perceived objects
// list of object that we had last round but not at this moment.
List<Object> addList = new ArrayList<Object>();
List<Object> delList = new ArrayList<Object>();
AsPercept annotation = method.getAnnotation(AsPercept.class);
Filter.Type filter = annotation.filter();
String perceptName = annotation.name();
List<Object> previous = previousPercepts.get(method);
// Avoid translating objects that don't need to be translated.
if (filter == Filter.Type.ONCE && previous != null) {
return new ArrayList<Percept>();
}
if (previous == null) {
previous = new LinkedList<Object>();
previousPercepts.put(method, previous);
}
// do the proper filtering.
switch (filter) {
case ALWAYS:
case ONCE:
addList = perceptObjects;
break;
case ON_CHANGE:
if (!perceptObjects.equals(previous)) {
addList = perceptObjects;
}
break;
case ON_CHANGE_NEG:
addList.addAll(perceptObjects);
if (previous != null) {
addList.removeAll(previous);
delList.addAll(previous);
delList.removeAll(perceptObjects);
}
break;
}
// Translate addList.
List<Percept> percepts = new ArrayList<Percept>();
for (Object javaObject : addList) {
Parameter[] parameters;
try {
parameters = Translator.getInstance().translate2Parameter(
javaObject);
if (annotation.multipleArguments()) {
parameters = extractMultipleParameters(parameters);
}
} catch (TranslationException e) {
throw new PerceiveException("Unable to translate percept "
+ perceptName, e);
}
percepts.add(new Percept(perceptName, parameters));
}
// Translate delList.
for (Object javaObject : delList) {
Parameter[] parameters;
try {
parameters = Translator.getInstance().translate2Parameter(
javaObject);
if (annotation.multipleArguments()) {
parameters = extractMultipleParameters(parameters);
}
} catch (TranslationException e) {
throw new PerceiveException("Unable to translate percept "
+ perceptName, e);
}
percepts.add(new Percept("not", new Function(perceptName,
parameters)));
}
previousPercepts.put(method, perceptObjects);
return percepts;
}
/**
* Handle multiple arguments. The parameter list must be a list containing
* just one {@link ParameterList}. Returns a list with all elements in that
* {@link ParameterList}.
*
* @param parameters
* @return
* @throws PerceiveException
* if parameters is not the right format.
*/
private Parameter[] extractMultipleParameters(Parameter[] parameters)
throws PerceiveException {
if (parameters.length == 1 && parameters[0] instanceof ParameterList) {
// special case where the top set is the set of arguments
// for function
ParameterList params = (ParameterList) parameters[0];
parameters = new Parameter[params.size()];
for (int i = 0; i < params.size(); i++) {
parameters[i] = params.get(i);
}
} else {
throw new PerceiveException(
"multipleArguments parameter is set and therefore expecting a set but got "
+ parameters);
}
return parameters;
}
/**
* Depending on {@link AsPercept#multiplePercepts()} a perceptObject is
* either a collection that contains multiple percept objects or a single
* percept. This method unpacks either case into a list of percept objects.
* The {@link AsPercept#multipleArguments()} aspect is not handled here.
*
* @param method
* that generated the percept object
* @param perceptObject
* the perceptObject generated by the method.
* @return a unpacked version of the percept object. A null perceptObject is
* translated into an empty list.
* @throws PerceiveException
*/
protected final List<Object> unpackPerceptObject(Method method,
Object perceptObject) throws PerceiveException {
AsPercept annotation = method.getAnnotation(AsPercept.class);
String perceptName = annotation.name();
if (!annotation.multiplePercepts()) {
// This is percept does not provide multiples.
List<Object> unpacked = new ArrayList<Object>(1);
if (perceptObject != null) {
unpacked.add(perceptObject);
}
return unpacked;
}
// The method claims to provide multiple but doesn't provide a
// collection.
if (!(perceptObject instanceof Collection<?>)) {
throw new PerceiveException("Unable to perceive " + perceptName
+ " because a collection was expected but a "
+ perceptObject.getClass() + " was returned instead");
}
// The multiple percepts are a collection, put them in a list.
Collection<?> javaCollection = (Collection<?>) perceptObject;
ArrayList<Object> unpacked = new ArrayList<Object>(javaCollection);
return unpacked;
}
@Override
public void reset() {
previousPercepts.clear();
}
}