/** * Analyse an expression. * * @param availDecls Total set of declarations available. * @param ast The AST for the expression. * @return The <code>Set</code> of declarations used by the expression. * @throws RecognitionException If an error occurs in the parser. */ @SuppressWarnings("unchecked") private MVELAnalysisResult analyze( final Set<String> identifiers, final BoundIdentifiers availableIdentifiers) { MVELAnalysisResult result = new MVELAnalysisResult(); result.setIdentifiers(identifiers); final Set<String> notBound = new HashSet<String>(identifiers); notBound.remove("this"); Map<String, Class<?>> usedDecls = new HashMap<String, Class<?>>(); Map<String, Class<?>> usedGlobals = new HashMap<String, Class<?>>(); for (Entry<String, Class<?>> entry : availableIdentifiers.getDeclarations().entrySet()) { if (identifiers.contains(entry.getKey())) { usedDecls.put(entry.getKey(), entry.getValue()); notBound.remove(entry.getKey()); } } for (Entry<String, Class<?>> entry : availableIdentifiers.getGlobals().entrySet()) { if (identifiers.contains(entry.getKey())) { usedGlobals.put(entry.getKey(), entry.getValue()); notBound.remove(entry.getKey()); } } result.setBoundIdentifiers( new BoundIdentifiers(usedDecls, usedGlobals, availableIdentifiers.getThisClass())); result.setNotBoundedIdentifiers(notBound); return result; }
private Declaration[] getUsedDeclarations( Map<String, Declaration> decls, AnalysisResult analysis) { final BoundIdentifiers usedIdentifiers = analysis.getBoundIdentifiers(); List<Declaration> usedDeclarations = new ArrayList<Declaration>(); for (String id : usedIdentifiers.getDeclrClasses().keySet()) { if (decls.containsKey(id)) { usedDeclarations.add(decls.get(id)); } } if (!usedDeclarations.isEmpty()) { Collections.sort(usedDeclarations, SortDeclarations.instance); } return usedDeclarations.toArray(new Declaration[usedDeclarations.size()]); }
/** * Builds and returns an Eval Conditional Element * * @param context The current build context * @param utils The current build utils instance * @param patternBuilder not used by EvalBuilder * @param descr The Eval Descriptor to build the eval conditional element from * @return the Eval Conditional Element */ public RuleConditionElement build( final RuleBuildContext context, final BaseDescr descr, final Pattern prefixPattern) { // it must be an EvalDescr final EvalDescr evalDescr = (EvalDescr) descr; final String className = "eval" + context.getNextId(); evalDescr.setClassMethodName(className); Map<String, Declaration> decls = context.getDeclarationResolver().getDeclarations(context.getRule()); AnalysisResult analysis = context .getDialect() .analyzeExpression( context, evalDescr, evalDescr.getContent(), new BoundIdentifiers( context.getDeclarationResolver().getDeclarationClasses(decls), context.getPackageBuilder().getGlobals())); final BoundIdentifiers usedIdentifiers = analysis.getBoundIdentifiers(); final Declaration[] declarations = decls.values().toArray(new Declaration[decls.size()]); Arrays.sort(declarations, SortDeclarations.instance); final EvalCondition eval = new EvalCondition(declarations); final Map map = createVariableContext( className, (String) evalDescr.getContent(), context, declarations, null, usedIdentifiers.getGlobals(), null); generatTemplates("evalMethod", "evalInvoker", context, className, map, eval, descr); return eval; }
/** * Analyze an expression. * * @param expr The expression to analyze. * @param availDecls Total set of declarations available. * @return The <code>Set</code> of declarations used by the expression. * @throws RecognitionException If an error occurs in the parser. */ public MVELAnalysisResult analyzeExpression( final PackageBuildContext context, final String expr, final BoundIdentifiers availableIdentifiers, final Map<String, Class<?>> localTypes) { MVELAnalysisResult result = null; if (expr.trim().length() > 0) { MVEL.COMPILER_OPT_ALLOW_NAKED_METH_CALL = true; MVELDialect dialect = (MVELDialect) context.getDialect("mvel"); // creating a reusable parser configuration ParserConfiguration conf = new ParserConfiguration(); conf.setImports(dialect.getImports()); conf.setPackageImports((HashSet) dialect.getPackgeImports()); conf.setClassLoader(context.getPackageBuilder().getRootClassLoader()); // first compilation is for verification only // @todo proper source file name final ParserContext parserContext1 = new ParserContext(conf); if (localTypes != null) { for (Entry entry : localTypes.entrySet()) { parserContext1.addInput((String) entry.getKey(), (Class) entry.getValue()); } } if (availableIdentifiers.getThisClass() != null) { parserContext1.addInput("this", availableIdentifiers.getThisClass()); } parserContext1.setStrictTypeEnforcement(false); parserContext1.setStrongTyping(false); parserContext1.setInterceptors(dialect.getInterceptors()); Class returnType = null; try { returnType = MVEL.analyze(expr, parserContext1); } catch (Exception e) { context .getErrors() .add( new DescrBuildError( context.getParentDescr(), null, null, "Unable to Analyse Expression " + expr + ":\n" + e.getMessage())); return null; } Set<String> requiredInputs = new HashSet(); requiredInputs.addAll(parserContext1.getInputs().keySet()); HashMap<String, Class<?>> variables = (HashMap<String, Class<?>>) ((Map) parserContext1.getVariables()); // MVEL includes direct fields of context object in non-strict mode. so we need to strip those if (availableIdentifiers.getThisClass() != null) { for (Iterator<String> it = requiredInputs.iterator(); it.hasNext(); ) { if (PropertyTools.getFieldOrAccessor(availableIdentifiers.getThisClass(), it.next()) != null) { it.remove(); } } } // now, set the required input types and compile again final ParserContext parserContext2 = new ParserContext(conf); parserContext2.setStrictTypeEnforcement(true); parserContext2.setStrongTyping(true); parserContext2.setInterceptors(dialect.getInterceptors()); if (context.isTypesafe()) { if (localTypes != null) { for (Entry<String, Class<?>> entry : localTypes.entrySet()) { parserContext2.addInput(entry.getKey(), entry.getValue()); } } for (String str : requiredInputs) { Class cls = availableIdentifiers.getDeclarations().get(str); if (cls != null) { parserContext2.addInput(str, cls); continue; } if (cls == null) { cls = availableIdentifiers.getGlobals().get(str); if (cls != null) { parserContext2.addInput(str, cls); continue; } } if (cls == null) { if (str.equals("drools")) { parserContext2.addInput("drools", KnowledgeHelper.class); } else if (str.equals("kcontext")) { parserContext2.addInput("kcontext", RuleContext.class); } if (str.equals("rule")) { parserContext2.addInput("rule", Rule.class); } } } if (availableIdentifiers.getThisClass() != null) { parserContext2.addInput("this", availableIdentifiers.getThisClass()); } try { returnType = MVEL.analyze(expr, parserContext2); } catch (Exception e) { context .getErrors() .add( new DescrBuildError( context.getParentDescr(), null, null, "Unable to Analyse Expression " + expr + ":\n" + e.getMessage())); return null; } requiredInputs = new HashSet(); requiredInputs.addAll(parserContext2.getInputs().keySet()); requiredInputs.addAll(variables.keySet()); variables = (HashMap<String, Class<?>>) ((Map) parserContext2.getVariables()); } result = analyze(requiredInputs, availableIdentifiers); result.setReturnType(returnType); result.setMvelVariables(variables); } else { result = analyze((Set<String>) Collections.EMPTY_SET, availableIdentifiers); result.setMvelVariables(new HashMap<String, Class<?>>()); } return result; }
/** * Builds and returns an Eval Conditional Element * * @param context The current build context * @param utils The current build utils instance * @param patternBuilder not used by EvalBuilder * @param descr The Eval Descriptor to build the eval conditional element from * @return the Eval Conditional Element */ public RuleConditionElement build( final RuleBuildContext context, final BaseDescr descr, final Pattern prefixPattern) { boolean typesafe = context.isTypesafe(); // it must be an EvalDescr final EvalDescr evalDescr = (EvalDescr) descr; try { MVELDialect dialect = (MVELDialect) context.getDialect(context.getDialect().getId()); Map<String, Declaration> decls = context.getDeclarationResolver().getDeclarations(context.getRule()); AnalysisResult analysis = context .getDialect() .analyzeExpression( context, evalDescr, evalDescr.getContent(), new BoundIdentifiers( context.getDeclarationResolver().getDeclarationClasses(decls), context.getPackageBuilder().getGlobals())); final BoundIdentifiers usedIdentifiers = analysis.getBoundIdentifiers(); int i = usedIdentifiers.getDeclrClasses().keySet().size(); Declaration[] previousDeclarations = new Declaration[i]; i = 0; for (String id : usedIdentifiers.getDeclrClasses().keySet()) { previousDeclarations[i++] = decls.get(id); } Arrays.sort(previousDeclarations, SortDeclarations.instance); MVELCompilationUnit unit = dialect.getMVELCompilationUnit( (String) evalDescr.getContent(), analysis, previousDeclarations, null, null, context, "drools", KnowledgeHelper.class, false); final EvalCondition eval = new EvalCondition(previousDeclarations); MVELEvalExpression expr = new MVELEvalExpression(unit, dialect.getId()); eval.setEvalExpression(expr); MVELDialectRuntimeData data = (MVELDialectRuntimeData) context.getPkg().getDialectRuntimeRegistry().getDialectData("mvel"); data.addCompileable(eval, expr); expr.compile(data); return eval; } catch (final Exception e) { copyErrorLocation(e, evalDescr); context.addError( new DescrBuildError( context.getParentDescr(), evalDescr, e, "Unable to build expression for 'eval':" + e.getMessage() + " '" + evalDescr.getContent() + "'")); return null; } finally { context.setTypesafe(typesafe); } }
@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 = context.getDeclarationResolver().getDeclarationClasses(decls); declarationClasses.putAll( context.getDeclarationResolver().getDeclarationClasses(sourceOuterDeclr)); BoundIdentifiers boundIds = new BoundIdentifiers(declarationClasses, context.getPackageBuilder().getGlobals()); boundIds.setDeclarations(mergedDecl); Accumulator[] accumulators = null; final boolean readLocalsFromTuple = PackageBuilderUtil.isReadLocalsFromTuple(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, source, dialect, decls, sourceOuterDeclr, boundIds, readLocalsFromTuple); } final Accumulate accumulate = new Accumulate(source, null, accumulators, accumDescr.isMultiFunction()); MVELDialectRuntimeData data = (MVELDialectRuntimeData) context.getPkg().getDialectRuntimeRegistry().getDialectData("mvel"); int index = 0; for (Accumulator accumulator : accumulators) { data.addCompileable(accumulate.new Wirer(index++), (MVELCompileable) accumulator); ((MVELCompileable) accumulator).compile(data); } return accumulate; } catch (Exception e) { 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); } }