package org.drools.chance.rule.builder; import org.drools.base.ClassObjectType; import org.drools.base.DroolsQuery; import org.drools.base.EvaluatorWrapper; import org.drools.base.ValueType; import org.drools.base.evaluators.EvaluatorDefinition; import org.drools.base.evaluators.Operator; import org.drools.base.mvel.MVELCompilationUnit; import org.drools.chance.factmodel.Imperfect; import org.drools.chance.common.ChanceStrategyFactory; import org.drools.chance.rule.constraint.core.connectives.ConnectiveCore; import org.drools.chance.rule.constraint.core.connectives.ConnectiveFactory; import org.drools.chance.rule.constraint.core.connectives.impl.MvlFamilies; import org.drools.chance.rule.constraint.core.evaluators.ImperfectEvaluator; import org.drools.chance.degree.Degree; import org.drools.chance.degree.DegreeType; import org.drools.chance.distribution.ImpKind; import org.drools.chance.distribution.ImpType; import org.drools.chance.rule.constraint.ImperfectEvaluatorConstraint; import org.drools.chance.rule.constraint.ImperfectMvelConstraint; import org.drools.chance.rule.constraint.OperatorConstraint; import org.drools.chance.rule.constraint.core.evaluators.ImperfectEvaluatorWrapper; import org.drools.chance.rule.constraint.core.evaluators.ImperfectMvelEvaluator; import org.drools.compiler.AnalysisResult; import org.drools.compiler.DescrBuildError; import org.drools.core.util.index.IndexUtil; import org.drools.lang.descr.*; import org.drools.rule.*; import org.drools.rule.builder.MVELConstraintBuilder; import org.drools.rule.builder.RuleBuildContext; import org.drools.rule.builder.dialect.mvel.MVELAnalysisResult; import org.drools.rule.builder.dialect.mvel.MVELDialect; import org.drools.rule.constraint.EvaluatorConstraint; import org.drools.rule.constraint.MvelConstraint; import org.drools.spi.*; import org.drools.spi.Restriction; import java.util.Date; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import static org.drools.rule.builder.dialect.DialectUtil.copyErrorLocation; public class ChanceMVELConstraintBuilder extends MVELConstraintBuilder { public boolean isMvelOperator( String operator ) { return mvelOperators.contains( operator ) || ChanceOperators.lookup( operator ); } public MVELCompilationUnit buildCompilationUnit( final RuleBuildContext context, final Declaration[] previousDeclarations, final Declaration[] localDeclarations, final PredicateDescr predicateDescr, final AnalysisResult analysis ) { if (context.isTypesafe() && analysis instanceof MVELAnalysisResult) { Class<?> returnClass = ((MVELAnalysisResult)analysis).getReturnType(); if ( ! returnClass.isAssignableFrom( Degree.class ) && returnClass != Boolean.class && returnClass != Boolean.TYPE) { context.addError( new DescrBuildError( context.getParentDescr(), predicateDescr, null, "Predicate '" + predicateDescr.getContent() + "' must be a (generalized) Boolean expression\n" + predicateDescr.positionAsString() ) ); } } MVELDialect dialect = (MVELDialect) context.getDialect( context.getDialect().getId() ); MVELCompilationUnit unit = null; try { Map<String, Class< ? >> declIds = context.getDeclarationResolver().getDeclarationClasses( context.getRule() ); Pattern p = (Pattern) context.getBuildStack().peek(); if ( p.getObjectType() instanceof ClassObjectType) { declIds.put( "this", ((ClassObjectType) p.getObjectType()).getClassType() ); } unit = dialect.getMVELCompilationUnit( (String) predicateDescr.getContent(), analysis, previousDeclarations, localDeclarations, null, context, "drools", KnowledgeHelper.class ); } catch ( final Exception e ) { copyErrorLocation(e, predicateDescr); context.addError( new DescrBuildError( context.getParentDescr(), predicateDescr, e, "Unable to build expression for 'inline-eval' : " + e.getMessage() + "'" + predicateDescr.getContent() + "'\n" + e.getMessage() ) ); } return unit; } public Constraint buildLiteralConstraint(RuleBuildContext context, Pattern pattern, ValueType vtype, FieldValue field, String expression, String leftValue, String operator, String rightValue, InternalReadAccessor extractor, LiteralRestrictionDescr restrictionDescr) { boolean isImperfect = ChanceOperators.isImperfect( operator ); if (USE_MVEL_EXPRESSION) { if ( ! isMvelOperator( operator ) ) { // custom or complex operator Evaluator evaluator = buildLiteralEvaluator( context, extractor, restrictionDescr, vtype ); if ( isImperfect ) { if ( evaluator instanceof ImperfectEvaluator ) { // imperfect evaluator, to be used imperfectly ImperfectEvaluatorConstraint iec = new ImperfectEvaluatorConstraint( field, evaluator, extractor ); iec.setLabel( extractConstraintLabel(restrictionDescr.getParameters()) ); return iec; } else { // imperfect eval can work in a single return new EvaluatorConstraint( field, evaluator, extractor ); } } else { if ( evaluator instanceof ImperfectEvaluator ) { //TODO // imperfect evaluator, coerce results into boolean return new EvaluatorConstraint( field, evaluator, extractor ); } else { // standard evaluator called in a standard way return new EvaluatorConstraint( field, evaluator, extractor ); } } } else { if ( isImperfect ) { ImperfectMvelEvaluator evaluator = new ImperfectMvelEvaluator( vtype, Operator.determineOperator( ChanceOperators.makePerfect( operator ), false ), restrictionDescr.getParameters(), true, rightValue ); ImperfectEvaluatorConstraint iec = new ImperfectEvaluatorConstraint( field, evaluator, extractor ); if ( restrictionDescr.getParameters() != null && restrictionDescr.getParameters().contains( "cut" ) ) { iec.setCutting( true ); } iec.setLabel( extractConstraintLabel( restrictionDescr.getParameters()) ); return iec; } else { String mvelExpr = normalizeMVELLiteralExpression(vtype, field, expression, leftValue, operator, rightValue, restrictionDescr); IndexUtil.ConstraintType constraintType = IndexUtil.ConstraintType.decode(operator); MVELCompilationUnit compilationUnit = buildCompilationUnit( context, pattern, mvelExpr, null ); return new MvelConstraint( context.getPkg().getName(), mvelExpr, compilationUnit, constraintType, field, extractor ); } } } else { throw new UnsupportedOperationException( "Chance Constraint Builder does not support legacy constraints " + expression ); } } private String extractConstraintLabel( List<String> parameters ) { if ( parameters == null || parameters.size() == 0 ) { return null; } for ( String keyVal : parameters ) { StringTokenizer tok = new StringTokenizer( keyVal, "=" ); String key = tok.nextToken().trim(); if ( "label".equals( key ) && tok.hasMoreTokens() ) { return tok.nextToken().trim(); } } return null; } public Constraint buildVariableConstraint(RuleBuildContext context, Pattern pattern, String expression, Declaration[] declarations, String leftValue, OperatorDescr operatorDescr, String rightValue, InternalReadAccessor extractor, Declaration requiredDeclaration, RelationalExprDescr relDescr ) { boolean isImperfect = ChanceOperators.isImperfect( operatorDescr.getOperator() ); if ( ! isMvelOperator( operatorDescr.getOperator() ) ) { EvaluatorDefinition.Target right = getRightTarget( extractor ); EvaluatorDefinition.Target left = (requiredDeclaration.isPatternDeclaration() && !(Date.class.isAssignableFrom( requiredDeclaration.getExtractor().getExtractToClass() ) || Number.class.isAssignableFrom( requiredDeclaration.getExtractor().getExtractToClass() ))) ? EvaluatorDefinition.Target.HANDLE : EvaluatorDefinition.Target.FACT; final Evaluator evaluator = getEvaluator( context, relDescr, extractor.getValueType(), operatorDescr.getOperator(), relDescr.isNegated(), relDescr.getParametersText(), left, right ); if ( isImperfect ) { if ( evaluator instanceof ImperfectEvaluator) { ImperfectEvaluatorConstraint iec = new ImperfectEvaluatorConstraint( new Declaration[] { requiredDeclaration }, evaluator, extractor ); iec.setLabel( extractConstraintLabel( operatorDescr.getParameters()) ); return iec; } else { return new EvaluatorConstraint( new Declaration[] { requiredDeclaration }, evaluator, extractor ); } } else { return new EvaluatorConstraint( new Declaration[] { requiredDeclaration }, evaluator, extractor ); } } else { if ( isImperfect ) { EvaluatorDefinition.Target right = getRightTarget( extractor ); EvaluatorDefinition.Target left = (requiredDeclaration.isPatternDeclaration() && !(Date.class.isAssignableFrom( requiredDeclaration.getExtractor().getExtractToClass() ) || Number.class.isAssignableFrom( requiredDeclaration.getExtractor().getExtractToClass() ))) ? EvaluatorDefinition.Target.HANDLE : EvaluatorDefinition.Target.FACT; final Evaluator evaluator = getEvaluator( context, relDescr, extractor.getValueType(), operatorDescr.getOperator(), relDescr.isNegated(), relDescr.getParametersText(), left, right ); ImperfectEvaluatorConstraint iec = new ImperfectEvaluatorConstraint( new Declaration[] { requiredDeclaration }, evaluator, extractor ); iec.setLabel( extractConstraintLabel( operatorDescr.getParameters()) ); return iec; } else { boolean isUnification = requiredDeclaration != null && requiredDeclaration.getPattern().getObjectType().equals( new ClassObjectType( DroolsQuery.class ) ) && Operator.EQUAL.getOperatorString().equals( operatorDescr.getOperator() ); if (isUnification) { expression = resolveUnificationAmbiguity(expression, declarations, leftValue, rightValue); } IndexUtil.ConstraintType constraintType = IndexUtil.ConstraintType.decode( operatorDescr.getOperator() ); MVELCompilationUnit compilationUnit = isUnification ? null : buildCompilationUnit( context, pattern, expression, null ); return new MvelConstraint(context.getPkg().getName(), expression, declarations, compilationUnit, constraintType, requiredDeclaration, extractor, isUnification); } } } // // if (USE_MVEL_EXPRESSION) { // // if ( !isMvelOperator( operatorDescr.getOperator() ) ) { // EvaluatorDefinition.Target right = getRightTarget( extractor ); // EvaluatorDefinition.Target left = (requiredDeclaration.isPatternDeclaration() && !(Date.class.isAssignableFrom( requiredDeclaration.getExtractor().getExtractToClass() ) || Number.class.isAssignableFrom( requiredDeclaration.getExtractor().getExtractToClass() ))) ? EvaluatorDefinition.Target.HANDLE : EvaluatorDefinition.Target.FACT; // final Evaluator evaluator = getEvaluator(context, // relDescr, // extractor.getValueType(), // operatorDescr.getOperator(), // relDescr.isNegated(), // relDescr.getParametersText(), // left, // right); // if ( isImperfect ) { // if ( evaluator instanceof ImperfectEvaluator) { // ImperfectEvaluatorConstraint iec = new ImperfectEvaluatorConstraint( requiredDeclaration, evaluator, extractor ); // iec.setLabel( extractConstraintLabel( operatorDescr.getParameters()) ); // return iec; // } else { // return new EvaluatorConstraint( restriction.getRequiredDeclarations(), evaluator, extractor ); // } // } else { // return new EvaluatorConstraint( restriction.getRequiredDeclarations(), evaluator, extractor ); // } // } else { // if ( isImperfect ) { // // Evaluator evaluator = restriction.getEvaluator(); // // ImperfectEvaluatorConstraint iec = new ImperfectEvaluatorConstraint( restriction.getRequiredDeclarations(), evaluator, extractor ); // iec.setLabel( extractConstraintLabel( operatorDescr.getParameters()) ); // return iec; // // } else { // // boolean isUnification = requiredDeclaration != null && requiredDeclaration.getPattern().getObjectType().equals( new ClassObjectType( DroolsQuery.class ) ) && Operator.EQUAL.getOperatorString().equals( operatorDescr.getOperator() ); // if (isUnification) { // expression = resolveUnificationAmbiguity(expression, declarations, leftValue, rightValue); // } // // boolean isIndexable = operatorDescr.getOperator().equals("=="); // MVELCompilationUnit compilationUnit = isUnification ? null : buildCompilationUnit(context, pattern, expression); // return new MvelConstraint(context.getPkg().getName(), expression, declarations, compilationUnit, isIndexable, getIndexingDeclaration(restriction), extractor, isUnification); // // } public Constraint buildMvelConstraint(String packageName, String expression, Declaration[] declarations, MVELCompilationUnit compilationUnit, boolean isDynamic) { return new ImperfectMvelConstraint( packageName, expression, declarations, compilationUnit, isDynamic ); } public Constraint buildMvelConstraint(String packageName, String expression, Declaration[] declarations, MVELCompilationUnit compilationUnit, boolean isDynamic, PredicateDescr base ) { ImperfectMvelConstraint imc = new ImperfectMvelConstraint( packageName, expression, declarations, compilationUnit, isDynamic ); imc.setLabel( extractConstraintLabel( base.getParameters() ) ); return imc; } public EvaluatorWrapper wrapEvaluator(Evaluator evaluator, Declaration left, Declaration right) { return new ImperfectEvaluatorWrapper( evaluator, left, right ); } protected Constraint buildOperatorConstraint( RuleBuildContext context, PatternDescr patternDescr, Pattern pattern, ConstraintConnectiveDescr ccd ) { ImpType type = null; ImpKind kind = null; DegreeType degree = null; MvlFamilies family = null; String label = null; if ( ccd.getAnnotation( Imperfect.class.getSimpleName() ) != null ) { AnnotationDescr ann = ccd.getAnnotation( Imperfect.class.getSimpleName() ); type = ImpType.parse( ann.getValue( ImpType.name ) ); kind = ImpKind.parse( ann.getValue( ImpKind.name ) ); degree = DegreeType.parse( ann.getValue( DegreeType.name ) ); family = MvlFamilies.parse( ann.getValue( MvlFamilies.name ) ); label = ann.getValue( "label" ); } ConnectiveFactory factory = ChanceStrategyFactory.getConnectiveFactory(kind, type); ConnectiveCore conn = null; switch ( ccd.getConnective() ) { case INC_AND: case AND: conn = family != null ? factory.getAnd( family.value() ) : factory.getAnd(); break; case INC_OR: case OR: conn = family != null ? factory.getOr( family.value() ) : factory.getOr(); break; case XOR: conn = family != null ? factory.getXor( family.value() ) : factory.getXor(); break; default: throw new IllegalStateException( "Unable to find connective for " + ccd.getConnective() ); } int effectiveArity = ccd.getDescrs().size(); for ( BaseDescr child : ccd.getDescrs() ) { if ( child instanceof ConstraintConnectiveDescr ) { ConstraintConnectiveDescr cccd = (ConstraintConnectiveDescr) child; if ( cccd.getDescrs().size() == 1 && cccd.getDescrs().get( 0 ) instanceof BindingDescr ) { effectiveArity--; } } } // return new OperatorConstraint( ccd.getDescrs().size(), conn ); return new OperatorConstraint( effectiveArity, conn, label ); } }