@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()
                  + "'"));
    }
  }