All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jadex.rules.parser.conditions.javagrammar.ConstraintBuilder Maven / Gradle / Ivy

Go to download

Jadex Rules is a small lightweight rule engine, which currently employs the well-known Rete algorithm for highly efficient rule matching. Jadex rules is therefore similar to other rule engines like JESS and Drools. Despite the similarities there are also important differences between these systems: * Jadex Rules is very small and intended to be used as component of other software. Even though rules can be specified in a Java dialect as well as (a small variation of) the CLIPS language its primary usage is on the API level. Jadex Rules is currently the core component of the Jadex BDI reasoning engine. * Jadex Rules cleanly separates between state and rule representation. This allows the state implementation as well as the matcher to be flexibly exchanged. Some experiments have e.g. been conducted with a Jena representation. Regarding the matcher, it is planned to support also the Treat algorithm, which has a lower memory footprint than Rete. * Jadex Rules pays close attention to rule debugging. The state as well as the rete engine can be observed at runtime. The rule debugger provides functionalities to execute a rule program stepwise and also use rule breakpoints to stop the execution at those points.

There is a newer version: 2.4
Show newest version
package jadex.rules.parser.conditions.javagrammar;

import jadex.commons.SReflect;
import jadex.commons.SUtil;
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.CollectCondition;
import jadex.rules.rulesystem.rules.Constant;
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.LiteralConstraint;
import jadex.rules.rulesystem.rules.MethodCall;
import jadex.rules.rulesystem.rules.NotCondition;
import jadex.rules.rulesystem.rules.ObjectCondition;
import jadex.rules.rulesystem.rules.PredicateConstraint;
import jadex.rules.rulesystem.rules.TestCondition;
import jadex.rules.rulesystem.rules.Variable;
import jadex.rules.rulesystem.rules.functions.IFunction;
import jadex.rules.rulesystem.rules.functions.Identity;
import jadex.rules.rulesystem.rules.functions.MethodCallFunction;
import jadex.rules.rulesystem.rules.functions.OperatorFunction;
import jadex.rules.state.OAVAttributeType;
import jadex.rules.state.OAVJavaType;
import jadex.rules.state.OAVObjectType;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;


/**
 *  The constraint builder takes an expression
 *  (e.g. from the parser) and generates appropriate
 *  constraints and/or conditions for it.
 */
