package org.drools.chance.rule.builder; import org.drools.chance.factmodel.Imperfect; import org.drools.chance.common.ImperfectField; import org.drools.chance.core.util.IntHashMap; import org.drools.chance.reteoo.nodes.ChanceObjectTypeNode; import org.drools.compiler.DescrBuildError; import org.drools.lang.MVELDumper; import org.drools.lang.descr.*; import org.drools.rule.Declaration; import org.drools.rule.Pattern; import org.drools.rule.builder.PatternBuilder; import org.drools.rule.builder.RuleBuildContext; import org.drools.spi.InternalReadAccessor; import java.util.ArrayList; import java.util.List; public class ChanceRulePatternBuilder extends PatternBuilder { public ChanceRulePatternBuilder() { super(); } protected void processConstraintsAndBinds( final RuleBuildContext context, final PatternDescr patternDescr, final Pattern pattern ) { MVELDumper.MVELDumperContext mvelCtx = new MVELDumper.MVELDumperContext().setRuleContext(context); List constraints = patternDescr.getConstraint().getDescrs(); List<? extends BaseDescr> temp = new ArrayList<BaseDescr>( patternDescr.getConstraint().getDescrs() ); List<ConstraintConnectiveDescr> rootConstraints = new ArrayList<ConstraintConnectiveDescr>(); IntHashMap<Boolean> impFlags = new IntHashMap<Boolean>(); IntHashMap<Boolean> posFlags = new IntHashMap<Boolean>(); boolean hasImperfectConstraint = ( patternDescr.getAnnotation( Imperfect.class.getSimpleName() ) != null ); for ( BaseDescr b : temp ) { String expression; boolean isPositional = false; if ( b instanceof BindingDescr ) { BindingDescr bind = (BindingDescr) b; expression = bind.getVariable() + (bind.isUnification() ? " := " : " : ") + bind.getExpression(); } else if ( b instanceof ExprConstraintDescr ) { ExprConstraintDescr descr = (ExprConstraintDescr) b; expression = descr.getExpression(); isPositional = descr.getType() == ExprConstraintDescr.Type.POSITIONAL; } else { expression = b.getText(); } ConstraintConnectiveDescr result = parseExpression( context, patternDescr, b, expression ); boolean isImperfect = ChanceObjectTypeNode.isImperfect(pattern.getObjectType()) || analyzeConstraintConnective( result, context, pattern ); if ( isImperfect ) { hasImperfectConstraint = true; if ( ! isPositional && result != null ) { rootConstraints.add( result ); int index = constraints.indexOf( b ); int k = index; impFlags.put( k, true ); posFlags.put( k, false ); constraints.remove( index ); for ( BaseDescr sub : expand( result ) ) { constraints.add( index, sub ); ++k; impFlags.put( k, true ); posFlags.put( k, false ); } } } else { int index = constraints.indexOf( b ); impFlags.put( index, false ); rootConstraints.add( result ); if (! isPositional) { constraints.remove( index ); constraints.add( index, result ); } else { if ( result.getDescrs().get( 0 ) instanceof BindingDescr ) { constraints.remove( index ); constraints.add( index, result ); } } if ( ! isPositional ) { posFlags.put( index, false ); } else { posFlags.put( index, true ); } } } // Add the pattern-level "and" if ( hasImperfectConstraint ) { ConstraintConnectiveDescr root = new ConstraintConnectiveDescr( ConnectiveType.AND ); // mock "isA" to increase cardinality by 1 root.addDescr( new RelationalExprDescr( "isA", false, null, new AtomicExprDescr( "this" ), new AtomicExprDescr( patternDescr.getObjectType() ) ) ); for ( BaseDescr rootChild : rootConstraints ) { root.addDescr( rootChild ); } if ( patternDescr.getAnnotation( Imperfect.class.getSimpleName() ) != null ) { root.addAnnotation( patternDescr.getAnnotation( Imperfect.class.getSimpleName() ) ); } constraints.add( root ); impFlags.put( constraints.size() - 1, true ); posFlags.put( constraints.size() - 1, false ); } int index = 0; for ( BaseDescr b : patternDescr.getDescrs() ) { boolean isPositional = posFlags.get( index ); if ( b instanceof BindingDescr ) { // it is just a bind, so build it buildRuleBindings( context, patternDescr, pattern, (BindingDescr) b ); } else if ( b instanceof ConstraintConnectiveDescr ) { if ( impFlags.get( index ) == true ) { build(context, patternDescr, pattern, (ConstraintConnectiveDescr) b); } else { ConstraintConnectiveDescr result = (ConstraintConnectiveDescr) b; if ( result.getDescrs().size() == 1 && result.getDescrs().get( 0 ) instanceof BindingDescr ) { // it is just a bind, so build it buildRuleBindings( context, patternDescr, pattern, (BindingDescr) result.getDescrs().get( 0 ) ); } else { super.build(context, patternDescr, pattern, (ConstraintConnectiveDescr) b, mvelCtx); } } } else if ( isPositional ) { processPositional(context, patternDescr, pattern, (ExprConstraintDescr) b); } else { // need to build the actual constraint buildCcdDescr( context, patternDescr, pattern, b, new ConstraintConnectiveDescr( ), mvelCtx); } index++; } // combineConstraints(context, pattern); } protected void processDuplicateBindings( boolean isUnification, PatternDescr patternDescr, Pattern pattern, BaseDescr original, String leftExpression, String rightIdentifier, RuleBuildContext context ) { MVELDumper.MVELDumperContext mvelCtx = new MVELDumper.MVELDumperContext().setRuleContext(context); if ( isUnification ) { String expr = leftExpression + " == " + rightIdentifier; ConstraintConnectiveDescr result = parseExpression( context, patternDescr, patternDescr, expr ); BaseDescr constr = result.getDescrs().get( 0 ); buildCcdDescr( context, patternDescr, pattern, constr, result, mvelCtx); } else { // This declaration already exists, so throw an Exception context.addError(new DescrBuildError(context.getParentDescr(), patternDescr, null, "Duplicate declaration for variable '" + leftExpression + "' in the rule '" + context.getRule().getName() + "'")); } } private boolean analyzeConstraintConnective( BaseDescr descr, RuleBuildContext context, Pattern pattern ) { if ( descr instanceof ConstraintConnectiveDescr ) { ConstraintConnectiveDescr ccd = (ConstraintConnectiveDescr) descr; if ( ( (ConstraintConnectiveDescr) descr ).getAnnotation( Imperfect.class.getSimpleName() ) != null ) { return true; } for ( BaseDescr child : ccd.getDescrs() ) { if ( analyzeConstraintConnective( child, context, pattern ) ) { return true; } } } else if ( descr instanceof RelationalExprDescr ) { RelationalExprDescr rel = (RelationalExprDescr) descr; if ( ChanceOperators.isImperfect( rel.getOperator() ) ) { return true; } String left = rel.getLeft() instanceof BindingDescr ? ( (BindingDescr) rel.getLeft() ).getExpression() : rel.getLeft().toString(); InternalReadAccessor extractor = getFieldReadAccessor( context, rel, pattern.getObjectType(), left, null, false ); if ( extractor != null ) { if ( extractor.getExtractToClass().isAssignableFrom( ImperfectField.class ) ) { return true; } else { return false; } } if ( analyzeConstraintConnective( rel.getLeft(), context, pattern ) ) { return true; } if ( analyzeConstraintConnective( rel.getRight(), context, pattern ) ) { return true; } } else if ( descr instanceof AtomicExprDescr ) { AtomicExprDescr atom = (AtomicExprDescr) descr; if ( atom.isLiteral() ) { return false; } // TODO ? if ( pattern.getInnerDeclarations().get( atom.getExpression() ) != null ) { Declaration ref = pattern.getInnerDeclarations().get( atom.getExpression() ); return ref.getExtractor().getExtractToClass().isAssignableFrom( ImperfectField.class ); } return false; } else if ( descr instanceof BindingDescr ) { return false; } else { throw new UnsupportedOperationException( "Can't analyze " + descr.getClass() + "for imperfection" ); } return false; } protected void build( RuleBuildContext context, PatternDescr patternDescr, Pattern pattern, ConstraintConnectiveDescr descr ) { pattern.addConstraint( ((ChanceMVELConstraintBuilder) getConstraintBuilder( context )).buildOperatorConstraint( context, patternDescr, pattern, descr ) ); } private List<BaseDescr> expand( ConstraintConnectiveDescr d ) { int N = d.getDescrs().size(); List<BaseDescr> ret = new ArrayList<BaseDescr>(); for ( int j = 0; j < N; j++ ) { BaseDescr child = d.getDescrs().get(j); if ( child instanceof ConstraintConnectiveDescr ) { ret.addAll( 0, expand( (ConstraintConnectiveDescr) child) ); } else { ret.add( 0, child ); } } ret.add( 0, d ); return ret; } protected boolean addConstraintToPattern( final RuleBuildContext context, final Pattern pattern, final RelationalExprDescr relDescr, String expr, String value1, String value2, boolean isConstant) { InternalReadAccessor extractor = getFieldReadAccessor( context, relDescr, pattern.getObjectType(), value1, null, false ); if ( extractor == null ) { return false; // impossible to create extractor } boolean isOperatorImperfect = ChanceOperators.isImperfect( relDescr.getOperatorDescr().getOperator() ); if ( ! isOperatorImperfect ) { // operator works on crisp values. Any imperfect-field expression must be narrowed down to its crisp, certain value BaseDescr leftDescr = relDescr.getLeft(); BaseDescr rightDescr = relDescr.getRight(); boolean isLeftImperfect = ImperfectField.class.isAssignableFrom( extractor.getExtractToClass() ); boolean isRightImperfect = false; if ( rightDescr instanceof AtomicExprDescr ) { AtomicExprDescr right = ((AtomicExprDescr) relDescr.getRight()); String potentialVar = right.getExpression(); Declaration decl = context.getDeclarationResolver().getDeclaration(context.getRule(), potentialVar); if ( decl != null && decl.getExtractor() != null && ImperfectField.class.isAssignableFrom( decl.getExtractor().getExtractToClass() ) ) { isRightImperfect = true; } } if ( leftDescr instanceof AtomicExprDescr ) { AtomicExprDescr left = ((AtomicExprDescr) leftDescr ); if ( isLeftImperfect ) { value1 = left.getExpression() + ".getCrisp()"; left.setExpression( value1 ); } expr = ( (AtomicExprDescr) leftDescr ).getExpression(); } else if ( leftDescr instanceof BindingDescr ) { BindingDescr left = ((BindingDescr) leftDescr); if ( isLeftImperfect ) { value1 = left.getExpression() + ".getCrisp()"; left.setExpression( value1 ); } expr = ((BindingDescr) leftDescr).getExpression(); } expr += " " + relDescr.getOperator() + " "; if ( rightDescr instanceof AtomicExprDescr ) { AtomicExprDescr right = ((AtomicExprDescr) rightDescr ); if ( isRightImperfect ) { right.setExpression( right.getExpression() + ".getCrisp()" ); } expr += right.getExpression(); } else { throw new UnsupportedOperationException( "ChanceRulePatternBuilder can't process right expressions of this type yet " + rightDescr ); } extractor = getFieldReadAccessor( context, relDescr, pattern.getObjectType(), value1, null, false ); if ( extractor == null ) { return false; // impossible to create extractor } } return super.addConstraintToPattern( context, pattern, relDescr, expr, value1, value2, isConstant, extractor ); } }