Skip to content

Commit

Permalink
[JBRULES-3263] implementing variable constraints with mvel
Browse files Browse the repository at this point in the history
  • Loading branch information
mariofusco committed Dec 16, 2011
1 parent b321385 commit 41ca700
Show file tree
Hide file tree
Showing 26 changed files with 500 additions and 120 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@
import org.drools.rule.LiteralConstraint;
import org.drools.rule.LiteralRestriction;
import org.drools.rule.MVELDialectRuntimeData;
import org.drools.rule.ReturnValueRestriction;
import org.drools.rule.UnificationRestriction;
import org.drools.rule.VariableConstraint;
import org.drools.rule.constraint.BooleanConversionHandler;
import org.drools.rule.constraint.EvaluatorConstraint;
import org.drools.rule.constraint.MvelConstraint;
import org.drools.spi.Constraint;
import org.drools.spi.Evaluator;
Expand All @@ -21,33 +24,78 @@
import org.mvel2.ParserConfiguration;

import java.util.Date;
import java.util.HashSet;
import java.util.Set;

public class ConstraintBuilder {

private static final boolean USE_MVEL_EXPRESSION = false;
private static final boolean USE_MVEL_EXPRESSION = true;
private static Set<String> mvelOperators;

static {
if (USE_MVEL_EXPRESSION) {
DataConversion.addConversionHandler(Boolean.class, BooleanConversionHandler.INSTANCE);
DataConversion.addConversionHandler(boolean.class, BooleanConversionHandler.INSTANCE);

mvelOperators = new HashSet<String>() {{
add("==");
add("!=");
add(">");
add(">=");
add("<");
add("<=");
add("str");
add("contains");
add("matches");
add("excludes");
add("memberOf");
}};
}
}

private static boolean isMvelOperator(String operator) {
return mvelOperators.contains(operator);
}

public static Constraint buildVariableConstraint(RuleBuildContext context,
String expr,
Declaration[] declrations,
String leftValue,
String operator,
String rightValue,
InternalReadAccessor extractor,
Restriction restriction) {
if (USE_MVEL_EXPRESSION) {
if (!isMvelOperator(operator)) {
return new EvaluatorConstraint(restriction.getRequiredDeclarations(), restriction.getEvaluator(), extractor);
}

String packageName = context.getPkg().getName();
ParserConfiguration conf = getParserConfiguration(context);
return new MvelConstraint(conf, packageName, expr, operator, declrations, extractor);

// resolve ambiguity between variable and bound value with the same name in unifications
if (restriction instanceof UnificationRestriction && leftValue.equals(rightValue)) {
rightValue = rightValue + "__";
for (Declaration declaration : declrations) {
if (declaration.getIdentifier().equals(leftValue)) {
declaration.setBindingName(rightValue);
}
}
expr = leftValue + " == " + rightValue;
}

return new MvelConstraint(conf, packageName, expr, operator, declrations, getIndexingDeclaration(restriction), extractor);
} else {
return new VariableConstraint(extractor, restriction);
}
}

private static Declaration getIndexingDeclaration(Restriction restriction) {
if (restriction instanceof ReturnValueRestriction) return null;
Declaration[] declarations = restriction.getRequiredDeclarations();
return declarations != null && declarations.length > 0 ? declarations[0] : null;
}

public static Constraint buildLiteralConstraint(RuleBuildContext context,
ValueType vtype,
FieldValue field,
Expand All @@ -58,6 +106,11 @@ public static Constraint buildLiteralConstraint(RuleBuildContext context,
InternalReadAccessor extractor,
LiteralRestrictionDescr restrictionDescr) {
if (USE_MVEL_EXPRESSION) {
if (!isMvelOperator(operator)) {
Evaluator evaluator = buildLiteralEvaluator(context, extractor, restrictionDescr, field, vtype);
return new EvaluatorConstraint(field, evaluator, extractor);
}

return buildMVELConstraint(context, vtype, field, expr, value1, operator, value2, restrictionDescr);
} else {
LiteralRestriction restriction = buildLiteralRestriction(context, extractor, restrictionDescr, field, vtype);
Expand All @@ -79,23 +132,23 @@ private static Constraint buildMVELConstraint(RuleBuildContext context,
String packageName = context.getPkg().getName();
ParserConfiguration conf = getParserConfiguration(context);

String mvelExpr = normalizeMVELExpression(vtype, field, expr, leftValue, operator, rightValue, restrictionDescr);
String mvelExpr = normalizeMVELLiteralExpression(vtype, field, expr, leftValue, operator, rightValue, restrictionDescr);

return new MvelConstraint(conf, packageName, mvelExpr, operator, null, null);
return new MvelConstraint(conf, packageName, mvelExpr, operator);
}

private static ParserConfiguration getParserConfiguration(RuleBuildContext context) {
MVELDialectRuntimeData data = (MVELDialectRuntimeData) context.getPkg().getDialectRuntimeRegistry().getDialectData( "mvel" );
return data.getParserConfiguration();
}

private static String normalizeMVELExpression(ValueType vtype,
FieldValue field,
String expr,
String leftValue,
String operator,
String rightValue,
LiteralRestrictionDescr restrictionDescr) {
private static String normalizeMVELLiteralExpression(ValueType vtype,
FieldValue field,
String expr,
String leftValue,
String operator,
String rightValue,
LiteralRestrictionDescr restrictionDescr) {
if (vtype == ValueType.DATE_TYPE) {
Date date = (Date)field.getValue();
return leftValue + " " + operator + (date != null ? " new java.util.Date(" + date.getTime() + ")" : " null");
Expand All @@ -107,37 +160,38 @@ private static String normalizeMVELExpression(ValueType vtype,
}
return (restrictionDescr.isNegated() ? "!" : "") + leftValue + "." + method + "(" + rightValue + ")";
}
if (operator.equals("soundslike")) {
return "if (" + leftValue + " == null || " + rightValue + " == null) return false;\n" +
"String soundex1 = soundex(" + leftValue + ");\n" +
"if (soundex1 == null) return false;\n" +
"return soundex1.equals(soundex(" + rightValue + "));";

// resolve ambiguity between mvel's "empty" keyword and constraints like: List(empty == ...)
if (expr.startsWith("empty") && (operator.equals("==") || operator.equals("!="))) {
expr = "isEmpty()" + expr.substring(5);
}
return expr;
}

public static LiteralRestriction buildLiteralRestriction( final RuleBuildContext context,
final InternalReadAccessor extractor,
final LiteralRestrictionDescr literalRestrictionDescr,
final FieldValue field,
final ValueType vtype) {
public static LiteralRestriction buildLiteralRestriction( RuleBuildContext context,
InternalReadAccessor extractor,
LiteralRestrictionDescr literalRestrictionDescr,
FieldValue field,
ValueType vtype) {
Evaluator evaluator = buildLiteralEvaluator(context, extractor, literalRestrictionDescr, field, vtype);
return evaluator == null ? null : new LiteralRestriction(field, evaluator, extractor);
}

public static Evaluator buildLiteralEvaluator( RuleBuildContext context,
InternalReadAccessor extractor,
LiteralRestrictionDescr literalRestrictionDescr,
FieldValue field,
ValueType vtype) {
EvaluatorDefinition.Target right = getRightTarget( extractor );
EvaluatorDefinition.Target left = EvaluatorDefinition.Target.FACT;
final Evaluator evaluator = getEvaluator( context,
literalRestrictionDescr,
vtype,
literalRestrictionDescr.getEvaluator(),
literalRestrictionDescr.isNegated(),
literalRestrictionDescr.getParameterText(),
left,
right );
if ( evaluator == null ) {
return null;
}

return new LiteralRestriction( field,
evaluator,
extractor );
return getEvaluator( context,
literalRestrictionDescr,
vtype,
literalRestrictionDescr.getEvaluator(),
literalRestrictionDescr.isNegated(),
literalRestrictionDescr.getParameterText(),
left,
right );
}

public static EvaluatorDefinition.Target getRightTarget( final InternalReadAccessor extractor ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -784,7 +784,7 @@ private boolean addConstraintToPattern( final RuleBuildContext context,
}
*/
}
pattern.addConstraint(buildVariableConstraint(context, expr, declarations, operator, extractor, restriction));
pattern.addConstraint(buildVariableConstraint(context, expr, declarations, value1, operator, value2, extractor, restriction));
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
import org.drools.spi.Activation;
import org.drools.spi.AgendaGroup;
import org.drools.spi.CompiledInvoker;
import org.drools.spi.Constraint;
import org.drools.spi.PropagationContext;
import org.drools.util.ClassLoaderUtil;
import org.drools.util.CompositeClassLoader;
Expand Down Expand Up @@ -394,23 +395,23 @@ public void testReturnValueMethodCompare() {
fail( builder1.getErrors().toString() );
}
final Pattern pattern1 = (Pattern) builder1.getPackage().getRules()[0].getLhs().getChildren().get( 0 );
final VariableConstraint returnValue1 = (VariableConstraint) pattern1.getConstraints().get( 0 );
final Constraint returnValue1 = pattern1.getConstraints().get( 0 );

final PackageBuilder builder2 = new PackageBuilder();
final PackageDescr packageDescr2 = new PackageDescr( "package2" );
createReturnValueRule( packageDescr2,
" x + y " );
builder2.addPackage( packageDescr2 );
final Pattern pattern2 = (Pattern) builder2.getPackage().getRules()[0].getLhs().getChildren().get( 0 );
final VariableConstraint returnValue2 = (VariableConstraint) pattern2.getConstraints().get( 0 );
final Constraint returnValue2 = pattern2.getConstraints().get( 0 );

final PackageBuilder builder3 = new PackageBuilder();
final PackageDescr packageDescr3 = new PackageDescr( "package3" );
createReturnValueRule( packageDescr3,
" x - y " );
builder3.addPackage( packageDescr3 );
final Pattern pattern3 = (Pattern) builder3.getPackage().getRules()[0].getLhs().getChildren().get( 0 );
final VariableConstraint returnValue3 = (VariableConstraint) pattern3.getConstraints().get( 0 );
final Constraint returnValue3 = pattern3.getConstraints().get( 0 );

assertEquals( returnValue1,
returnValue2 );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.drools.reteoo.LeftInputAdapterNode;
import org.drools.reteoo.ObjectTypeNode;
import org.drools.reteoo.ReteooWorkingMemory;
import org.drools.rule.IndexableConstraint;
import org.drools.rule.PredicateConstraint;
import org.drools.rule.VariableConstraint;
import org.junit.Test;
Expand Down Expand Up @@ -99,28 +100,28 @@ public void testBuildsIndexedMemory() {
JoinNode j11 = ( JoinNode ) j10.getSinkPropagator().getSinks()[0]; // $p11

SingleBetaConstraints c = ( SingleBetaConstraints ) j2.getRawConstraints();
assertEquals( "$name", c.getConstraint().getRequiredDeclarations()[0].getIdentifier() );
assertEquals( "$name", ((IndexableConstraint)c.getConstraint()).getIndexingDeclaration().getIdentifier() );
assertTrue( c.isIndexed() );
BetaMemory bm = ( BetaMemory ) wm.getNodeMemory( j2 );
assertTrue( bm.getLeftTupleMemory() instanceof LeftTupleIndexHashTable );
assertTrue( bm.getRightTupleMemory() instanceof RightTupleIndexHashTable );

c = ( SingleBetaConstraints ) j3.getRawConstraints();
// assertEquals( "name", c.getConstraint().getRequiredDeclarations()[0].getIdentifier() );
assertEquals( "name", ((IndexableConstraint)c.getConstraint()).getIndexingDeclaration().getIdentifier() );
assertTrue( c.isIndexed() );
bm = ( BetaMemory ) wm.getNodeMemory( j3 );
assertTrue( bm.getLeftTupleMemory() instanceof LeftTupleIndexHashTable );
assertTrue( bm.getRightTupleMemory() instanceof RightTupleIndexHashTable );

c = ( SingleBetaConstraints ) j4.getRawConstraints();
assertEquals( "$p1", c.getConstraint().getRequiredDeclarations()[0].getIdentifier() );
assertEquals("$p1", c.getConstraint().getRequiredDeclarations()[0].getIdentifier());
assertFalse( c.isIndexed() );
bm = ( BetaMemory ) wm.getNodeMemory( j4 );
assertTrue( bm.getLeftTupleMemory() instanceof LeftTupleList );
assertTrue( bm.getRightTupleMemory() instanceof RightTupleList );

c = ( SingleBetaConstraints ) j5.getRawConstraints();
assertEquals( "name", c.getConstraint().getRequiredDeclarations()[0].getIdentifier() );
assertEquals("name", ((IndexableConstraint)c.getConstraint()).getIndexingDeclaration().getIdentifier());
assertTrue( c.isIndexed() );
bm = ( BetaMemory ) wm.getNodeMemory( j5 );
assertTrue( bm.getLeftTupleMemory() instanceof LeftTupleIndexHashTable );
Expand All @@ -135,35 +136,35 @@ public void testBuildsIndexedMemory() {
// assertTrue( bm.getRightTupleMemory() instanceof RightTupleList );

c = ( SingleBetaConstraints ) j7.getRawConstraints();
assertEquals( "name", c.getConstraint().getRequiredDeclarations()[0].getIdentifier() );
assertEquals("name", ((IndexableConstraint)c.getConstraint()).getIndexingDeclaration().getIdentifier());
assertTrue( c.isIndexed() );
bm = ( BetaMemory ) wm.getNodeMemory( j7 );
assertTrue( bm.getLeftTupleMemory() instanceof LeftTupleIndexHashTable );
assertTrue( bm.getRightTupleMemory() instanceof RightTupleIndexHashTable );

c = ( SingleBetaConstraints ) j8.getRawConstraints();
assertEquals( "name", c.getConstraint().getRequiredDeclarations()[0].getIdentifier() );
assertEquals("name", ((IndexableConstraint)c.getConstraint()).getIndexingDeclaration().getIdentifier());
assertTrue( c.isIndexed() );
bm = ( BetaMemory ) wm.getNodeMemory( j8 );
assertTrue( bm.getLeftTupleMemory() instanceof LeftTupleIndexHashTable );
assertTrue( bm.getRightTupleMemory() instanceof RightTupleIndexHashTable );

c = ( SingleBetaConstraints ) j9.getRawConstraints();
assertEquals( "$p1", c.getConstraint().getRequiredDeclarations()[0].getIdentifier() );
assertEquals("$p1", c.getConstraint().getRequiredDeclarations()[0].getIdentifier());
assertFalse( c.isIndexed() );
bm = ( BetaMemory ) wm.getNodeMemory( j9 );
assertTrue( bm.getLeftTupleMemory() instanceof LeftTupleList );
assertTrue( bm.getRightTupleMemory() instanceof RightTupleList );

c = ( SingleBetaConstraints ) j10.getRawConstraints();
assertEquals( "name", c.getConstraint().getRequiredDeclarations()[0].getIdentifier() );
assertEquals("name", ((IndexableConstraint)c.getConstraint()).getIndexingDeclaration().getIdentifier());
assertTrue( c.isIndexed() );
bm = ( BetaMemory ) wm.getNodeMemory( j10 );
assertTrue( bm.getLeftTupleMemory() instanceof LeftTupleIndexHashTable );
assertTrue( bm.getRightTupleMemory() instanceof RightTupleIndexHashTable );

c = ( SingleBetaConstraints ) j11.getRawConstraints();
assertEquals( "$p1", c.getConstraint().getRequiredDeclarations()[0].getIdentifier() );
assertEquals("$p1", c.getConstraint().getRequiredDeclarations()[0].getIdentifier());
assertFalse( c.isIndexed() );
bm = ( BetaMemory ) wm.getNodeMemory( j11 );
assertTrue( bm.getLeftTupleMemory() instanceof LeftTupleList );
Expand Down
Loading

0 comments on commit 41ca700

Please sign in to comment.