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];

      Class cls = null;

      LeftTupleSource leftTupleSource;
      if (leftInput.startsWith("mock")) {
        leftTupleSource = new MockTupleSource(buildContext.getNextId());
      } else {
        leftTupleSource = (LeftTupleSource) context.get(leftInput);
      }

      ObjectSource rightObjectSource;
      if (rightInput.startsWith("mock")) {
        String type = rightInput.substring(5, rightInput.length() - 1);
        try {
          cls = reteTesterHelper.getTypeResolver().resolveType(type);
        } catch (ClassNotFoundException e) {
          throw new RuntimeException(e);
        }
        rightObjectSource = new MockObjectSource(buildContext.getNextId());
      } else {
        rightObjectSource = (ObjectSource) context.get(rightInput);
        ObjectSource source = rightObjectSource;
        while (!(source instanceof ObjectTypeNode)) {
          source = source.getParentObjectSource();
        }
        cls = ((ClassObjectType) ((ObjectTypeNode) source).getObjectType()).getClassType();
      }

      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));
      }

      List<BetaNodeFieldConstraint> list = new ArrayList<BetaNodeFieldConstraint>();

      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(cls, fieldName, declr, operator);
            list.add(sourceBetaConstraint);
          } 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);
          }
        }
      }

      BetaConstraints betaSourceConstraints;
      switch (list.size()) {
        case 0:
          betaSourceConstraints = new EmptyBetaConstraints();
          break;
        case 1:
          betaSourceConstraints =
              new SingleBetaConstraints(
                  list.get(0), buildContext.getKnowledgeBase().getConfiguration());
          break;
        case 2:
          betaSourceConstraints =
              new DoubleBetaConstraints(
                  list.toArray(new BetaNodeFieldConstraint[2]),
                  buildContext.getKnowledgeBase().getConfiguration());
          break;
        case 3:
          betaSourceConstraints =
              new TripleBetaConstraints(
                  list.toArray(new BetaNodeFieldConstraint[2]),
                  buildContext.getKnowledgeBase().getConfiguration());
          break;
        case 4:
          betaSourceConstraints =
              new QuadroupleBetaConstraints(
                  list.toArray(new BetaNodeFieldConstraint[2]),
                  buildContext.getKnowledgeBase().getConfiguration());
          break;
        default:
          betaSourceConstraints =
              new DefaultBetaConstraints(
                  list.toArray(new BetaNodeFieldConstraint[2]),
                  buildContext.getKnowledgeBase().getConfiguration());
          break;
      }

      MVELDialectRuntimeData data =
          (MVELDialectRuntimeData)
              buildContext
                  .getKnowledgeBase()
                  .getPackage(buildContext.getRule().getPackageName())
                  .getDialectRuntimeRegistry()
                  .getDialectData("mvel");
      data.onAdd(null, buildContext.getKnowledgeBase().getRootClassLoader());
      // MvelD data = (MVELDialectRuntimeData) buildContext.getRuleBase().getPackage(
      // buildContext.getRule().getName() ).getDialectRuntimeRegistry().getDialectData( "mvel" );

      NodeTestCase testCase = (NodeTestCase) context.get("TestCase");

      try {
        for (String imp : testCase.getImports()) {
          if (imp.endsWith(".*")) {
            data.addPackageImport(imp.substring(0, imp.lastIndexOf('.')));
          } else {
            // classImports.add( imp );
            cls = data.getRootClassLoader().loadClass(imp);
            data.addImport(cls.getSimpleName(), cls);
          }
        }
      } catch (Exception e) {
        throw new RuntimeException("Unable to load class", e);
      }

      Declaration decl = (Declaration) context.get(expr);
      // build an external function executor
      MVELCompilationUnit compilationUnit =
          new MVELCompilationUnit(
              name,
              expr,
              new String[] {}, // global identifiers
              new EvaluatorWrapper[] {}, // operator identifiers
              new Declaration[] {}, // previous declarations
              new Declaration[] {decl}, // local declarations
              new String[] {}, // other identifiers
              new String[] {
                "this", "drools", "kcontext", "rule", decl.getIdentifier()
              }, // input identifiers
              new String[] {
                Object.class.getName(),
                KnowledgeHelper.class.getName(),
                KnowledgeHelper.class.getName(),
                Rule.class.getName(),
                decl.getValueType().getClassType().getName()
              }, // input types
              4,
              false,
              false);

      AccumulateFunction accFunction = new SumAccumulateFunction();

      Accumulator accumulator = new MVELAccumulatorFunctionExecutor(compilationUnit, accFunction);
      ((MVELCompileable) accumulator).compile(data);

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

      accNode.attach(buildContext);
      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 =
          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);
    }
  }