docs: private void processElement(final DeclarationScopeResolver resolver,
                                  final Stack<RuleConditionElement> contextStack,
                                  final RuleConditionElement element) {
          if ( element instanceof Pattern ) {
              Pattern pattern = (Pattern) element;
              for ( RuleConditionElement ruleConditionElement : pattern.getNestedElements() ) {
                  processElement( resolver,
                                  contextStack,
                                  ruleConditionElement );
              }
              for (Constraint constraint : pattern.getConstraints()) {
                  if (constraint instanceof Declaration) {
                      continue;
                  }
                  replaceDeclarations( resolver, pattern, constraint );
              }
  
          } else if ( element instanceof EvalCondition ) {
              processEvalCondition(resolver, (EvalCondition) element);
  
          } else if ( element instanceof Accumulate ) {
              for ( RuleConditionElement rce : element.getNestedElements() ) {
                  processElement( resolver, contextStack, rce );
              }
              Accumulate accumulate = (Accumulate)element;
              replaceDeclarations( resolver, accumulate );
  
          } else if ( element instanceof From ) {
              DataProvider provider = ((From) element).getDataProvider();
              Declaration[] decl = provider.getRequiredDeclarations();
              for (Declaration aDecl : decl) {
                  Declaration resolved = resolver.getDeclaration(aDecl.getIdentifier());
                  if (resolved != null && resolved != aDecl) {
                      provider.replaceDeclaration(aDecl, resolved);
                  } else if (resolved == null) {
                      // it is probably an implicit declaration, so find the corresponding pattern
                      Pattern old = aDecl.getPattern();
                      Pattern current = resolver.findPatternByIndex(old.getIndex());
                      if (current != null && old != current) {
                          resolved = new Declaration(aDecl.getIdentifier(), aDecl.getExtractor(), current);
                          provider.replaceDeclaration(aDecl, resolved);
                      }
                  }
              }
  
          } else if ( element instanceof QueryElement ) {
              QueryElement qe = ( QueryElement ) element;
              Pattern pattern = qe.getResultPattern();
  
              for ( Entry<String, Declaration> entry : pattern.getInnerDeclarations().entrySet() ) {
                  Declaration resolved = resolver.getDeclaration( entry.getValue().getIdentifier() );
                  if ( resolved != null && resolved != entry.getValue() && resolved.getPattern() != pattern ) {
                      entry.setValue( resolved );
                  }
              }
  
              List<Integer> varIndexes = asList( qe.getVariableIndexes() );
              for (int i = 0; i < qe.getArguments().length; i++) {
                  if (!(qe.getArguments()[i] instanceof QueryArgument.Declr)) {
                      continue;
                  }
                  Declaration declr = ((QueryArgument.Declr) qe.getArguments()[i]).getDeclaration();
                  Declaration resolved = resolver.getDeclaration( declr.getIdentifier() );
                  if ( resolved != declr && resolved.getPattern() != pattern ) {
                      qe.getArguments()[i] = new QueryArgument.Declr(resolved);
                  }
  
                  if( ClassObjectType.DroolsQuery_ObjectType.isAssignableFrom( resolved.getPattern().getObjectType() ) ) {
                      // if the resolved still points to DroolsQuery, we know this is the first unification pattern, so redeclare it as the visible Declaration
                      declr = pattern.addDeclaration( declr.getIdentifier() );
  
                      // this bit is different, notice its the ArrayElementReader that we wire up to, not the declaration.
                      ArrayElementReader reader = new ArrayElementReader( new SelfReferenceClassFieldReader(Object[].class),
                                                                          i,
                                                                          resolved.getDeclarationClass() );
                      declr.setReadAccessor( reader );
  
                      varIndexes.add( i );
                  }
              }
              qe.setVariableIndexes( toIntArray( varIndexes ) );
  
          }  else if ( element instanceof ConditionalBranch ) {
              processBranch( resolver, (ConditionalBranch) element );
  
          } else {
              contextStack.push( element );
              for (RuleConditionElement ruleConditionElement : element.getNestedElements()) {
                  processElement(resolver,
                          contextStack,
                          ruleConditionElement);
              }
              contextStack.pop();
          }
      }