@SuppressWarnings({"unchecked", "rawtypes"})
  private void foreach(boolean inheritedSkip) {
    ForEachToken feToken = (ForEachToken) tokenStream.currentToken();
    Iterable iterable = (Iterable) feToken.evaluate(context);
    // begin a fresh iteration with a reset index
    feToken.setIterator(iterable.iterator());
    feToken.resetIndex();
    tokenStream.consume();

    context.model.enterScope();
    context.push(feToken);
    try {

      // in case we do not want to evaluate the body, we just do a quick
      // scan until the matching end
      if (inheritedSkip || !feToken.iterator().hasNext()) {
        Token contentToken;
        while ((contentToken = tokenStream.currentToken()) != null
            && !(contentToken instanceof EndToken)) {
          content(true);
        }
        if (contentToken == null) {
          engine.getErrorHandler().error("missing-end", feToken);
        } else {
          tokenStream.consume();
          context.notifyProcessListener(contentToken, Action.END);
        }
      } else {

        while (feToken.iterator().hasNext()) {

          context.model.put(feToken.getVarName(), feToken.advance());
          addSpecialVariables(feToken, context.model);

          // for each iteration we need to rewind to the beginning
          // of the for loop
          tokenStream.rewind(feToken);
          Token contentToken;
          while ((contentToken = tokenStream.currentToken()) != null
              && !(contentToken instanceof EndToken)) {
            content(false);
          }
          if (contentToken == null) {
            engine.getErrorHandler().error("missing-end", feToken);
          } else {
            tokenStream.consume();
            context.notifyProcessListener(contentToken, Action.END);
          }
          if (!feToken.isLast()) {
            output.append(feToken.getSeparator());
          }
        }
      }

    } finally {
      context.model.exitScope();
      context.pop();
    }
  }
 private void content(boolean skip) {
   Token token = tokenStream.currentToken();
   context.notifyProcessListener(token, skip ? Action.SKIP : Action.EVAL);
   if (token instanceof PlainTextToken) {
     tokenStream.consume();
     if (!skip) {
       output.append(token.getText());
     }
   } else if (token instanceof StringToken) {
     tokenStream.consume();
     if (!skip) {
       String expanded = (String) token.evaluate(context);
       output.append(expanded);
     }
   } else if (token instanceof ForEachToken) {
     foreach(skip);
   } else if (token instanceof IfToken) {
     condition(skip);
   } else if (token instanceof ElseToken) {
     tokenStream.consume();
     engine.getErrorHandler().error("else-out-of-scope", token);
   } else if (token instanceof EndToken) {
     tokenStream.consume();
     engine.getErrorHandler().error("unmatched-end", token);
   } else if (token instanceof InvalidToken) {
     tokenStream.consume();
     engine.getErrorHandler().error("invalid-expression", token);
   } else {
     tokenStream.consume();
     // what ever else there may be, we just evaluate it
     String evaluated = (String) token.evaluate(context);
     output.append(evaluated);
   }
 }
  private void condition(boolean inheritedSkip) {
    IfToken ifToken = (IfToken) tokenStream.currentToken();
    tokenStream.consume();

    context.push(ifToken);
    try {
      boolean localSkip;
      if (inheritedSkip) {
        localSkip = true;
      } else {
        localSkip = !(Boolean) ifToken.evaluate(context);
      }

      Token contentToken;
      while ((contentToken = tokenStream.currentToken()) != null
          && !(contentToken instanceof EndToken)
          && !(contentToken instanceof ElseToken)) {
        content(localSkip);
      }

      if (contentToken instanceof ElseToken) {
        tokenStream.consume();
        // toggle for else branch
        if (!inheritedSkip) {
          localSkip = !localSkip;
        }
        context.notifyProcessListener(contentToken, inheritedSkip ? Action.SKIP : Action.EVAL);

        while ((contentToken = tokenStream.currentToken()) != null
            && !(contentToken instanceof EndToken)) {
          content(localSkip);
        }
      }

      if (contentToken == null) {
        engine.getErrorHandler().error("missing-end", ifToken);
      } else {
        tokenStream.consume();
        context.notifyProcessListener(contentToken, Action.END);
      }
    } finally {
      context.pop();
    }
  }