public class ConstraintBuilder
{
	/**
	 *  Build or adapt conditions for representing the given constraints.
	 *  @param expression	The expression, which contains the constraints to represent.
	 *  @param context	The build context.
	 *  @return The generated condition.
	 */
	public static ICondition	buildConstraints(Expression expression, BuildContext context, IParserHelper helper)
	{
		// Decompose expression into 1) object conditions, 2) collect conditions, 3) remaining constraints, 4) NOT conditions.
		List	conditions	= new ArrayList();
		List	nots	= new ArrayList();
		List	constraints	= new ArrayList();
		constraints.add(expression);
		for(int i=0; i1)
					{
						// Todo: when adding to next condition, dummy constraints should be first!?
						target	= (ConstrainableCondition)lcons.get(i+1);
					}
					else if(i>0)
					{
						target	= (ConstrainableCondition)lcons.get(i-1);
					}
					else
					{
						throw new UnsupportedOperationException("No object conditions produced (todo: test condition): "+expression);
					}
					
					lcons.remove(i);
					for(int j=0; j1 ? new AndCondition(lcons) : (ICondition)lcons.get(0);
	}
		
	/**
	 *  Build a constraint for a single expression.
	 */
	protected static void	buildConstraint(Expression exp, BuildContext context, boolean invert, IParserHelper helper)
	{
		if(exp instanceof OperationExpression)
		{
			OperationExpression	opex	= (OperationExpression)exp;
			if(opex.getOperator() instanceof IOperator)
			{
				IOperator	operator	= (IOperator)opex.getOperator();
				if(invert)
				{
					IOperator	inverse	= OperationExpression.getInverseOperator0(operator);
					if(inverse==null)
					{
						buildOperatorConstraint(exp, new LiteralExpression(Boolean.TRUE), IOperator.NOTEQUAL, context, helper);
					}
					else
					{
						buildOperatorConstraint(opex.getLeftValue(), opex.getRightValue(), inverse, context, helper);
					}
				}
				else
				{
					buildOperatorConstraint(opex.getLeftValue(), opex.getRightValue(), operator, context, helper);
				}
			}
//			else if(opex.getOperator() instanceof IFunction)
//			{
//				buildFunctionConstraint(opex.getLeftValue(), opex.getRightValue(), (IFunction)opex.getOperator(), context);
//			}
			else
			{
				throw new RuntimeException("Unexpected operator type: "+opex);
			}
		}
		else if(exp instanceof UnaryExpression)
		{
			UnaryExpression	unex	= (UnaryExpression)exp;
			if(unex.getOperator().equals(UnaryExpression.OPERATOR_NOT))
			{
				buildConstraint(unex.getValue(), context, !invert, helper);
			}
			else
			{
				throw new RuntimeException("Unexpected operator type: "+unex);
			}
		}
		// Conditional
		// Literal
		// Primary
		// Variable
		else
		{
			buildOperatorConstraint(exp, new LiteralExpression(Boolean.TRUE), invert ? IOperator.NOTEQUAL : IOperator.EQUAL, context, helper);
		}
	}

	/**
	 *  Build an operator constraint.
	 */
	protected static void	buildOperatorConstraint(Expression left, Expression right, IOperator op, BuildContext context, IParserHelper helper)
	{
		// Special treatment for unbound variables -> build test condition.
		// Currently not supported by Rete builder
		if(left instanceof VariableExpression && context.getConstrainableCondition0(((VariableExpression)left).getVariable())==null)
		{
			// Flatten right side to literal or (temporary) variable.
			right	= flattenToPrimary(right, context, helper);
			Object	rightsource	= right instanceof LiteralExpression
				? new Constant(((LiteralExpression)right).getValue())
				: ((VariableExpression)right).getVariable();
			
			// Build test condition for left side (variable).
			TestCondition	testcon	= new TestCondition(new PredicateConstraint(new FunctionCall(
				new OperatorFunction(op), new Object[]{((VariableExpression)left).getVariable(), rightsource})));
			
			context.getConditions().add(testcon);
		}

		// Normal treatment: Find object condition + value source for left part and attach constraint based on right part.
		else
		{
			// Get object condition and value source for left part.
			Object	valuesource	= getObjectConditionAndValueSource(left, context, helper);
	
			// Flatten right side to literal value or (temporary) variable.
			right	= flattenToPrimary(right, context, helper);
	
			// Build literal constraint for constant values
			if(right instanceof LiteralExpression)
			{
				// Right side of literal constraint is not a value source (i.e. Constant)
				context.getCurrentCondition().addConstraint(new LiteralConstraint(valuesource, ((LiteralExpression)right).getValue(), op));
			}
	
			// Build variable constraint for other expressions
			else //if(right instanceof VariableExpression)
			{
				context.getCurrentCondition().addConstraint(new BoundConstraint(valuesource, ((VariableExpression)right).getVariable(), op));
			}
			
			context.popCondition();
		}
	}
	
	/**
	 *  Find or create an object condition for a value and
	 *  return the appropriate value source.
	 *  The condition is pushed on the stack of the build context.
	 *  When the condition is no longer required (e.g. all current constraints added) it has to be popped from the stack (manually).
	 *  @param value	The value to be obtained.
	 *  @param lcons	The existing conditions.
	 *  @param bcons	The conditions for existing variables.
	 *  @param tmodel	The type model.
	 *  @return	The value source.
	 */
	protected static Object getObjectConditionAndValueSource(Expression value, BuildContext context, IParserHelper helper)
	{
		Object	valuesource;
		int	stacksize	= context.oconstack!=null ? context.oconstack.size() : 0;
		
		if(value instanceof VariableExpression)
		{
			context.pushCondition(context.getConstrainableCondition(((VariableExpression)value).getVariable()));
			valuesource	= context.getBoundConstraint(((VariableExpression)value).getVariable()).getValueSource();
		}
		else if(value instanceof PrimaryExpression)
		{
			Expression	prim	= ((PrimaryExpression)value).getPrefix();
			OAVObjectType	type;
						
			if(prim instanceof VariableExpression)
			{
				Variable	var	= ((VariableExpression)prim).getVariable();
				type	= var.getType();
				valuesource	= context.getBoundConstraint(var).getValueSource();
				context.pushCondition(context.getConstrainableCondition(var));
			}
			else if(prim instanceof LiteralExpression)
			{
				Constant	c	= new Constant(((LiteralExpression)prim).getValue());
				valuesource	= new FunctionCall(new Identity(), new Object[]{c});
				if(c.getValue()!=null)
					type	= context.getTypeModel().getJavaType(c.getValue().getClass());
				else
					type	= OAVJavaType.java_object_type;

				context.pushCondition(context.getDummyCondition());
			}
			else if(prim instanceof StaticMethodAccess)
			{
				context.pushCondition(context.getDummyCondition());

				StaticMethodAccess	sma	= (StaticMethodAccess)prim;
				MethodCall mc = createMethodCall(sma.getType(), sma.getName(), sma.getParameterValues(), context, helper);

				List	paramsources	= mc.getParameterSources();
				paramsources.add(0, new Constant(null));	// first param is object to be invoked (use null for static method).
				valuesource	= new FunctionCall(new MethodCallFunction(mc.getMethod()), paramsources);
				type	= context.getTypeModel().getJavaType(mc.getMethod().getReturnType());
			}
			else if(prim instanceof CastExpression)
			{
				type	= ((CastExpression)prim).getType();
				valuesource	= getObjectConditionAndValueSource(((CastExpression)prim).getValue(), context, helper);
				// Skip pop/push of same condition.
			}
			else if(prim instanceof CollectExpression)
			{
				CollectExpression	colex	= (CollectExpression)prim;
				BuildContext	newcon	= new BuildContext(context);
				ICondition	cond	= buildConstraints(colex.getExpression(), newcon, helper);
				CollectCondition	colco; 
				if(cond instanceof ObjectCondition)
				{
					colco	= new CollectCondition((ObjectCondition)cond);
				}
				else if(cond instanceof AndCondition)
				{
					colco	= new CollectCondition(((AndCondition)cond).getConditions(), null);
				}
				else
				{
					throw new RuntimeException("Cannot collect: "+cond);
				}
				context.addCondition(colco);
				context.pushCondition(colco);
				type	= OAVJavaType.java_collection_type;
				valuesource	= null;
			}
			else
			{
				throw new UnsupportedOperationException("Unsupported start of primary expression: "+prim);
			}
			
			List	suffs	= new ArrayList();
			Suffix[]	suffixes	= ((PrimaryExpression)value).getSuffixes();
			for(int i=0; i1)
		{
			System.out.println("Warning: Multiple matching methods found for: "+clazz.getName()+"."+name+SUtil.arrayToString(paramvalues));
		}
		MethodCall	mc	= new MethodCall(type, methods[matches[0]], params);
		return mc;
	}

	/**
	 *  Flatten a value to a primary value (literal or variable).
	 *  For a complex expression, additional conditions might be created
	 *  that bind the desired value in a new variable. 
	 *  @param value	The value to be obtained.
	 *  @param lcons	The existing conditions.
	 *  @param bcons	The conditions for existing variables.
	 *  @param tmodel	The type model.
	 *  @return	The primary value (i.e. variable or literal).
	 */
	protected static Expression	flattenToPrimary(Expression value, BuildContext context, IParserHelper helper)
	{
		Expression	ret;

		if(value instanceof VariableExpression)
		{
			ret	= value;
		}
		else if(value instanceof LiteralExpression)
		{
			ret	= value;
		}
		else if(value instanceof PrimaryExpression)
		{
			Object	valuesource	= getObjectConditionAndValueSource(value, context, helper);
			ret	= new VariableExpression(context.generateVariableBinding(context.getCurrentCondition(), valuesource));
			context.popCondition();
		}
		else if(value instanceof OperationExpression)
		{
			OperationExpression	opex	= (OperationExpression)value;
			IFunction	func;
			if(opex.getOperator() instanceof IFunction)
				func	= (IFunction)opex.getOperator();
			else
				func	= new OperatorFunction((IOperator)opex.getOperator());
			
			Object	left	= getObjectConditionAndValueSource(opex.getLeftValue(), context, helper);
			Object	right	= flattenToPrimary(opex.getRightValue(), context, helper);
			if(right instanceof VariableExpression)
			{
				right	= ((VariableExpression)right).getVariable();
			}
			else //if(right instanceof LiteralExpression)
			{
				right	= new Constant(((LiteralExpression)right).getValue());
			}

			Object	valuesource	= new FunctionCall(func, new Object[]{left, right});
			ret	= new VariableExpression(context.generateVariableBinding(context.getCurrentCondition(), valuesource));
			context.popCondition();
		}
		else if(value instanceof CastExpression)
		{
			Expression	prim	= flattenToPrimary(((CastExpression)value).getValue(), context, helper);
			if(prim instanceof VariableExpression)
			{
				VariableExpression	varex	= (VariableExpression)prim;
				ConstrainableCondition	ocon	= context.getConstrainableCondition(varex.getVariable());
				Object	valuesource	= context.getBoundConstraint(varex.getVariable()).getValueSource();
				OAVObjectType	type	= ((CastExpression)value).getType();
				ret	= new VariableExpression(context.generateVariableBinding(ocon, context.generateVariableName(), type, valuesource));
			}
			else
			{
				throw new UnsupportedOperationException("Todo: flatten expressions of type: "+value);
			}
		}
		else
		{
			throw new UnsupportedOperationException("Todo: flatten expressions of type: "+value);
		}
		
		return ret;
	}
	
	/**
	 *  Shuffle conditions and constraints, such that all variables are bound
	 *  before used.
	 *  @param lcons	The list of conditions (shuffled in place).
	 */
	protected static void	shuffle(BuildContext context)
	{
		List lcons	= context.getConditions();
		Set	boundvars	= new HashSet();	// Variables, which are bound and therefore can be used.
		if(context.getParent()!=null)
			boundvars.addAll(context.getParent().getBoundVariables());
		boolean	progress	= true;
		int	finished	= 0;
		
		while(progress)
		{
			progress	= false;
			int skipped	= 0;
			for(int i=finished; i




© 2015 - 2024 Weber Informatics LLC | Privacy Policy