package jadex.rules.rulesystem.rete.nodes;
import jadex.rules.rulesystem.AbstractAgenda;
import jadex.rules.rulesystem.rete.Tuple;
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.LinkedHashSet;
/**
* A test node takes evaluates a predicate.
*/
public class TestNode extends AbstractNode implements ITupleConsumerNode, ITupleSourceNode
{
//-------- attributes --------
/** The tuple consumers. */
protected ITupleConsumerNode[] tconsumers;
/** The tuple source. */
protected ITupleSourceNode tsource;
/** The constraint evaluator. */
final protected IConstraintEvaluator evaluator;
/** The set of relevant attributes. */
protected AttributeSet relevants;
/** The set of indirect attributes. */
protected AttributeSet indirects;
//-------- constructors --------
/**
* Create a new test node.
* @param evaluator The evaluator.
*/
public TestNode(int nodeid, IConstraintEvaluator evaluator)
{
super(nodeid);
this.evaluator = evaluator;
}
//-------- ITupleConsumerNode ---------
/**
* Add a new tuple to this node.
* @param tuple The tuple.
*/
public void addTuple(Tuple tuple, IOAVState state, ReteMemory mem, AbstractAgenda agenda)
{
//System.out.println("Add tuple called: "+this+" "+tuple);
state.getProfiler().start(IProfiler.TYPE_NODE, this);
state.getProfiler().start(IProfiler.TYPE_NODEEVENT, IProfiler.NODEEVENT_TUPLEADDED);
Collection tmem = (Collection)mem.getNodeMemory(this);
if(!tmem.contains(tuple) && evaluator.evaluate(null, tuple, state))
{
tmem.add(tuple);
//System.out.println("Tuple passed constraint check: "+this+" "+object);
propagateAdditionToTupleConsumers(tuple, state, mem, agenda);
}
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 called: "+this+" "+tuple);
state.getProfiler().start(IProfiler.TYPE_NODE, this);
state.getProfiler().start(IProfiler.TYPE_NODEEVENT, IProfiler.NODEEVENT_TUPLEREMOVED);
if(mem.hasNodeMemory(this))
{
if(((Collection)mem.getNodeMemory(this)).remove(tuple))
{
propagateRemovalToTupleConsumers(tuple, state, mem, agenda);
}
}
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)
{
//System.out.println("Modify object called: "+this+" "+object);
state.getProfiler().start(IProfiler.TYPE_NODE, this);
state.getProfiler().start(IProfiler.TYPE_NODEEVENT, IProfiler.NODEEVENT_TUPLEMODIFIED);
if(getRelevantAttributes().contains(type))
{
// Check if modification changes node memory.
boolean affected = isAffected(type);
boolean contains = mem.hasNodeMemory(this) && ((Collection)mem.getNodeMemory(this)).contains(tuple);
if(affected)
{
boolean check = evaluator.evaluate(null, tuple, state);
// tuple no longer valid -> remove
if(contains && !check)
{
((Collection)mem.getNodeMemory(this)).remove(tuple);
propagateRemovalToTupleConsumers(tuple, state, mem, agenda);
}
// tuple newly valid -> add
else if(!contains && check)
{
((Collection)mem.getNodeMemory(this)).add(tuple);
propagateAdditionToTupleConsumers(tuple, state, mem, agenda);
}
else if(contains)
{
propagateModificationToTupleConsumers(tuple, tupleindex, type,
oldvalue, newvalue, state, mem, agenda);
}
}
else
{
// tuple changed in memory -> propagate modification
if(contains)
{
propagateModificationToTupleConsumers(tuple, tupleindex, type,
oldvalue, newvalue, state, mem, agenda);
}
}
}
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)
{
throw new UnsupportedOperationException("Unsupported method.");
}
/**
* 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;
}
//-------- ITupleSourceNode --------
/**
* Add an tuple consumer node.
* @param node A new consumer node.
*/
public void addTupleConsumer(ITupleConsumerNode node)
{
if(tconsumers==null)
{
tconsumers = new ITupleConsumerNode[]{node};
}
else
{
ITupleConsumerNode[] tmp = new ITupleConsumerNode[tconsumers.length+1];
System.arraycopy(tconsumers, 0, tmp, 0, tconsumers.length);
tmp[tconsumers.length] = node;
tconsumers = tmp;
}
relevants = null; // Will be recalculated on next access;
}
/**
* Remove an tuple consumer.
* @param node The consumer node.
*/
public void removeTupleConsumer(ITupleConsumerNode node)
{
if(tconsumers!=null)
{
for(int i=0; i<tconsumers.length; i++)
{
if(tconsumers[i].equals(node))
{
if(tconsumers.length==1)
{
tconsumers = null;
}
else
{
ITupleConsumerNode[] tmp = new ITupleConsumerNode[tconsumers.length-1];
if(i>0)
System.arraycopy(tconsumers, 0, tmp, 0, i);
if(i<tconsumers.length-1)
System.arraycopy(tconsumers, i+1, tmp, i, tconsumers.length-1-i);
tconsumers = 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 tuple consumer nodes.
* @return All tuple consumer nodes.
*/
public ITupleConsumerNode[] getTupleConsumers()
{
return tconsumers;
}
//-------- INode --------
/**
* Get the set of relevant attribute types.
*/
public AttributeSet getRelevantAttributes()
{
if(relevants==null)
{
synchronized(this)
{
if(relevants==null)
{
relevants = new AttributeSet();
relevants.addAll(evaluator.getRelevantAttributes());
for(int i=0; tconsumers!=null && i<tconsumers.length; i++)
{
relevants.addAll(tconsumers[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();
indirects.addAll(evaluator.getIndirectAttributes());
}
}
}
return indirects;
}
/**
* Create the node memory.
* @param state The state.
* @return The node memory.
*/
public Object createNodeMemory(IOAVState state)
{
return new LinkedHashSet();
}
//-------- methods --------
/**
* Test if the node is affected from a modification.
* @param type The attribute type.
* @return True, if possibly affected.
*/
public boolean isAffected(OAVAttributeType attr)
{
return evaluator.isAffected(-1, attr);
}
/**
* Get the evaluator.
*/
public IConstraintEvaluator getConstraintEvaluator()
{
return evaluator;
}
//-------- helper methods --------
/**
* Propagate a new tuple to all tuple consumers.
* @param tuple The new tuple.
*/
protected void propagateAdditionToTupleConsumers(Tuple tuple, IOAVState state, ReteMemory mem, AbstractAgenda agenda)
{
ITupleConsumerNode[] tcon = tconsumers;
for(int i=0; tcon!=null && i<tcon.length; i++)
tcon[i].addTuple(tuple, state, mem, agenda);
}
/**
* Propagate a removed tuple to all tuple consumers.
* @param tuple The new tuple.
*/
protected void propagateRemovalToTupleConsumers(Tuple tuple, IOAVState state, ReteMemory mem, AbstractAgenda agenda)
{
ITupleConsumerNode[] tcon = tconsumers;
for(int i=0; tcon!=null && i<tcon.length; i++)
tcon[i].removeTuple(tuple, state, mem, agenda);
}
/**
* Propagate a modified tuple to all tuple consumers.
* @param tuple The new tuple.
*/
protected void propagateModificationToTupleConsumers(Tuple tuple, int tupleindex, OAVAttributeType type, Object oldvalue,
Object newvalue, IOAVState state, ReteMemory mem, AbstractAgenda agenda)
{
ITupleConsumerNode[] tcon = tconsumers;
for(int i=0; tcon!=null && i<tcon.length; i++)
tcon[i].modifyTuple(tuple, tupleindex, type, oldvalue, newvalue, state, mem, agenda);
}
//-------- 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)
{
TestNode clone = (TestNode)theclone;
// Deep clone tuple consumers
clone.tconsumers = new ITupleConsumerNode[tconsumers.length];
for(int i=0; i<tconsumers.length; i++)
clone.tconsumers[i] = (ITupleConsumerNode)tconsumers[i].clone();
// Set the source
clone.tsource = (ITupleSourceNode)tsource.clone();
// Keep the evaluator
// Shallow copy the relevant attributes
if(relevants!=null)
clone.relevants = (AttributeSet)((AttributeSet)relevants).clone();
}
}