package context.arch.enactor;
import java.util.ArrayList;
import java.util.List;
import context.arch.discoverer.ComponentDescription;
import context.arch.discoverer.query.AbstractQueryItem;
import context.arch.service.helper.ServiceInput;
import context.arch.storage.Attribute;
import context.arch.storage.Attributes;
import context.arch.widget.Widget;
/**
* Fully describes a widget via a set of attributes, and a set of conditions on
* those attributes. Any attribute conditioned on should be represented in
* Attributes.
*
* TODO: enforce data correspondence, possible change over to collections.
*
* Added outcome field to specify what is the outcome when the rule is satisfied.
*
* @author alann
* @author Brian Y. Lim
*/
public class EnactorReference {
// /**
// * Even though the convention now (Jun 2010) is that each enactor has one descriptionQuery, and
// * all of its references have the same one,
// * in future, they may be able to support a different descriptionQuery for each enactor reference
// */
// protected AbstractQueryItem descriptionQuery;
protected AbstractQueryItem<?,?> conditionQuery; // TODO is this being saved by hibernate?
// TODO: also since we are now using 2 query items, the hibernate storage needs to be fixed
protected Enactor enactor;
protected String outcomeName;
protected String outcomeValue; // value
/**
* For interpreting actions in script form
*/
protected List<AttributeEvalParser<?>> attEvalParsers;
protected List<ServiceInput> serviceInputs;
protected boolean _break;
/**
*
* @param enactor
* @param conditionQuery
* @param outcomeValue
* @param serviceInput to bind to a service to automatically invoke when the conditionQuery returns true
*/
public EnactorReference(Enactor enactor, AbstractQueryItem<?,?> conditionQuery,
String outcomeValue,
List<AttributeEvalParser<?>> attEvalParsers,
List<ServiceInput> serviceInputs) {
super();
setEnactor(enactor);
this.conditionQuery = conditionQuery; // requiring during initialization ensures that this is unlikely to be null
this.outcomeValue = outcomeValue;
this.attEvalParsers = new ArrayList<AttributeEvalParser<?>>(attEvalParsers);
this.serviceInputs = new ArrayList<ServiceInput>(serviceInputs);
}
public EnactorReference(Enactor enactor, AbstractQueryItem<?,?> conditionQuery,
String outcomeValue,
List<AttributeEvalParser<?>> attEvalParsers,
List<ServiceInput> serviceInputs,
boolean _break) {
this(enactor, conditionQuery, outcomeValue, attEvalParsers, serviceInputs);
this._break = _break;
}
/**
* @param outcomeValue
* @param logicalRule
*/
public EnactorReference(Enactor enactor, AbstractQueryItem<?,?> conditionQuery, String outcomeValue) {
super();
setEnactor(enactor);
this.conditionQuery = conditionQuery; // requiring during initialization ensures that this is unlikely to be null
this.outcomeValue = outcomeValue;
this.attEvalParsers = new ArrayList<AttributeEvalParser<?>>();
this.serviceInputs = new ArrayList<ServiceInput>();
}
public EnactorReference(Enactor enactor, AbstractQueryItem<?,?> conditionQuery, String outcomeValue, boolean _break) {
this(enactor, conditionQuery, outcomeValue);
this._break = _break;
}
public void addServiceInput(ServiceInput serviceInput) {
serviceInputs.add(serviceInput);
}
public void setEnactor(Enactor r) {
enactor = r;
}
public Enactor getEnactor() {
return enactor;
}
public AbstractQueryItem<?,?> getConditionQuery() {
return conditionQuery;
}
public void setConditionQuery(AbstractQueryItem<?,?> conditionQuery) {
this.conditionQuery = conditionQuery;
}
public void setOutcomeName(String outcomeName) {
this.outcomeName = outcomeName;
}
public String getOutcomeName() {
return outcomeName;
}
public void setOutcomeValue(String outcomeValue) {
this.outcomeValue = outcomeValue;
}
public String getOutcomeValue() {
return outcomeValue;
}
/**
* This method is called when a new batch of state data concerning a widget
* should be evaluated by this EnactorReference.
* Called to notify listeners.
*
* @param widgetSubId the unique identifier for the widget subscription
* @param widgetState the current state of the widget
*/
public void evaluateComponent(EnactorComponentInfo eci) {
// check if timestamp has been set yet
// if not, then it is likely that the widget had never been set up yet, though it
// Enactor.getAtt(Widget.TIMESTAMP, eci.getCurrentState().getAllAttributes());
// add outcome attribute to the current state
ComponentDescription widgetState = eci.getCurrentState();
// String outcomeName = enactor.getOutcomeName();
// update the input state and outcome value of the enactor
enactor.setOutcomeValue(outcomeValue);
enactor.setInWidgetState(widgetState);
// save this enactor reference as the last activated one
enactor.setLastSatisfied(this.getConditionQuery(), widgetState);
// subclass can do whatever
conditionSatisfied(eci);
// notify listeners after reacting
enactor.fireComponentEvaluated(eci);
}
// public abstract void conditionSatisfied(EnactorComponentInfo eci);
/**
* Does some default tasks that are common for Enactors.
* Namely extract the event timestamp and save it into the data widget.
* @param eci sent from changes in the input Widget
*/
public void conditionSatisfied(EnactorComponentInfo eci) {
// get state of widget
ComponentDescription inWidgetState = eci.getCurrentState();
// System.out.println(this.getClass().getSimpleName() + ".conditionSatisfied query = " + this.conditionQuery);
// System.out.println(this.getClass().getSimpleName() + ".conditionSatisfied outcome = " + this.outcomeValue);
// System.out.println(this.getClass().getSimpleName() + ".conditionSatisfied inWidgetState = " + inWidgetState);
/*
* Evaluate assignment scripts
*/
Attributes outAtts = new Attributes();
// get time of the source, so that we can trace back by ID and timestamp
Long timestamp = inWidgetState.getAttributeValue(Widget.TIMESTAMP);
timestamp = timestamp != null ? timestamp : System.currentTimeMillis();
outAtts.addAttribute(Widget.TIMESTAMP, timestamp);
for (AttributeEvalParser<?> assnParser : attEvalParsers) {
outAtts.addAttribute(
assnParser.getAttributeName(),
assnParser.getAttributeValue(inWidgetState));
}
// legacy method that uses outcomeName and outcomeValue
if (outcomeName != null && outcomeValue != null) {
outAtts.addAttribute(outcomeName, outcomeValue);
}
// subclass enactor may override attribute values
outAtts = conditionSatisfied(enactor.getInWidgetState(), outAtts);
// send request to update Out Widget with new data
enactor.updateOutWidget(outAtts);
/*
* Send request to execute service
*/
for (ServiceInput serviceInput : serviceInputs) {
// set attribute values
Attributes inputAtts = serviceInput.getInput();
for (Attribute<?> att : serviceInput.getInput().values()) {
String attName = att.getName();
// copy value from WidgetData into input attribute
//((AttributeNameValue<?>) att).copyValue((AttributeNameValue<?>) outAtts.get(attName)); // this fails if each of serviceInput.getInput() originally set as Attribute instead of AttributeNameValue
inputAtts.add(outAtts.get(attName));
}
for(ComponentDescription cd:enactor.widgetComponentDescriptions[Enactor.OUT_WIDGET_INDEX])
enactor.executeWidgetService(cd,serviceInput);
}
}
/**
* Subclasses implement this to help build and return a data entity.
* Other tasks can also be done, such as generating explanations.
* @param inWidgetState
* @param outAtts out-widget attributes with some attributes already set (e.g. timestamp)
* @param originalTimestamp
* @return
*/
protected Attributes conditionSatisfied(ComponentDescription inWidgetState, Attributes outAtts) {
return outAtts;
}
/**
* Called whenever a widget satisfies the EnactorReference
* descriptionQuery. A call to this method is advance notice that
* evaluateWidget will be called with this widgetSubId. A EnactorReference
* may override this method to provide more custom parameter Attributes to be
* set up by the Enactor, if unsure just leave the third arg null.
*
* @param widgetSubId
*/
public void componentAdded(EnactorComponentInfo eci) {
//TODO: warn if eci.getReference() != this
enactor.fireComponentAdded(eci, null);
}
/**
* Called whenever a widget no longer satisfies the descriptionQuery.
*
* @param widgetSubId
*/
public void componentRemoved(EnactorComponentInfo eci) {
//TODO: warn if eci.getReference() != this
enactor.fireComponentRemoved(eci, null);
}
}