package jadex.rules.rulesystem.rete.nodes;
import jadex.rules.rulesystem.AbstractAgenda;
import jadex.rules.rulesystem.IRule;
import jadex.rules.rulesystem.rete.builder.ReteBuilder;
import jadex.rules.rulesystem.rete.extractors.AttributeSet;
import jadex.rules.state.IOAVState;
import jadex.rules.state.IProfiler;
import jadex.rules.state.OAVAttributeType;
import jadex.rules.state.OAVJavaType;
import jadex.rules.state.OAVObjectType;
import jadex.rules.state.OAVTypeModel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* ReteNode implementation of the IConditionSystem.
*/
public class ReteNode extends AbstractNode implements IObjectSourceNode
{
//-------- attributes --------
/** The type nodes. */
protected Map typenodes;
/** Matching nodes for each (sub)type (cached for speed). */
protected Map typenodesets;
/** Indirectly affected nodes for an attribute type (cached for speed). */
protected Map indirectnodesets;
/** The initial fact node (if any). */
protected InitialFactNode initialfact;
/** The terminal nodes (IRule -> Node). */
protected Map terminalnodes;
/** The rete builder. */
protected ReteBuilder builder;
/** The set of relevant attributes. */
protected AttributeSet relevants;
/** Do a consistency check after each state change (requires asserts). */
protected boolean check;
/** The node counter in this network. */
protected int nodecounter;
//-------- constructors --------
/**
* Create a new rete system.
* @param state The state.
*/
public ReteNode()
{
super(0);
this.nodecounter = 1;
this.typenodes = new LinkedHashMap();
this.terminalnodes = new LinkedHashMap();
// The typenode mapping for each object type is dynamically created on first access. hack???
this.typenodesets = Collections.synchronizedMap(new LinkedHashMap());
}
//-------- methods --------
/**
* Tell the condition system about a
* new object in the state.
* @param object The new object.
*/
public void addObject(Object id, OAVObjectType type, IOAVState state, ReteMemory mem, AbstractAgenda agenda)
{
// System.out.println("Value added: "+id+" "+type);
state.getProfiler().start(IProfiler.TYPE_NODE, this);
state.getProfiler().start(IProfiler.TYPE_NODEEVENT, IProfiler.NODEEVENT_OBJECTADDED);
Set tns = getTypeNodes(type);
if(tns!=null)
{
for(Iterator it=tns.iterator(); it.hasNext(); )
((AlphaNode)it.next()).addObject(id, state, mem, agenda);
assert !check || checkConsistency(mem);
}
// else
// System.out.println("No typenode(s) available for: "+type);
state.getProfiler().stop(IProfiler.TYPE_NODEEVENT, IProfiler.NODEEVENT_OBJECTADDED);
state.getProfiler().stop(IProfiler.TYPE_NODE, this);
}
/**
* Tell the condition system about a
* removed object in the state.
* @param object The removed object.
*/
public void removeObject(Object id, OAVObjectType type, IOAVState state, ReteMemory mem, AbstractAgenda agenda)
{
// if(type instanceof OAVJavaType && ((OAVJavaType)type).getClazz().getName().indexOf("Wastebin")!=-1)
// {
// System.out.println("removedRETE: "+id);
// }
state.getProfiler().start(IProfiler.TYPE_NODE, this);
state.getProfiler().start(IProfiler.TYPE_NODEEVENT, IProfiler.NODEEVENT_OBJECTREMOVED);
Set tns = getTypeNodes(type);
if(tns!=null)
{
for(Iterator it=tns.iterator(); it.hasNext(); )
((AlphaNode)it.next()).removeObject(id, state, mem, agenda);
assert !check || checkConsistency(mem);
}
// else
// System.out.println("No typenode(s) available for: "+type);
//assert !mem.contains(id);
// System.out.println("Value removed: "+id+" "+type);
state.getProfiler().stop(IProfiler.TYPE_NODEEVENT, IProfiler.NODEEVENT_OBJECTREMOVED);
state.getProfiler().stop(IProfiler.TYPE_NODE, this);
}
/**
* Tell the condition system about a
* modified object in the state.
* @param object The new object.
*/
public void modifyObject(Object id, OAVObjectType type, OAVAttributeType attr, Object oldvalue,
Object newvalue, IOAVState state, ReteMemory mem, AbstractAgenda agenda)
{
// System.out.println("Value set: "+id+" "+type+" "+value);
state.getProfiler().start(IProfiler.TYPE_NODE, this);
state.getProfiler().start(IProfiler.TYPE_NODEEVENT, IProfiler.NODEEVENT_OBJECTMODIFIED);
// if(attr.getName().indexOf("daytime")!=-1)
// System.out.println("test");
if(getRelevantAttributes().contains(attr))
{
Set tns = getTypeNodes(type);
if(tns!=null && !tns.isEmpty())
{
for(Iterator it=tns.iterator(); it.hasNext(); )
{
TypeNode tn = (TypeNode)it.next();
tn.modifyObject(id, attr, oldvalue, newvalue, state, mem, agenda);
// old code for modified object
//tn.removeObject(id, state, mem, agenda);
//tn.addObject(id, state, mem, agenda);
}
assert !check || checkConsistency(mem);
}
//else
// System.out.println("No typenode(s) available for: "+value);
}
Set ins = getIndirectNodes(attr, state.getTypeModel());
if(ins!=null)
{
for(Iterator it=ins.iterator(); it.hasNext(); )
{
((INode)it.next()).modifyIndirectObject(id, attr, 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 object The changed object.
*/
public void modifyIndirectObject(Object object, OAVAttributeType type, Object oldvalue, Object newvalue, IOAVState state, ReteMemory mem, AbstractAgenda agenda)
{
// Should never be called.
throw new UnsupportedOperationException("Unsupported method.");
}
/**
* Add a rule to the network.
* @param rule The rule to add.
*/
public void addRule(IRule rule)
{
if(builder==null)
builder = new ReteBuilder();
builder.addRule(this, rule);
}
/**
* Remove a rule from the network.
* @param rule The rule to remove.
*/
public void removeRule(IRule rule)
{
if(builder==null)
builder = new ReteBuilder();
builder.removeRule(this, rule);
}
/**
* Set the terminal node for a rule.
* @param rule The rule.
* @param node The node.
*/
public void putTerminalNode(TerminalNode node)
{
terminalnodes.put(node.getRule(), node);
}
/**
* Set the terminal node for a rule.
* @param rule The rule.
* @param node The node.
*/
public TerminalNode getTerminalNode(IRule rule)
{
return (TerminalNode)terminalnodes.get(rule);
}
/**
* Get the number of nodes in the network.
* @return The number of nodes.
*/
public int getNodeCount()
{
List ret = new ArrayList();
ret.add(this);
for(int i=0; i<ret.size(); i++)
{
INode node = (INode)ret.get(i);
if(node instanceof IObjectSourceNode)
{
IObjectConsumerNode[] consumers = ((IObjectSourceNode)node).getObjectConsumers();
for(int j=0; j<consumers.length; j++)
{
if(!ret.contains(consumers[j]))
ret.add(consumers[j]);
}
}
if(node instanceof ITupleSourceNode)
{
ITupleConsumerNode[] consumers = ((ITupleSourceNode)node).getTupleConsumers();
for(int j=0; j<consumers.length; j++)
{
if(!ret.contains(consumers[j]))
ret.add(consumers[j]);
}
}
}
return ret.size();
}
//-------- object source node --------
/**
* Add an object consumer node.
* @param node A new consumer node.
*/
public void addObjectConsumer(IObjectConsumerNode node)
{
if(node instanceof TypeNode)
{
if(typenodes.put(((TypeNode)node).getObjectType(), node)!=null)
throw new RuntimeException("Type node already present in network: "+node);
}
else if(node instanceof InitialFactNode)
{
if(initialfact!=null)
throw new RuntimeException("Initial fact node already present in network: "+node);
initialfact = (InitialFactNode)node;
}
else
{
throw new RuntimeException("Rete node only allows type or initial fact node children: "+node);
}
relevants = null; // Will be recalculated on next access;
}
/**
* Remove an object consumer.
* @param node The consumer node.
*/
public void removeObjectConsumer(IObjectConsumerNode node)
{
typenodes.remove(((TypeNode)node).getObjectType());
}
/**
* 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()
{
Collection vals = typenodes.values();
IObjectConsumerNode[] ret = (IObjectConsumerNode[])vals.toArray(new IObjectConsumerNode[vals.size()+(initialfact!=null?1:0)]);
if(initialfact!=null)
ret[ret.length-1] = initialfact;
return ret;
}
//-------- methods --------
/**
* Get the node for a type.
* @param type The type.
* @return The type node (if any).
*/
public TypeNode getTypeNode(OAVObjectType type)
{
return (TypeNode)typenodes.get(type);
}
/**
* Get the initial fact node (if any).
*/
public InitialFactNode getInitialFactNode()
{
return initialfact;
}
/**
* Create the node memory.
* @param state The state.
* @return The node memory.
*/
public Object createNodeMemory(IOAVState state)
{
return null;
}
/**
* Get the set of relevant attribute types.
*/
public AttributeSet getRelevantAttributes()
{
if(relevants==null)
{
synchronized(this)
{
if(relevants==null)
{
relevants = new AttributeSet();
for(Iterator it=typenodes.values().iterator(); it.hasNext(); )
{
relevants.addAll(((INode)it.next()).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()
{
return AttributeSet.EMPTY_ATTRIBUTESET;
}
/**
* Get the builder.
* @return The rete builder.
*/
public ReteBuilder getBuilder()
{
return builder;
}
//-------- helper methods --------
/**
* Get the set of matching type nodes for a (sub)type.
* @param type The object type.
* @return The set of type nodes for that object type.
*/
protected Set getTypeNodes(OAVObjectType type)
{
Set ret = (Set)typenodesets.get(type);
if(ret==null)
{
synchronized(this)
{
ret = (Set)typenodesets.get(type);
if(ret==null)
{
ret = new HashSet();
for(Iterator it=typenodes.values().iterator(); it.hasNext(); )
{
TypeNode tnode = (TypeNode)it.next();
if(type.isSubtype(tnode.getObjectType()))
ret.add(tnode);
}
typenodesets.put(type, ret);
}
}
}
return ret;
}
/**
* Get the set of indirectly affected nodes for an attribute type.
* @param attrtype The attribute type.
* @param tmodel The OAV type model.
* @return The set of indirectly affected nodes for that attribute type.
*/
protected Set getIndirectNodes(OAVAttributeType attrtype, OAVTypeModel tmodel)
{
if(indirectnodesets==null)
{
synchronized(this)
{
if(indirectnodesets==null)
{
indirectnodesets = new HashMap();
List nodelist = new ArrayList();
List nodeset = new ArrayList();
nodelist.addAll(typenodes.values());
nodeset.addAll(nodelist);
for(int i=0; i<nodelist.size(); i++)
{
INode node = (INode)nodelist.get(i);
AttributeSet attrset = node.getIndirectAttributes();
if(attrset.getAttributeSet()!=null)
{
for(Iterator it=attrset.getAttributeSet().iterator(); it.hasNext(); )
{
Object attr = it.next();
Set indinodes = (Set)indirectnodesets.get(attr);
if(indinodes==null)
{
indinodes = new HashSet();
indirectnodesets.put(attr, indinodes);
}
indinodes.add(node);
}
}
if(attrset.getAllTypesSet()!=null)
{
for(Iterator it=attrset.getAllTypesSet().iterator(); it.hasNext(); )
{
Object objtype = it.next();
Set indinodes = (Set)indirectnodesets.get(objtype);
if(indinodes==null)
{
indinodes = new HashSet();
indirectnodesets.put(objtype, indinodes);
}
indinodes.add(node);
}
}
if(node instanceof IObjectSourceNode)
{
INode[] subs = ((IObjectSourceNode)node).getObjectConsumers();
for(int n=0; subs!=null && n<subs.length; n++)
{
if(!nodeset.contains(subs[n]))
{
nodelist.add(subs[n]);
nodeset.add(subs[n]);
}
}
}
if(node instanceof ITupleSourceNode)
{
INode[] subs = ((ITupleSourceNode)node).getTupleConsumers();
for(int n=0; subs!=null && n<subs.length; n++)
{
if(!nodeset.contains(subs[n]))
{
nodelist.add(subs[n]);
nodeset.add(subs[n]);
}
}
}
}
}
}
}
Set tmp1 = (Set)indirectnodesets.get(attrtype);
Set tmp2 = (Set)indirectnodesets.get(attrtype.getObjectType());
Set ret = tmp1;
if(tmp2!=null)
{
if(ret!=null)
ret.addAll(tmp2);
else
ret = tmp2;
}
if(attrtype.getObjectType() instanceof OAVJavaType)
{
List classes = new ArrayList();
Set sclasses = new HashSet();
classes.add(((OAVJavaType)attrtype.getObjectType()).getClazz());
for(int i=0; i<classes.size(); i++)
{
Class clazz = (Class)classes.get(i);
if(clazz.getSuperclass()!=null && !sclasses.contains(clazz.getSuperclass()))
{
classes.add(clazz.getSuperclass());
sclasses.add(clazz.getSuperclass());
}
Class[] ifs = clazz.getInterfaces();
for(int j=0; j<ifs.length; j++)
{
if(!sclasses.contains(ifs[j]))
{
classes.add(ifs[j]);
sclasses.add(ifs[j]);
}
}
}
for(Iterator it=sclasses.iterator(); it.hasNext(); )
{
Class clazz = (Class)it.next();
tmp2 = (Set)indirectnodesets.get(tmodel.getJavaType(clazz));
if(tmp2!=null)
{
if(ret!=null)
ret.addAll(tmp2);
else
ret = tmp2;
}
}
}
return ret;
}
//-------- cloneable --------
/**
* Do clone makes a deep clone without regarding cycles.
* @param clone The clone.
*/
protected void doClone(Object theclone)
{
ReteNode clone = (ReteNode)theclone;
// Deep clone type nodes
clone.typenodes = new LinkedHashMap();
for(Iterator it=typenodes.keySet().iterator(); it.hasNext(); )
{
OAVObjectType type = (OAVObjectType)it.next();
clone.typenodes.put(type, getTypeNode(type).clone());
}
// Update typenodesets
clone.typenodesets = new LinkedHashMap();
for(Iterator it=typenodesets.keySet().iterator(); it.hasNext(); )
{
OAVObjectType type = (OAVObjectType)it.next();
Set oldtns = getTypeNodes(type);
if(oldtns!=null)
{
Set newtns = new HashSet();
for(Iterator it2=oldtns.iterator(); it.hasNext(); )
{
INode oldtn = (INode)it2.next();
newtns.add(oldtn.clone());
}
clone.typenodesets.put(type, newtns);
}
}
// Deep clone initial fact node
if(initialfact!=null)
clone.initialfact = (InitialFactNode)initialfact.clone();
// Refresh terminal nodes
// Searches the whole net and if a terminal node is found
// it is added to the terminalnodes map.
clone.terminalnodes = new HashMap();
List nodes = new ArrayList();
nodes.addAll(clone.typenodes.values());
for(int i=0; i<nodes.size(); i++)
{
Object node = nodes.get(i);
if(node instanceof IObjectSourceNode)
{
IObjectSourceNode osn = (IObjectSourceNode)node;
IObjectConsumerNode[] cons = osn.getObjectConsumers();
for(int j=0; j<cons.length; j++)
{
if(!nodes.contains(cons[j]))
nodes.add(cons[j]);
}
}
if(node instanceof ITupleSourceNode)
{
ITupleSourceNode tsn = (ITupleSourceNode)node;
ITupleConsumerNode[] cons = tsn.getTupleConsumers();
for(int j=0; j<cons.length; j++)
{
if(!nodes.contains(cons[j]))
nodes.add(cons[j]);
}
}
if(node instanceof TerminalNode)
clone.putTerminalNode((TerminalNode)((TerminalNode)node).clone());
}
// Keep same stateless rete builder
// Shallow copy the relevant attributes
if(relevants!=null)
clone.relevants = (AttributeSet)((AttributeSet)relevants).clone();
}
//-------- checking --------
protected int changecnt;
protected List checked = new ArrayList();
/**
* Check consistency of Rete network/memory.
* For debugging. Only performs some simple
* checks and does not assure complete consistency.
*/
protected boolean checkConsistency(ReteMemory mem)
{
boolean consistent = true;
// Iterate through all node.
List nodes = new ArrayList();
nodes.add(this);
while(consistent && !nodes.isEmpty())
{
INode node = (INode)nodes.remove(nodes.size()-1);
if(node instanceof ITupleSourceNode)
{
INode[] subnodes = ((ITupleSourceNode)node).getTupleConsumers();
for(int i=0; i<subnodes.length; i++)
nodes.add(subnodes[i]);
}
if(node instanceof IObjectSourceNode)
{
INode[] subnodes = ((IObjectSourceNode)node).getObjectConsumers();
for(int i=0; i<subnodes.length; i++)
nodes.add(subnodes[i]);
}
node.checkNodeConsistency(mem);
}
changecnt++;
checked.clear();
return consistent;
}
/**
* Get the next nodecounter.
* @return The id for the next node.
*/
public int getNextNodeId()
{
return nodecounter++;
}
}