package jadex.rules.rulesystem.rete.nodes; import jadex.rules.rulesystem.AbstractAgenda; import jadex.rules.rulesystem.Activation; import jadex.rules.rulesystem.IRule; import jadex.rules.rulesystem.IVariableAssignments; import jadex.rules.rulesystem.rete.Tuple; import jadex.rules.rulesystem.rete.extractors.AttributeSet; import jadex.rules.rulesystem.rete.extractors.IValueExtractor; import jadex.rules.state.IOAVState; import jadex.rules.state.IProfiler; import jadex.rules.state.OAVAttributeType; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * A terminal node stores the full matches and notifies * the agenda of the activated conditions. */ public class TerminalNode extends AbstractNode implements ITupleConsumerNode { //-------- attributes -------- /** The tuple source. */ protected ITupleSourceNode tsource; /** A mapping for fetching variable values (variable -> extractor). */ protected Map extractors; /** The rule of the terminal node. */ protected IRule rule; /** The set of relevant attributes. */ protected AttributeSet relevants; /** The set of indirect attributes. */ protected AttributeSet indirects; //-------- constructors -------- /** * Create a new node. */ public TerminalNode(int nodeid, IRule rule, Map extractors) { super(nodeid); this.rule = rule; this.extractors = extractors; } //-------- tuple consumer interface -------- /** * Send an tuple to this node. * @param tuple The tuple. */ public void addTuple(Tuple tuple, IOAVState state, ReteMemory mem, AbstractAgenda agenda) { // System.out.println("Add Tuple: "+tuple); state.getProfiler().start(IProfiler.TYPE_NODE, this); state.getProfiler().start(IProfiler.TYPE_NODEEVENT, IProfiler.NODEEVENT_TUPLEADDED); // Store variable assignments. Map nodemem = (Map)mem.getNodeMemory(this); Map vars = new HashMap(); for(Iterator it=extractors.keySet().iterator(); it.hasNext(); ) { Object variable = it.next(); vars.put(variable, ((IValueExtractor)extractors.get(variable)).getValue(tuple, null, null, state)); } ReteVariableAssignments assignments = new ReteVariableAssignments(vars, rule); nodemem.put(tuple, assignments); // Create activation for tuple. // Activation act = new Activation(rule, new ReteVariableAssignments(state, tuple, extractors)); Activation act = new Activation(rule, assignments, state); agenda.addActivation(act); state.getProfiler().stop(IProfiler.TYPE_NODEEVENT, IProfiler.NODEEVENT_TUPLEADDED); state.getProfiler().stop(IProfiler.TYPE_NODE, this); } /** * Remove a tuple from this node. * @param tuple The tuple. */ public void removeTuple(Tuple tuple, IOAVState state, ReteMemory mem, AbstractAgenda agenda) { // System.out.println("Remove Tuple: "+tuple); state.getProfiler().start(IProfiler.TYPE_NODE, this); state.getProfiler().start(IProfiler.TYPE_NODEEVENT, IProfiler.NODEEVENT_TUPLEREMOVED); // Get old assignments. Map nodemem = (Map)mem.getNodeMemory(this); ReteVariableAssignments assignments = (ReteVariableAssignments)nodemem.remove(tuple); // Activation act = new Activation(rule, new ReteVariableAssignments(state, tuple, extractors)); Activation act = new Activation(rule, assignments, state); agenda.removeActivation(act); state.getProfiler().stop(IProfiler.TYPE_NODEEVENT, IProfiler.NODEEVENT_TUPLEREMOVED); state.getProfiler().stop(IProfiler.TYPE_NODE, this); } /** * Modify a tuple in this node. * @param tuple The tuple. */ public void modifyTuple(Tuple tuple, int tupleindex, OAVAttributeType type, Object oldvalue, Object newvalue, IOAVState state, ReteMemory mem, AbstractAgenda agenda) { state.getProfiler().start(IProfiler.TYPE_NODE, this); state.getProfiler().start(IProfiler.TYPE_NODEEVENT, IProfiler.NODEEVENT_TUPLEMODIFIED); // Get old assignments. Map nodemem = (Map)mem.getNodeMemory(this); ReteVariableAssignments oldass = (ReteVariableAssignments)nodemem.get(tuple); // Calculate new variable assignments. Map newvars = new HashMap(); for(Iterator it=extractors.keySet().iterator(); it.hasNext(); ) { Object variable = it.next(); newvars.put(variable, ((IValueExtractor)extractors.get(variable)).getValue(tuple, null, null, state)); } // Change activation, if necessary. if(!oldass.assignments.equals(newvars)) { ReteVariableAssignments newass = new ReteVariableAssignments(newvars, rule); nodemem.put(tuple, newass); // System.out.println("Modify triggered: rule="+rule.getName()+" tuple="+tuple+", index="+tupleindex // +", attribute="+type+" oldvalue="+oldvalue+", newvalue="+newvalue); agenda.removeActivation(new Activation(rule, oldass, state)); agenda.addActivation(new Activation(rule, newass, state)); } state.getProfiler().stop(IProfiler.TYPE_NODEEVENT, IProfiler.NODEEVENT_TUPLEMODIFIED); state.getProfiler().stop(IProfiler.TYPE_NODE, this); } /** * Propagate an indirect object change to this node. * @param object The changed object. */ public void modifyIndirectObject(Object object, OAVAttributeType type, Object oldvalue, Object newvalue, IOAVState state, ReteMemory mem, AbstractAgenda agenda) { // Recheck all tuples Collection input = getTupleSource().getNodeMemory(mem); if(input!=null) { for(Iterator it=input.iterator(); it.hasNext(); ) { modifyTuple((Tuple)it.next(), -1, null, null, null, state, mem, agenda); } } } /** * Set the tuple source of this node. * @param node The tuple source node. */ public void setTupleSource(ITupleSourceNode node) { this.tsource = node; } /** * Get the tuple source of this node. * @return The object source node. */ public ITupleSourceNode getTupleSource() { return tsource; } //-------- methods -------- /** * Create the node memory. * @param state The state. * @return The node memory. */ public Object createNodeMemory(IOAVState state) { // Memory stores old variable assignments: // Map(tuple -> Map(variable -> value)) return new HashMap(); } /** * Get the memory for this node. * @return The memory. */ public Collection getNodeMemory(ReteMemory mem) { // Hack??? // try { return getTupleSource().getNodeMemory(mem); } // catch(Exception e) // { // Object o = getTupleSource().getNodeMemory(mem); // return null; // } } /** * Get the set of relevant attribute types. */ public AttributeSet getRelevantAttributes() { if(relevants==null) { synchronized(this) { if(relevants==null) { if(extractors.isEmpty()) { relevants = AttributeSet.EMPTY_ATTRIBUTESET; } else { relevants = new AttributeSet(); for(Iterator it=extractors.values().iterator(); it.hasNext(); ) { IValueExtractor ex = (IValueExtractor)it.next(); relevants.addAll(ex.getRelevantAttributes()); } } } } } return relevants; } /** * Get the set of indirect attribute types. * I.e. attributes of objects, which are not part of an object conditions * (e.g. for chained extractors) * @return The relevant attribute types. */ public AttributeSet getIndirectAttributes() { if(indirects==null) { synchronized(this) { if(indirects==null) { if(extractors.isEmpty()) { indirects = AttributeSet.EMPTY_ATTRIBUTESET; } else { indirects = new AttributeSet(); for(Iterator it=extractors.values().iterator(); it.hasNext(); ) { IValueExtractor ex = (IValueExtractor)it.next(); indirects.addAll(ex.getIndirectAttributes()); } } } } } return indirects; } /** * Get the rule. * @return The rule. */ public IRule getRule() { return rule; } //-------- cloneable -------- /** * Do clone makes a deep clone without regarding cycles. * Method is overridden by subclasses to actually incorporate their attributes. * @param theclone The clone. */ protected void doClone(Object theclone) { TerminalNode ret = (TerminalNode)theclone; // Source node is set from creating node ret.tsource = (ITupleSourceNode)tsource.clone(); // Extractors shallow copy ret.extractors = (Map)((HashMap)extractors).clone(); // Rule keeps the same } //-------- helpers -------- /** * The rete variable assignment help extracting values for varaibles. */ public static class ReteVariableAssignments implements IVariableAssignments { //-------- attributes -------- // /** The state. */ // protected IOAVState state; // // /** The tuple. */ // protected Tuple tuple; // // /** The extractors. */ // protected Map extractors; /** The map with assignments. */ protected Map assignments; /** The cached hashcode as multi-slots could change and would prevent lookup. */ protected int hashcode; //-------- constructors -------- /** * Create a new variable assignments. */ // public ReteVariableAssignments(IOAVState state, Tuple tuple, Map extractors) public ReteVariableAssignments(Map assignments, IRule rule) { // this.state = state; // this.tuple = tuple; // this.extractors = extractors; this.assignments = assignments; this.hashcode = 31 * assignments.hashCode(); for(Iterator it=assignments.keySet().iterator(); it.hasNext(); ) { // Todo: check if variable is used in action to avoid unnecessary cloning? Object key = it.next(); Object val = assignments.get(key); if(val instanceof Map) { Map newval = new HashMap(); newval.putAll((Map)val); assignments.put(key, newval); // System.out.println("replacing "+rule.getName()+": "+newval); } else if(val instanceof Set) { Set newval = new HashSet(); newval.addAll((Set)val); assignments.put(key, newval); // System.out.println("replacing "+rule.getName()+": "+newval); } else if(val instanceof List) { List newval = new ArrayList(); newval.addAll((List)val); assignments.put(key, newval); // System.out.println("replacing "+rule.getName()+": "+newval); } } } //-------- constructors -------- /** * Get a variable values. * @param var The variable name. */ public Object getVariableValue(String var) { if(!assignments.containsKey(var)) throw new RuntimeException("Variable not found: "+var); // IValueExtractor ex = (IValueExtractor)extractors.get(var); // return ex.getValue(tuple, null, state); return assignments.get(var); } /** * Get the variable names. * @return All variable names. */ public String[] getVariableNames() { // return (String[])extractors.keySet().toArray(new String[extractors.keySet().size()]); return (String[])assignments.keySet().toArray(new String[assignments.keySet().size()]); } //-------- methods -------- /** * Get the hashcode of this object. * @return The hashcode. */ public int hashCode() { // int result = 31 * state.hashCode(); // result = 31 * result + tuple.hashCode(); // result = 31 * result + extractors.hashCode(); // return result; return hashcode; } /** * Test if an object equals this. * @param obj The object. */ public boolean equals(Object obj) { boolean ret = this==obj; if(!ret && obj instanceof ReteVariableAssignments) { ReteVariableAssignments va = (ReteVariableAssignments)obj; // ret = va.state == this.state && va.tuple.equals(this.tuple) // && va.extractors.equals(this.extractors); ret = va.assignments.equals(this.assignments); } return ret; } /** * Get the string representation. * @return The string representation. */ public String toString() { // return ""+tuple.getObjects(); return "VariableAssignment"+assignments; } } }