package jadex.rules.rulesystem.rete.nodes; import jadex.commons.SUtil; import jadex.rules.rulesystem.AbstractAgenda; import jadex.rules.rulesystem.rete.constraints.IConstraintEvaluator; import jadex.rules.rulesystem.rete.extractors.AttributeSet; import jadex.rules.state.IOAVState; import jadex.rules.state.IProfiler; import jadex.rules.state.OAVAttributeType; import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Set; /** * An alpha node is a 1-input -> 1-output node which * propagates objects matching its constraints. */ public class AlphaNode extends AbstractNode implements IObjectConsumerNode, IObjectSourceNode { //-------- attributes -------- /** The object source. */ protected IObjectSourceNode osource; /** The object consumers. */ protected IObjectConsumerNode[] oconsumers; /** The constraint evaluator. */ protected IConstraintEvaluator[] evaluators; /** The set of relevant attributes. */ protected AttributeSet relevants; /** The set of indirect attributes. */ protected AttributeSet indirects; //-------- constructors -------- /** * Create a new node. * @param evaluators The evaluators. */ public AlphaNode(int nodeid, IConstraintEvaluator[] evaluators) { super(nodeid); this.evaluators = evaluators; } //-------- object consumer interface -------- /** * Send a new object to this node. * @param object The object. */ public void addObject(Object object, IOAVState state, ReteMemory mem, AbstractAgenda agenda) { //System.out.println("Add object called: "+this+" "+object); // if(object.getClass().toString().indexOf("Order")!=-1) // System.out.println("here: "+object); // if(state.getType(object).getName().indexOf("goal")!=-1) // System.out.println("here: "+object); state.getProfiler().start(IProfiler.TYPE_NODE, this); state.getProfiler().start(IProfiler.TYPE_NODEEVENT, IProfiler.NODEEVENT_OBJECTADDED); assert !mem.hasNodeMemory(this) || !((Collection)mem.getNodeMemory(this)).contains(object) : "New objects shouldn't be contained."; if(checkConstraints(object, state)) { ((Collection)mem.getNodeMemory(this)).add(object); //System.out.println("Object passed constraint check: "+this+" "+object); propagateAdditionToObjectConsumers(object, state, mem, agenda); } state.getProfiler().stop(IProfiler.TYPE_NODEEVENT, IProfiler.NODEEVENT_OBJECTADDED); state.getProfiler().stop(IProfiler.TYPE_NODE, this); } /** * Send a removed object to this node. * @param object The object. */ public void removeObject(Object object, IOAVState state, ReteMemory mem, AbstractAgenda agenda) { //System.out.println("Remove object called: "+this+" "+object); state.getProfiler().start(IProfiler.TYPE_NODE, this); state.getProfiler().start(IProfiler.TYPE_NODEEVENT, IProfiler.NODEEVENT_OBJECTREMOVED); if(mem.hasNodeMemory(this) && ((Collection)mem.getNodeMemory(this)).remove(object)) { //System.out.println("Object passed constraint check: "+this+" "+object); propagateRemovalToObjectConsumers(object, state, mem, agenda); } state.getProfiler().stop(IProfiler.TYPE_NODEEVENT, IProfiler.NODEEVENT_OBJECTREMOVED); state.getProfiler().stop(IProfiler.TYPE_NODE, this); } /** * Propagate an object change to this node. * @param object The new object. */ public void modifyObject(Object object, OAVAttributeType type, Object oldvalue, Object newvalue, IOAVState state, ReteMemory mem, AbstractAgenda agenda) { //System.out.println("Modify object called: "+this+" "+object); state.getProfiler().start(IProfiler.TYPE_NODE, this); state.getProfiler().start(IProfiler.TYPE_NODEEVENT, IProfiler.NODEEVENT_OBJECTMODIFIED); if(getRelevantAttributes().contains(type)) { // Check if modification changes node memory. boolean affected = isAffected(type); boolean contains = mem.hasNodeMemory(this) && ((Collection)mem.getNodeMemory(this)).contains(object); if(affected) { boolean check = checkConstraints(object, state); // Object no longer valid -> remove if(contains && !check) { ((Collection)mem.getNodeMemory(this)).remove(object); propagateRemovalToObjectConsumers(object, state, mem, agenda); } // Object newly valid -> add else if(!contains && check) { ((Collection)mem.getNodeMemory(this)).add(object); propagateAdditionToObjectConsumers(object, state, mem, agenda); } // Propagate modification for deeper nodes if here no change. else if(contains) { propagateModificationToObjectConsumers(object, type, oldvalue, newvalue, state, mem, agenda); } } else { // Object changed in memory -> propagate modification if(contains) { propagateModificationToObjectConsumers(object, type, oldvalue, newvalue, state, mem, agenda); } } } state.getProfiler().stop(IProfiler.TYPE_NODEEVENT, IProfiler.NODEEVENT_OBJECTMODIFIED); state.getProfiler().stop(IProfiler.TYPE_NODE, this); } /** * Propagate an indirect object change to this node. * @param id The changed object. */ public void modifyIndirectObject(Object id, OAVAttributeType type, Object oldvalue, Object newvalue, IOAVState state, ReteMemory mem, AbstractAgenda agenda) { Collection oldmem = getNodeMemory(mem); Collection input = getObjectSource().getNodeMemory(mem); if(input!=null) { // Todo: Use index for avoiding the need for checking all objects. for(Iterator it=input.iterator(); it.hasNext(); ) { Object object = it.next(); boolean contains = oldmem!=null && oldmem.contains(object); boolean check = checkConstraints(object, state); // Object no longer valid -> remove if(contains && !check) { ((Collection)mem.getNodeMemory(this)).remove(object); propagateRemovalToObjectConsumers(object, state, mem, agenda); } // Object newly valid -> add else if(!contains && check) { ((Collection)mem.getNodeMemory(this)).add(object); propagateAdditionToObjectConsumers(object, state, mem, agenda); } } } } /** * Set the object source of this node. * @param node The object source node. */ public void setObjectSource(IObjectSourceNode node) { this.osource = node; } /** * Get the object source of this node. * @return The object source node. */ public IObjectSourceNode getObjectSource() { return osource; } //-------- object source interface -------- /** * Add an object consumer node. * @param node A new consumer node. */ public void addObjectConsumer(IObjectConsumerNode node) { if(oconsumers==null) { oconsumers = new IObjectConsumerNode[]{node}; } else { IObjectConsumerNode[] tmp = new IObjectConsumerNode[oconsumers.length+1]; System.arraycopy(oconsumers, 0, tmp, 0, oconsumers.length); tmp[oconsumers.length] = node; oconsumers = tmp; } relevants = null; // Will be recalculated on next access; } /** * Remove an object consumer. * @param node The consumer node. */ public void removeObjectConsumer(IObjectConsumerNode node) { if(oconsumers!=null) { for(int i=0; i<oconsumers.length; i++) { if(oconsumers[i].equals(node)) { if(oconsumers.length==1) { oconsumers = null; } else { IObjectConsumerNode[] tmp = new IObjectConsumerNode[oconsumers.length-1]; if(i>0) System.arraycopy(oconsumers, 0, tmp, 0, i); if(i<oconsumers.length-1) System.arraycopy(oconsumers, i+1, tmp, i, oconsumers.length-1-i); oconsumers = tmp; } break; } } } } /** * Get the memory for this node. * @return The memory. */ public Collection getNodeMemory(ReteMemory mem) { return mem.hasNodeMemory(this) ? (Collection)mem.getNodeMemory(this) : null; } /** * Get all object consumer nodes. * @return All object consumer nodes. */ public IObjectConsumerNode[] getObjectConsumers() { return oconsumers; } //-------- methods -------- /** * Create the node memory. * @param state The state. * @return The node memory. */ public Object createNodeMemory(IOAVState state) { return state.isJavaIdentity() ? (Set)new MixedIdentityHashSet(state) : new LinkedHashSet(); } //-------- helper methods -------- /** * Propagate a new object to all object consumers. * @param object The new object. */ protected void propagateAdditionToObjectConsumers(Object object, IOAVState state, ReteMemory mem, AbstractAgenda agenda) { IObjectConsumerNode[] ocon = oconsumers; for(int i=0; ocon!=null && i<ocon.length; i++) ocon[i].addObject(object, state, mem, agenda); } /** * Propagate a removed object to all object consumers. * @param object The new object. */ protected void propagateRemovalToObjectConsumers(Object object, IOAVState state, ReteMemory mem, AbstractAgenda agenda) { IObjectConsumerNode[] ocon = oconsumers; for(int i=0; ocon!=null && i<ocon.length; i++) ocon[i].removeObject(object, state, mem, agenda); } /** * Propagate a modified object to all object consumers. * @param object The new object. */ protected void propagateModificationToObjectConsumers(Object object, OAVAttributeType type, Object oldvalue, Object newvalue, IOAVState state, ReteMemory mem, AbstractAgenda agenda) { IObjectConsumerNode[] ocon = oconsumers; for(int i=0; ocon!=null && i<ocon.length; i++) ocon[i].modifyObject(object, type, oldvalue, newvalue, state, mem, agenda); } /** * Check the constraints with respect * to the object. * @return True, if object fits constraints. */ protected boolean checkConstraints(Object right, IOAVState state) { boolean pass = true; for(int i=0; pass && evaluators!=null && i<evaluators.length; i++) pass = evaluators[i].evaluate(right, null, state); return pass; } /** * Test if the node is affected from a modification. * @param type The attribute type. * @return True, if possibly affected. */ public boolean isAffected(OAVAttributeType attr) { boolean ret = false; for(int i=0; !ret && evaluators!=null && i<evaluators.length; i++) ret = evaluators[i].isAffected(-1, attr); return ret; } /** * Get the set of relevant attribute types. */ public AttributeSet getRelevantAttributes() { if(relevants==null) { synchronized(this) { if(relevants==null) { relevants = new AttributeSet(); for(int i=0; evaluators!=null && i<evaluators.length; i++) { relevants.addAll(evaluators[i].getRelevantAttributes()); } for(int i=0; oconsumers!=null && i<oconsumers.length; i++) { relevants.addAll(oconsumers[i].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) { indirects = new AttributeSet(); for(int i=0; evaluators!=null && i<evaluators.length; i++) { indirects.addAll(evaluators[i].getIndirectAttributes()); } } } } return indirects; } /** * Get the string representation. * @return The string representation. */ public String toString() { return toString(", evaluators="+SUtil.arrayToString(evaluators)); } /** * Get the constraint evaluators. */ public IConstraintEvaluator[] getConstraintEvaluators() { return evaluators; } //-------- 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) { AlphaNode clone = (AlphaNode)theclone; // Deep clone tuple consumers clone.oconsumers = new IObjectConsumerNode[oconsumers.length]; for(int i=0; i<oconsumers.length; i++) clone.oconsumers[i] = (IObjectConsumerNode)oconsumers[i].clone(); // Set the new source clone.setObjectSource((IObjectSourceNode)osource.clone()); // Shallow clone evaluators if(evaluators!=null) { clone.evaluators = new IConstraintEvaluator[evaluators.length]; System.arraycopy(evaluators, 0, clone.evaluators, 0, evaluators.length); } // Shallow copy the relevant attributes if(relevants!=null) clone.relevants = (AttributeSet)((AttributeSet)relevants).clone(); } }