@SuppressWarnings("unchecked")
  public RuleConditionElement build(
      final RuleBuildContext context, final BaseDescr descr, final Pattern prefixPattern) {
    try {
      final AccumulateDescr accumDescr = (AccumulateDescr) descr;

      if (!accumDescr.hasValidInput()) {
        return null;
      }

      final RuleConditionBuilder builder =
          (RuleConditionBuilder) context.getDialect().getBuilder(accumDescr.getInput().getClass());

      Declaration[] previousDeclarations =
          (Declaration[])
              context
                  .getDeclarationResolver()
                  .getDeclarations(context.getRule())
                  .values()
                  .toArray(
                      new Declaration
                          [context
                              .getDeclarationResolver()
                              .getDeclarations(context.getRule())
                              .size()]);

      // create source CE
      final RuleConditionElement source = builder.build(context, accumDescr.getInput());

      if (source == null) {
        return null;
      }

      MVELDialect dialect = (MVELDialect) context.getDialect();

      final Declaration[] sourceDeclArr =
          (Declaration[])
              source
                  .getOuterDeclarations()
                  .values()
                  .toArray(new Declaration[source.getOuterDeclarations().size()]);

      Accumulator accumulator = null;
      Declaration[] declarations = null;

      if (accumDescr.isExternalFunction()) {
        // build an external function executor
        AccumulateFunction function =
            context.getConfiguration().getAccumulateFunction(accumDescr.getFunctionIdentifier());

        if (function == null) {
          context
              .getErrors()
              .add(
                  new DescrBuildError(
                      accumDescr,
                      context.getRuleDescr(),
                      null,
                      "Unknown accumulate function: '"
                          + accumDescr.getFunctionIdentifier()
                          + "' on rule '"
                          + context.getRuleDescr().getName()
                          + "'. All accumulate functions must be registered before building a resource."));
          return null;
        }

        Map<String, Class<?>> declarationsMap =
            context.getDeclarationResolver().getDeclarationClasses(context.getRule());
        final Dialect.AnalysisResult analysis =
            dialect.analyzeExpression(
                context,
                accumDescr,
                accumDescr.getExpression(),
                new Map[] {declarationsMap, context.getPackageBuilder().getGlobals()});

        MVELCompilationUnit unit =
            dialect.getMVELCompilationUnit(
                (String) accumDescr.getExpression(),
                analysis,
                previousDeclarations,
                (Declaration[])
                    source
                        .getOuterDeclarations()
                        .values()
                        .toArray(new Declaration[source.getOuterDeclarations().size()]),
                null,
                context);

        accumulator = new MVELAccumulatorFunctionExecutor(unit, function);
      } else {
        // it is a custom accumulate
        Map<String, Class<?>> declarationsMap =
            context.getDeclarationResolver().getDeclarationClasses(context.getRule());
        final MVELAnalysisResult initCodeAnalysis =
            (MVELAnalysisResult)
                dialect.analyzeBlock(
                    context,
                    accumDescr,
                    accumDescr.getInitCode(),
                    new Map[] {declarationsMap, context.getPackageBuilder().getGlobals()});

        final MVELAnalysisResult actionCodeAnalysis =
            (MVELAnalysisResult)
                dialect.analyzeBlock(
                    context,
                    accumDescr,
                    null,
                    accumDescr.getActionCode(),
                    new Map[] {declarationsMap, context.getPackageBuilder().getGlobals()},
                    initCodeAnalysis.getMvelVariables());

        final MVELAnalysisResult resultCodeAnalysis =
            (MVELAnalysisResult)
                dialect.analyzeExpression(
                    context,
                    accumDescr,
                    accumDescr.getResultCode(),
                    new Map[] {declarationsMap, context.getPackageBuilder().getGlobals()},
                    initCodeAnalysis.getMvelVariables());

        Dialect.AnalysisResult reverseCodeAnalysis = null;
        if (accumDescr.getReverseCode() != null) {
          reverseCodeAnalysis =
              dialect.analyzeBlock(
                  context,
                  accumDescr,
                  null,
                  accumDescr.getActionCode(),
                  new Map[] {declarationsMap, context.getPackageBuilder().getGlobals()},
                  initCodeAnalysis.getMvelVariables());
        }

        MVELCompilationUnit initUnit =
            dialect.getMVELCompilationUnit(
                (String) accumDescr.getInitCode(),
                initCodeAnalysis,
                previousDeclarations,
                (Declaration[])
                    source
                        .getOuterDeclarations()
                        .values()
                        .toArray(new Declaration[source.getOuterDeclarations().size()]),
                null,
                context);

        MVELCompilationUnit actionUnit =
            dialect.getMVELCompilationUnit(
                (String) accumDescr.getActionCode(),
                actionCodeAnalysis,
                previousDeclarations,
                (Declaration[])
                    source
                        .getOuterDeclarations()
                        .values()
                        .toArray(new Declaration[source.getOuterDeclarations().size()]),
                initCodeAnalysis.getMvelVariables(),
                context);

        MVELCompilationUnit reverseUnit = null;
        if (accumDescr.getReverseCode() != null) {
          reverseUnit =
              dialect.getMVELCompilationUnit(
                  (String) accumDescr.getReverseCode(),
                  reverseCodeAnalysis,
                  previousDeclarations,
                  (Declaration[])
                      source
                          .getOuterDeclarations()
                          .values()
                          .toArray(new Declaration[source.getOuterDeclarations().size()]),
                  initCodeAnalysis.getMvelVariables(),
                  context);
        }

        MVELCompilationUnit resultUnit =
            dialect.getMVELCompilationUnit(
                (String) accumDescr.getResultCode(),
                resultCodeAnalysis,
                previousDeclarations,
                (Declaration[])
                    source
                        .getOuterDeclarations()
                        .values()
                        .toArray(new Declaration[source.getOuterDeclarations().size()]),
                initCodeAnalysis.getMvelVariables(),
                context);

        if (reverseUnit != null) {
          Set<String> shadow = new HashSet<String>(source.getOuterDeclarations().keySet());
          shadow.retainAll(reverseCodeAnalysis.getNotBoundedIdentifiers());
          shadow.addAll(reverseCodeAnalysis.getBoundIdentifiers()[0]);

          String[] shadowVars = (String[]) shadow.toArray(new String[shadow.size()]);

          actionUnit.setShadowIdentifiers(shadowVars);
          reverseUnit.setShadowIdentifiers(shadowVars);
        }

        accumulator = new MVELAccumulator(initUnit, actionUnit, reverseUnit, resultUnit);
      }

      final Accumulate accumulate =
          new Accumulate(source, declarations, sourceDeclArr, new Accumulator[] {accumulator});

      MVELDialectRuntimeData data =
          (MVELDialectRuntimeData)
              context.getPkg().getDialectRuntimeRegistry().getDialectData("mvel");
      data.addCompileable(accumulate, (MVELCompileable) accumulator);

      ((MVELCompileable) accumulator).compile(context.getPackageBuilder().getRootClassLoader());

      return accumulate;
    } catch (Exception e) {
      context
          .getErrors()
          .add(
              new DescrBuildError(
                  context.getParentDescr(),
                  descr,
                  e,
                  "Unable to build expression for 'accumulate' : " + e.getMessage()));
      return null;
    }
  }
  public void execute(Map<String, Object> context, List<String[]> args) {
    BuildContext buildContext = (BuildContext) context.get("BuildContext");

    if (args.size() >= 1) {

      // The first argument list is the node parameters
      String[] a = args.get(0);
      String name = a[0];
      String leftInput = a[1];
      String rightInput = a[2];
      String sourceType = a[3];
      String expr = a[4];

      LeftTupleSource leftTupleSource;
      if ("mock".equals(leftInput)) {
        leftTupleSource = Mockito.mock(LeftTupleSource.class);
      } else {
        leftTupleSource = (LeftTupleSource) context.get(leftInput);
      }

      ObjectSource rightObjectSource;
      if ("mock".equals(rightInput)) {
        rightObjectSource = Mockito.mock(ObjectSource.class);
      } else {
        rightObjectSource = (ObjectSource) context.get(rightInput);
      }

      Pattern sourcePattern;
      Pattern resultPattern;
      try {
        sourcePattern = reteTesterHelper.getPattern(0, sourceType);

        // we always use the accumulate function "sum", so return type is always Number
        resultPattern =
            reteTesterHelper.getPattern(buildContext.getNextId(), Number.class.getName());
      } catch (Exception e) {
        throw new IllegalArgumentException(
            "Not possible to process arguments: " + Arrays.toString(a));
      }

      BetaConstraints betaSourceConstraints = new EmptyBetaConstraints();
      AlphaNodeFieldConstraint[] alphaResultConstraint = new AlphaNodeFieldConstraint[0];
      // the following arguments are constraints
      for (int i = 1; i < args.size(); i++) {
        a = args.get(i);
        String type = a[0];
        String fieldName = a[1];
        String operator = a[2];
        String val = a[3];

        if ("source".equals(type)) {
          Declaration declr = (Declaration) context.get(val);
          try {
            BetaNodeFieldConstraint sourceBetaConstraint =
                this.reteTesterHelper.getBoundVariableConstraint(
                    sourcePattern, fieldName, declr, operator);
            betaSourceConstraints =
                new SingleBetaConstraints(
                    sourceBetaConstraint, buildContext.getRuleBase().getConfiguration());
          } catch (IntrospectionException e) {
            throw new IllegalArgumentException();
          }
        } else if ("result".equals(type)) {
          alphaResultConstraint = new AlphaNodeFieldConstraint[1];
          try {
            alphaResultConstraint[0] =
                this.reteTesterHelper.getLiteralConstraint(resultPattern, fieldName, operator, val);
          } catch (IntrospectionException e) {
            throw new IllegalArgumentException(
                "Unable to configure alpha constraint: " + Arrays.toString(a), e);
          }
        }
      }

      NodeTestCase testCase = (NodeTestCase) context.get("TestCase");
      List<String> classImports = new ArrayList<String>();
      List<String> pkgImports = new ArrayList<String>();
      for (String imp : testCase.getImports()) {
        if (imp.endsWith(".*")) {
          pkgImports.add(imp.substring(0, imp.lastIndexOf('.')));
        } else {
          classImports.add(imp);
        }
      }

      // build an external function executor
      MVELCompilationUnit compilationUnit =
          new MVELCompilationUnit(
              name,
              expr,
              pkgImports.toArray(new String[0]), // pkg imports
              classImports.toArray(new String[0]), // imported classes
              new String[] {}, // imported methods
              new String[] {}, // imported fields
              new String[] {}, // global identifiers
              new Declaration[] {}, // previous declarations
              new Declaration[] {(Declaration) context.get(expr)}, // local declarations
              new String[] {}, // other identifiers
              new String[] {}, // input identifiers
              new String[] {}, // input types
              4,
              false);

      AccumulateFunction accFunction = new SumAccumulateFunction();

      Accumulator accumulator = new MVELAccumulatorFunctionExecutor(compilationUnit, accFunction);
      ((MVELCompileable) accumulator).compile(Thread.currentThread().getContextClassLoader());

      Accumulate accumulate =
          new Accumulate(
              sourcePattern,
              new Declaration[] {}, // required declaration
              new Declaration[] {}, // inner declarations
              new Accumulator[] {accumulator});
      AccumulateNode accNode =
          new AccumulateNode(
              buildContext.getNextId(),
              leftTupleSource,
              rightObjectSource,
              alphaResultConstraint,
              betaSourceConstraints,
              new EmptyBetaConstraints(),
              new Behavior[] {},
              accumulate,
              false,
              buildContext);

      accNode.attach();
      context.put(name, accNode);

    } else {
      StringBuilder msgBuilder = new StringBuilder();
      msgBuilder.append("Can not parse AccumulateNode step arguments: \n");
      for (String[] arg : args) {
        msgBuilder.append("    ");
        msgBuilder.append(Arrays.toString(arg));
        msgBuilder.append("\n");
      }
      throw new IllegalArgumentException(msgBuilder.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 =
          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);
    }
  }