@Test public void testDefaultConsequenceWithMultipleNamedConsequenceCompilation() { String defaultCon = " System.out.println(\"this is a test\" + $cheese);\n "; Map<String, Object> namedConsequences = new HashMap<String, Object>(); String name1 = " System.out.println(\"this is a test name1\" + $cheese);\n "; namedConsequences.put("name1", name1); String name2 = " System.out.println(\"this is a test name2\" + $cheese);\n "; namedConsequences.put("name2", name2); setupTest(defaultCon, namedConsequences); assertEquals(2, context.getRule().getNamedConsequences().size()); assertTrue(context.getRule().getConsequence() instanceof MVELConsequence); assertTrue(context.getRule().getNamedConsequences().get("name1") instanceof MVELConsequence); assertTrue(context.getRule().getNamedConsequences().get("name2") instanceof MVELConsequence); assertNotSame( context.getRule().getConsequence(), context.getRule().getNamedConsequences().get("name1")); assertNotSame( context.getRule().getConsequence(), context.getRule().getNamedConsequences().get("name2")); assertNotSame( context.getRule().getNamedConsequences().get("name1"), context.getRule().getNamedConsequences().get("name2")); }
@Test public void testDefaultConsequenceCompilation() { String consequence = " System.out.println(\"this is a test:\" + $cheese);\n " + "c1 = new Cheese().{ type = $cheese.type };" + "c2 = new Cheese().{ type = $map[$cheese.type] };" + "c3 = new Cheese().{ type = $map['key'] };"; setupTest(consequence, new HashMap<String, Object>()); assertNotNull(context.getRule().getConsequence()); assertFalse(context.getRule().hasNamedConsequences()); assertTrue(context.getRule().getConsequence() instanceof MVELConsequence); }
@Test public void testMVELDebugSymbols() throws DroolsParserException { MVELDebugHandler.setDebugMode(true); try { final DrlParser parser = new DrlParser(LanguageLevelOption.DRL5); final PackageDescr pkgDescr = parser.parse(new InputStreamReader(getClass().getResourceAsStream("mvel_rule.drl"))); // just checking there is no parsing errors assertFalse(parser.getErrors().toString(), parser.hasErrors()); InternalKnowledgePackage pkg = new KnowledgePackageImpl("org.drools"); final RuleDescr ruleDescr = pkgDescr.getRules().get(0); final RuleBuilder builder = new RuleBuilder(); final KnowledgeBuilderImpl pkgBuilder = new KnowledgeBuilderImpl(pkg); final KnowledgeBuilderConfigurationImpl conf = pkgBuilder.getBuilderConfiguration(); DialectCompiletimeRegistry dialectRegistry = pkgBuilder.getPackageRegistry(pkg.getName()).getDialectCompiletimeRegistry(); Dialect dialect = dialectRegistry.getDialect("mvel"); RuleBuildContext context = new RuleBuildContext(pkgBuilder, ruleDescr, dialectRegistry, pkg, dialect); builder.build(context); assertTrue(context.getErrors().toString(), context.getErrors().isEmpty()); final RuleImpl rule = context.getRule(); MVELConsequence mvelCons = (MVELConsequence) rule.getConsequence(); mvelCons.compile( (MVELDialectRuntimeData) pkgBuilder .getPackageRegistry(pkg.getName()) .getDialectRuntimeRegistry() .getDialectData("mvel")); String s = DebugTools.decompile(mvelCons.getCompExpr()); int fromIndex = 0; int count = 0; while ((fromIndex = s.indexOf("DEBUG_SYMBOL", fromIndex + 1)) > -1) { count++; } assertEquals(4, count); } finally { MVELDebugHandler.setDebugMode(false); } }
private void setupTest(String consequence, Map<String, Object> namedConsequences) { builder = new MVELConsequenceBuilder(); InternalKnowledgePackage pkg = new KnowledgePackageImpl("org.drools.compiler.test"); pkg.addImport(new ImportDeclaration(Cheese.class.getCanonicalName())); KnowledgeBuilderConfigurationImpl conf = new KnowledgeBuilderConfigurationImpl(); KnowledgeBuilderImpl pkgBuilder = new KnowledgeBuilderImpl(pkg, conf); ruleDescr = new RuleDescr("test consequence builder"); ruleDescr.setConsequence(consequence); ruleDescr.addAttribute(new AttributeDescr("dialect", "mvel")); for (Entry<String, Object> entry : namedConsequences.entrySet()) { ruleDescr.addNamedConsequences(entry.getKey(), entry.getValue()); } RuleImpl rule = new RuleImpl(ruleDescr.getName()); rule.addPattern(new Pattern(0, new ClassObjectType(Cheese.class), "$cheese")); rule.addPattern(new Pattern(0, new ClassObjectType(Map.class), "$map")); PackageRegistry pkgRegistry = pkgBuilder.getPackageRegistry(pkg.getName()); DialectCompiletimeRegistry reg = pkgBuilder.getPackageRegistry(pkg.getName()).getDialectCompiletimeRegistry(); context = new RuleBuildContext( pkgBuilder, ruleDescr, reg, pkg, reg.getDialect(pkgRegistry.getDialect())); context.getBuildStack().push(rule.getLhs()); context.getDialect().getConsequenceBuilder().build(context, RuleImpl.DEFAULT_CONSEQUENCE_NAME); for (String name : namedConsequences.keySet()) { context.getDialect().getConsequenceBuilder().build(context, name); } context.getDialect().addRule(context); pkgRegistry.getPackage().addRule(context.getRule()); pkgBuilder.compileAll(); pkgBuilder.reloadAll(); if (pkgBuilder.hasErrors()) { fail(pkgBuilder.getErrors().toString()); } }
@SuppressWarnings("unchecked") public RuleConditionElement build( final RuleBuildContext context, final BaseDescr descr, final Pattern prefixPattern) { boolean typesafe = context.isTypesafe(); try { final AccumulateDescr accumDescr = (AccumulateDescr) descr; if (!accumDescr.hasValidInput()) { return null; } final RuleConditionBuilder builder = (RuleConditionBuilder) context.getDialect().getBuilder(accumDescr.getInput().getClass()); // create source CE final RuleConditionElement source = builder.build(context, accumDescr.getInput()); if (source == null) { return null; } MVELDialect dialect = (MVELDialect) context.getDialect(); Map<String, Declaration> decls = context.getDeclarationResolver().getDeclarations(context.getRule()); Map<String, Declaration> sourceOuterDeclr = source.getOuterDeclarations(); Map<String, Declaration> mergedDecl = new HashMap(decls); mergedDecl.putAll(sourceOuterDeclr); Map<String, Class<?>> declarationClasses = DeclarationScopeResolver.getDeclarationClasses(decls); declarationClasses.putAll(DeclarationScopeResolver.getDeclarationClasses(sourceOuterDeclr)); BoundIdentifiers boundIds = new BoundIdentifiers(declarationClasses, context.getKnowledgeBuilder().getGlobals()); boundIds.setDeclarations(mergedDecl); Accumulator[] accumulators; final boolean readLocalsFromTuple = PackageBuilderUtil.isReadLocalsFromTuple(context, accumDescr, source); if (accumDescr.isExternalFunction()) { // uses accumulate functions accumulators = buildExternalFunctions( context, accumDescr, dialect, decls, sourceOuterDeclr, boundIds, readLocalsFromTuple); } else { // it is a custom accumulate accumulators = buildCustomAccumulate( context, accumDescr, dialect, decls, sourceOuterDeclr, boundIds, readLocalsFromTuple); } List<Declaration> requiredDeclarations = new ArrayList<Declaration>(); for (Accumulator acc : accumulators) { MvelAccumulator mvelAcc = (MvelAccumulator) acc; Collections.addAll(requiredDeclarations, mvelAcc.getRequiredDeclarations()); } MVELDialectRuntimeData data = (MVELDialectRuntimeData) context.getPkg().getDialectRuntimeRegistry().getDialectData("mvel"); Accumulate accumulate; if (accumDescr.isMultiFunction()) { accumulate = new MultiAccumulate( source, requiredDeclarations.toArray(new Declaration[requiredDeclarations.size()]), accumulators); int index = 0; for (Accumulator accumulator : accumulators) { data.addCompileable( ((MultiAccumulate) accumulate).new Wirer(index++), (MVELCompileable) accumulator); ((MVELCompileable) accumulator).compile(data, context.getRule()); } } else { accumulate = new SingleAccumulate( source, requiredDeclarations.toArray(new Declaration[requiredDeclarations.size()]), accumulators[0]); data.addCompileable( ((SingleAccumulate) accumulate).new Wirer(), (MVELCompileable) accumulators[0]); ((MVELCompileable) accumulators[0]).compile(data, context.getRule()); } return accumulate; } catch (Exception e) { DialectUtil.copyErrorLocation(e, descr); context.addError( new DescrBuildError( context.getParentDescr(), descr, e, "Unable to build expression for 'accumulate' : " + e.getMessage())); return null; } finally { context.setTypesafe(typesafe); } }
private Accumulator[] buildCustomAccumulate( final RuleBuildContext context, final AccumulateDescr accumDescr, MVELDialect dialect, Map<String, Declaration> decls, Map<String, Declaration> sourceOuterDeclr, BoundIdentifiers boundIds, boolean readLocalsFromTuple) { Accumulator[] accumulators; final MVELAnalysisResult initCodeAnalysis = (MVELAnalysisResult) dialect.analyzeBlock(context, accumDescr, accumDescr.getInitCode(), boundIds); // need to copy boundIds, as this as a "this" object. final MVELAnalysisResult actionCodeAnalysis = (MVELAnalysisResult) dialect.analyzeBlock( context, accumDescr.getActionCode(), boundIds, initCodeAnalysis.getMvelVariables(), "drools", KnowledgeHelper.class); final MVELAnalysisResult resultCodeAnalysis = (MVELAnalysisResult) dialect.analyzeExpression( context, accumDescr, accumDescr.getResultCode(), boundIds, initCodeAnalysis.getMvelVariables()); context.setTypesafe(initCodeAnalysis.isTypesafe()); MVELCompilationUnit initUnit = dialect.getMVELCompilationUnit( accumDescr.getInitCode(), initCodeAnalysis, getUsedDeclarations(decls, initCodeAnalysis), getUsedDeclarations(sourceOuterDeclr, initCodeAnalysis), initCodeAnalysis.getMvelVariables(), context, "drools", KnowledgeHelper.class, readLocalsFromTuple, MVELCompilationUnit.Scope.CONSTRAINT); context.setTypesafe(actionCodeAnalysis.isTypesafe()); MVELCompilationUnit actionUnit = dialect.getMVELCompilationUnit( accumDescr.getActionCode(), actionCodeAnalysis, getUsedDeclarations(decls, actionCodeAnalysis), getUsedDeclarations(sourceOuterDeclr, actionCodeAnalysis), initCodeAnalysis.getMvelVariables(), context, "drools", KnowledgeHelper.class, readLocalsFromTuple, MVELCompilationUnit.Scope.CONSTRAINT); MVELCompilationUnit reverseUnit = null; if (accumDescr.getReverseCode() != null) { context.setTypesafe(actionCodeAnalysis.isTypesafe()); reverseUnit = dialect.getMVELCompilationUnit( accumDescr.getReverseCode(), actionCodeAnalysis, getUsedDeclarations(decls, actionCodeAnalysis), getUsedDeclarations(sourceOuterDeclr, actionCodeAnalysis), initCodeAnalysis.getMvelVariables(), context, "drools", KnowledgeHelper.class, readLocalsFromTuple, MVELCompilationUnit.Scope.CONSTRAINT); } context.setTypesafe(resultCodeAnalysis.isTypesafe()); MVELCompilationUnit resultUnit = dialect.getMVELCompilationUnit( accumDescr.getResultCode(), resultCodeAnalysis, getUsedDeclarations(decls, resultCodeAnalysis), getUsedDeclarations(sourceOuterDeclr, resultCodeAnalysis), initCodeAnalysis.getMvelVariables(), context, "drools", KnowledgeHelper.class, readLocalsFromTuple, MVELCompilationUnit.Scope.CONSTRAINT); accumulators = new Accumulator[] {new MVELAccumulator(initUnit, actionUnit, reverseUnit, resultUnit)}; return accumulators; }
private Accumulator[] buildExternalFunctions( final RuleBuildContext context, final AccumulateDescr accumDescr, MVELDialect dialect, Map<String, Declaration> decls, Map<String, Declaration> sourceOuterDeclr, BoundIdentifiers boundIds, boolean readLocalsFromTuple) { Accumulator[] accumulators; List<AccumulateFunctionCallDescr> functions = accumDescr.getFunctions(); accumulators = new Accumulator[functions.size()]; // creating the custom array reader InternalReadAccessor arrayReader = new SelfReferenceClassFieldReader(Object[].class, "this"); int index = 0; Pattern pattern = (Pattern) context.getDeclarationResolver().peekBuildStack(); for (AccumulateFunctionCallDescr func : functions) { // build an external function executor AccumulateFunction function = context.getConfiguration().getAccumulateFunction(func.getFunction()); if (function == null) { // might have been imported in the package function = context .getKnowledgeBuilder() .getPackage() .getAccumulateFunctions() .get(func.getFunction()); } if (function == null) { context.addError( new DescrBuildError( accumDescr, context.getRuleDescr(), null, "Unknown accumulate function: '" + func.getFunction() + "' on rule '" + context.getRuleDescr().getName() + "'. All accumulate functions must be registered before building a resource.")); return null; } final AnalysisResult analysis = dialect.analyzeExpression( context, accumDescr, func.getParams().length > 0 ? func.getParams()[0] : "\"\"", boundIds); MVELCompilationUnit unit = dialect.getMVELCompilationUnit( func.getParams().length > 0 ? func.getParams()[0] : "\"\"", analysis, getUsedDeclarations(decls, analysis), getUsedDeclarations(sourceOuterDeclr, analysis), null, context, "drools", KnowledgeHelper.class, readLocalsFromTuple, MVELCompilationUnit.Scope.CONSTRAINT); accumulators[index] = new MVELAccumulatorFunctionExecutor(unit, function); // if there is a binding, create the binding if (func.getBind() != null) { if (context .getDeclarationResolver() .isDuplicated(context.getRule(), func.getBind(), function.getResultType().getName())) { if (!func.isUnification()) { context.addError( new DescrBuildError( context.getParentDescr(), accumDescr, null, "Duplicate declaration for variable '" + func.getBind() + "' in the rule '" + context.getRule().getName() + "'")); } else { Declaration inner = context.getDeclarationResolver().getDeclaration(context.getRule(), func.getBind()); Constraint c = new MvelConstraint( Collections.singletonList(context.getPkg().getName()), accumDescr.isMultiFunction() ? "this[ " + index + " ] == " + func.getBind() : "this == " + func.getBind(), new Declaration[] {inner}, null, IndexUtil.ConstraintType.EQUAL, context .getDeclarationResolver() .getDeclaration(context.getRule(), func.getBind()), accumDescr.isMultiFunction() ? new ArrayElementReader(arrayReader, index, function.getResultType()) : new SelfReferenceClassFieldReader(function.getResultType(), "this"), true); ((MutableTypeConstraint) c).setType(Constraint.ConstraintType.BETA); pattern.addConstraint(c); index++; } } else { Declaration declr = pattern.addDeclaration(func.getBind()); if (accumDescr.isMultiFunction()) { declr.setReadAccessor( new ArrayElementReader(arrayReader, index, function.getResultType())); } else { declr.setReadAccessor( new SelfReferenceClassFieldReader(function.getResultType(), "this")); } } } index++; } return accumulators; }
public void build(final RuleBuildContext context, String consequenceName) { // pushing consequence LHS into the stack for variable resolution context.getBuildStack().push(context.getRule().getLhs()); try { MVELDialect dialect = (MVELDialect) context.getDialect(context.getDialect().getId()); final RuleDescr ruleDescr = context.getRuleDescr(); String text = (Rule.DEFAULT_CONSEQUENCE_NAME.equals(consequenceName)) ? (String) ruleDescr.getConsequence() : (String) ruleDescr.getNamedConsequences().get(consequenceName); text = processMacros(text); Map<String, Declaration> decls = context.getDeclarationResolver().getDeclarations(context.getRule()); AnalysisResult analysis = dialect.analyzeBlock( context, context.getRuleDescr(), dialect.getInterceptors(), text, new BoundIdentifiers( DeclarationScopeResolver.getDeclarationClasses(decls), context.getPackageBuilder().getGlobals(), null, KnowledgeHelper.class), null, "drools", KnowledgeHelper.class); if (analysis == null) { // something bad happened, issue already logged in errors return; } final BoundIdentifiers usedIdentifiers = analysis.getBoundIdentifiers(); final Declaration[] declarations = new Declaration[usedIdentifiers.getDeclrClasses().size()]; String[] declrStr = new String[declarations.length]; int j = 0; for (String str : usedIdentifiers.getDeclrClasses().keySet()) { declrStr[j] = str; declarations[j++] = decls.get(str); } Arrays.sort(declarations, SortDeclarations.instance); for (int i = 0; i < declrStr.length; i++) { declrStr[i] = declarations[i].getIdentifier(); } context.getRule().setRequiredDeclarationsForConsequence(consequenceName, declrStr); MVELCompilationUnit unit = dialect.getMVELCompilationUnit( text, analysis, declarations, null, null, context, "drools", KnowledgeHelper.class, false); MVELConsequence expr = new MVELConsequence(unit, dialect.getId()); if (Rule.DEFAULT_CONSEQUENCE_NAME.equals(consequenceName)) { context.getRule().setConsequence(expr); } else { context.getRule().addNamedConsequence(consequenceName, expr); } MVELDialectRuntimeData data = (MVELDialectRuntimeData) context.getPkg().getDialectRuntimeRegistry().getDialectData("mvel"); data.addCompileable(context.getRule(), expr); expr.compile(data); } catch (final Exception e) { copyErrorLocation(e, context.getRuleDescr()); context.addError( new DescrBuildError( context.getParentDescr(), context.getRuleDescr(), null, "Unable to build expression for 'consequence': " + e.getMessage() + " '" + context.getRuleDescr().getConsequence() + "'")); } }