package jadex.rules.parser.conditions.javagrammar;
import jadex.rules.rulesystem.ICondition;
import jadex.rules.rulesystem.rules.AndCondition;
import jadex.rules.rulesystem.rules.ArraySelector;
import jadex.rules.rulesystem.rules.BoundConstraint;
import jadex.rules.rulesystem.rules.ConstrainableCondition;
import jadex.rules.rulesystem.rules.FunctionCall;
import jadex.rules.rulesystem.rules.IConstraint;
import jadex.rules.rulesystem.rules.IOperator;
import jadex.rules.rulesystem.rules.MethodCall;
import jadex.rules.rulesystem.rules.ObjectCondition;
import jadex.rules.rulesystem.rules.TestCondition;
import jadex.rules.rulesystem.rules.Variable;
import jadex.rules.rulesystem.rules.functions.OperatorFunction;
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.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* The build context captures knowledge about
* conditions, variables, etc. during constraint
* parsing or building.
*/
public class BuildContext
{
//-------- attributes --------
/** The list of conditions. */
protected List lcons;
/** The variables (name -> variable). */
protected Map variables;
/** The bound constraints (variable -> boundconstraint (only variable definitions, i.e. EQUAL constraints)). */
protected Map boundconstraints;
/** The object conditions (variable -> object conditions (object condition with defining bound constraint)). */
protected Map bcons;
/** The OAV type model. */
protected OAVTypeModel tmodel;
/** The dummy condition (if any). */
protected ObjectCondition dummy;
/** The parent build context (if any). */
protected BuildContext parent;
/** Stack for object conditions (for checking if constraints can be generated in current context). */
protected List oconstack;
//-------- constructors --------
/**
* Create a new build context.
* @param condition The initial condition.
*/
public BuildContext(ICondition condition, OAVTypeModel tmodel)
{
this.tmodel = tmodel;
this.lcons = new ArrayList();
this.variables = new HashMap();
this.boundconstraints = new HashMap();
this.bcons = new HashMap();
if(condition!=null)
addCondition(condition);
}
/**
* Create a new build context.
* @param parent The parent build context.
*/
public BuildContext(BuildContext parent)
{
this(null, parent.getTypeModel());
this.parent = parent;
}
//-------- methods --------
/**
* Get the conditions.
*/
public List getConditions()
{
return lcons;
}
/**
* Get the OAV type model.
*/
public OAVTypeModel getTypeModel()
{
return tmodel;
}
/**
* Get an object condition for a variable, i.e. a condition, where
* constraints related to the variable can be added to.
* @param var The variable
* @return The object condition.
* @throws RuntimeExcpetion when no condition was found.
*/
public ConstrainableCondition getConstrainableCondition(Variable var)
{
ConstrainableCondition ret = getConstrainableCondition0(var);
if(ret==null)
{
throw new RuntimeException("No object condition for: "+var);
}
return ret;
}
/**
* Get an object condition for a variable, i.e. a condition, where
* constraints related to the variable can be added to.
* @param var The variable
* @return The object condition.
* @throws RuntimeExcpetion when no condition was found.
*/
public ConstrainableCondition getConstrainableCondition0(Variable var)
{
ConstrainableCondition ret = null;
ret = parent!=null ? parent.getConstrainableCondition0(var) : null;
if(ret==null)
{
ret = (ObjectCondition)bcons.get(var);
}
return ret;
}
/**
* Get the bound constraint a variable, i.e. the value source required
* for obtaining the variable value from the variables object condition.
* @param var The variable
* @return The bound constraint.
* @throws RuntimeExcpetion when no constraint was found.
*/
public BoundConstraint getBoundConstraint(Variable var)
{
BoundConstraint ret = null;
try
{
ret = parent!=null ? parent.getBoundConstraint(var) : null;
}
catch(Exception e)
{
}
if(ret==null)
{
ret = (BoundConstraint)boundconstraints.get(var);
}
if(ret==null)
{
throw new RuntimeException("No bound constraint for: "+var);
}
return ret;
}
/**
* Create a new variable and bind it using the given object condition and value source.
* @param condition The object condition.
* @param valuesource The value source.
* @return The new variable.
*/
public Variable generateVariableBinding(ConstrainableCondition condition, Object valuesource)
{
return generateVariableBinding(condition, generateVariableName(), valuesource);
}
/**
* Generate a variable name.
* @return An unused variable name.
*/
public String generateVariableName()
{
String varname;
for(int i=1; getVariable(varname = "$tmpvar_"+i)!=null; i++);
return varname;
}
/**
* Create a new variable and bind it using the given object condition and value source.
* @param condition The object condition.
* @param name The variable name.
* @param valuesource The value source.
* @return The new variable.
*/
public Variable generateVariableBinding(ConstrainableCondition condition, String name, Object valuesource)
{
return generateVariableBinding(condition, name, getReturnType(condition, valuesource, tmodel), valuesource);
}
/**
* Create a new variable and bind it using the given object condition and value source.
* @param condition The object condition.
* @param name The variable name.
* @param valuesource The value source.
* @return The new variable.
*/
public Variable generateVariableBinding(ConstrainableCondition condition, String name, OAVObjectType type, Object valuesource)
{
Variable tmpvar = new Variable(name, type, false, true);
variables.put(name, tmpvar);
BoundConstraint bc = new BoundConstraint(valuesource, tmpvar);
boundconstraints.put(tmpvar, bc);
condition.addConstraint(bc);
bcons.put(tmpvar, condition);
return tmpvar;
}
/**
* Create a new object condition with the given constraints.
* Also adds mappings corresponding to bound constraints (if any).
* @param type The object type.
* @param constraints The constraints (if any).
*/
public ObjectCondition createObjectCondition(OAVObjectType type, IConstraint[] constraints)
{
ObjectCondition ocon = new ObjectCondition(type);
for(int i=0; constraints!=null && i<constraints.length; i++)
{
ocon.addConstraint(constraints[i]);
}
addCondition(ocon);
return ocon;
}
/**
* Get a variable.
* @param name The name of the variable.
* @return The variable, if any.
*/
public Variable getVariable(String name)
{
Variable ret = parent!=null ? parent.getVariable(name) : null;
if(ret==null)
ret = (Variable)variables.get(name);
return ret;
}
/**
* Add a variable.
* @param var The variable.
*/
public void addVariable(Variable var)
{
variables.put(var.getName(), var);
}
/**
* Expressions, which are unrelated to real object
* conditions should be bound to the dummy condition.
* After building all constraints, the dummy condition
* will be removed by reassigning its constraints to
* a suitable object condition (respecting variable assignment order).
*/
public ObjectCondition getDummyCondition()
{
if(dummy==null)
{
dummy = new ObjectCondition(OAVJavaType.java_object_type);
addCondition(dummy);
}
return dummy;
}
/**
* Test if a dummy condition was used in the context.
*/
public boolean hasDummyCondition()
{
return dummy!=null;
}
/**
* Add a condition to the context.
* @param condition The condition.
*/
public void addCondition(ICondition condition)
{
int start = lcons.size();
lcons.add(condition);
// Unfold AND conditions.
for(int i=start; i<lcons.size(); i++)
{
if(lcons.get(i) instanceof AndCondition)
{
lcons.addAll(i+1, ((AndCondition)lcons.get(i)).getConditions());
lcons.remove(i);
i--; // Decrement to check new condition at i instead of continuing with i+1.
}
}
// Find conditions, bound to specific variables.
for(int i=start; i<lcons.size(); i++)
{
List bcs = null;
if(lcons.get(i) instanceof ConstrainableCondition)
{
bcs = ((ConstrainableCondition)lcons.get(i)).getBoundConstraints();
for(int j=0; bcs!=null && j<bcs.size(); j++)
{
BoundConstraint bc = (BoundConstraint)bcs.get(j);
List bvars = bc.getBindVariables();
for(int k=0; k<bvars.size(); k++)
{
variables.put(((Variable)bvars.get(k)).getName(), bvars.get(k));
// Todo: by multiple ocurrences use the simplest bc.getValueSource()
if(bc.getOperator().equals(IOperator.EQUAL))
{
boundconstraints.put(bvars.get(k), bc);
bcons.put(bvars.get(k), lcons.get(i));
}
}
}
}
}
}
//-------- helper methods --------
/**
* Get the return type of a value source.
* @param valuesource The value source.
* @param tmodel The type model.
* @return The object type.
*/
protected static OAVObjectType getReturnType(ConstrainableCondition cond, Object valuesource, OAVTypeModel tmodel)
{
OAVObjectType ret;
int i=1; // nesting depth for arrays.
// For chained access only the last one is relevant.
if(valuesource instanceof Object[])
{
while(((Object[])valuesource)[((Object[])valuesource).length-i] instanceof ArraySelector)
{
i++;
}
valuesource = ((Object[])valuesource)[((Object[])valuesource).length-i];
}
else if(valuesource instanceof List)
{
while(((List)valuesource).get(((List)valuesource).size()-i) instanceof ArraySelector)
{
i++;
}
valuesource = ((List)valuesource).get(((List)valuesource).size()-i);
}
if(valuesource instanceof OAVAttributeType)
{
ret = ((OAVAttributeType)valuesource).getType();
}
else if(valuesource instanceof MethodCall)
{
ret = tmodel.getJavaType(((MethodCall)valuesource).getMethod().getReturnType());
}
else if(valuesource instanceof FunctionCall)
{
ret = tmodel.getJavaType(((FunctionCall)valuesource).getFunction().getReturnType());
}
else if(valuesource==null && cond instanceof ObjectCondition)
{
ret = ((ObjectCondition)cond).getObjectType();
}
else
{
throw new RuntimeException("Unknown value source type: "+valuesource);
}
// Unfold arrays.
if(i>1)
{
Class clazz = ((OAVJavaType)ret).getClazz();
for(int j=1; j<i; j++)
clazz = clazz.getComponentType();
ret = tmodel.getJavaType(clazz);
}
return ret;
}
/**
* Return the parent build context (if any).
*/
public BuildContext getParent()
{
return parent;
}
/**
* Get the variables, which are available in this build context.
*/
public Set getBoundVariables()
{
Set ret = new HashSet();
if(getParent()!=null)
ret.addAll(getParent().getBoundVariables());
for(int i=0; i<lcons.size(); i++)
{
ICondition con = (ICondition) lcons.get(i);
List bcs = null;
if(con instanceof ConstrainableCondition)
{
bcs = ((ConstrainableCondition)con).getBoundConstraints();
for(int j=0; bcs!=null && j<bcs.size(); j++)
{
BoundConstraint bc = (BoundConstraint)bcs.get(j);
if(bc.getOperator().equals(IOperator.EQUAL))
{
List bvars = bc.getBindVariables();
for(int k=0; k<bvars.size(); k++)
{
ret.add(bvars.get(k));
}
}
}
}
else if(con instanceof TestCondition)
{
FunctionCall func = ((TestCondition)con).getConstraint().getFunctionCall();
if(func.getFunction() instanceof OperatorFunction && ((OperatorFunction)func.getFunction()).getOperator().equals(IOperator.EQUAL))
{
List ps = func.getParameterSources();
if(ps.get(0) instanceof Variable)
{
ret.add(ps.get(0));
}
}
}
}
return ret;
}
/**
* Push a condition on the stack.
*/
public void pushCondition(ConstrainableCondition con)
{
if(getDefiningScope(con)!=this)
{
if(con instanceof ObjectCondition)
{
// Create clone of inconsistent condition in inner scope.
ObjectCondition ocon = (ObjectCondition)con;
generateVariableBinding(ocon, null); // new null bound constraint to make sure that cloned condition refers to SAME object.
con = createObjectCondition(ocon.getObjectType(), (IConstraint[])ocon.getConstraints().toArray(new IConstraint[ocon.getConstraints().size()]));
}
else
{
throw new RuntimeException("Wrong scope: "+con);
}
}
if(oconstack==null)
{
oconstack = new ArrayList();
}
oconstack.add(con);
}
/**
* Pop a condition from the stack.
*/
public void popCondition()
{
if(oconstack==null || oconstack.isEmpty())
throw new RuntimeException("Condition stack error: "+oconstack);
if(oconstack.size()==1)
oconstack = null;
else
oconstack.remove(oconstack.size()-1);
}
/**
* Get the current condition from the stack.
*/
public ConstrainableCondition getCurrentCondition()
{
if(oconstack==null || oconstack.isEmpty())
throw new RuntimeException("Condition stack error: "+oconstack);
return (ConstrainableCondition)oconstack.get(oconstack.size()-1);
}
/**
* Get the context in which the given condition is defined.
*/
protected BuildContext getDefiningScope(ICondition con)
{
BuildContext ret = null;
BuildContext scope = this;
while(scope!=null)
{
if(scope.lcons.contains(con))
ret = scope;
scope = scope.getParent();
}
return ret;
}
}