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