156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson/*
256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Copyright (C) 2010 Google Inc.
356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson *
456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Licensed under the Apache License, Version 2.0 (the "License");
556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * you may not use this file except in compliance with the License.
656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * You may obtain a copy of the License at
756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson *
856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * http://www.apache.org/licenses/LICENSE-2.0
956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson *
1056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Unless required by applicable law or agreed to in writing, software
1156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * distributed under the License is distributed on an "AS IS" BASIS,
1256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * See the License for the specific language governing permissions and
1456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * limitations under the License.
1556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */
1656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
1756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonpackage com.google.clearsilver.jsilver.interpreter;
1856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
1956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.autoescape.EscapeMode;
2056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.data.Data;
2156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.data.DataContext;
2256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.exceptions.ExceptionUtil;
2356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.exceptions.JSilverIOException;
2456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.exceptions.JSilverInterpreterException;
2556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.functions.FunctionExecutor;
2656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.analysis.DepthFirstAdapter;
2756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AAltCommand;
2856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AAutoescapeCommand;
2956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ACallCommand;
3056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ADataCommand;
3156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ADefCommand;
3256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AEachCommand;
3356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AEscapeCommand;
3456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AEvarCommand;
3556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AHardIncludeCommand;
3656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AHardLincludeCommand;
3756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AIfCommand;
3856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AIncludeCommand;
3956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ALincludeCommand;
4056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ALoopCommand;
4156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ALoopIncCommand;
4256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ALoopToCommand;
4356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ALvarCommand;
4456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ANameCommand;
4556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ANameVariable;
4656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ASetCommand;
4756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AUvarCommand;
4856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AVarCommand;
4956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AWithCommand;
5056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.PCommand;
5156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.PExpression;
5256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.PPosition;
5356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.PVariable;
5456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.TCsOpen;
5556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.TWord;
5656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.template.Macro;
5756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.template.RenderingContext;
5856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.template.Template;
5956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.template.TemplateLoader;
6056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.values.Value;
6156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.values.VariableValue;
6256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
6356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport java.io.IOException;
6456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport java.util.Iterator;
6556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport java.util.LinkedList;
6656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
6756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson/**
6856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Main JSilver interpreter. This walks a template's AST and renders the result out.
6956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */
7056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonpublic class TemplateInterpreter extends DepthFirstAdapter {
7156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
7256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private final Template template;
7356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
7456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private final ExpressionEvaluator expressionEvaluator;
7556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private final VariableLocator variableLocator;
7656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private final TemplateLoader templateLoader;
7756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private final RenderingContext context;
7856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private final DataContext dataContext;
7956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
8056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public TemplateInterpreter(Template template, TemplateLoader templateLoader,
8156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      RenderingContext context, FunctionExecutor functionExecutor) {
8256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    this.template = template;
8356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    this.templateLoader = templateLoader;
8456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    this.context = context;
8556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    this.dataContext = context.getDataContext();
8656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
8756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    expressionEvaluator = new ExpressionEvaluator(dataContext, functionExecutor);
8856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    variableLocator = new VariableLocator(expressionEvaluator);
8956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
9056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
9156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  // ------------------------------------------------------------------------
9256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  // COMMAND PROCESSING
9356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
9456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
9556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * Chunk of data (i.e. not a CS command).
9656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
9756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
9856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseADataCommand(ADataCommand node) {
9956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    context.writeUnescaped(node.getData().getText());
10056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
10156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
10256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
10356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * <?cs var:blah > expression. Evaluate as string and write output, using default escaping.
10456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
10556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
10656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseAVarCommand(AVarCommand node) {
10756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    setLastPosition(node.getPosition());
10856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
10956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // Evaluate expression.
11056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    Value value = expressionEvaluator.evaluate(node.getExpression());
11156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    writeVariable(value);
11256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
11356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
11456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
11556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * <?cs uvar:blah > expression. Evaluate as string and write output, but don't escape.
11656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
11756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
11856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseAUvarCommand(AUvarCommand node) {
11956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    setLastPosition(node.getPosition());
12056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
12156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // Evaluate expression.
12256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    Value value = expressionEvaluator.evaluate(node.getExpression());
12356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    context.writeUnescaped(value.asString());
12456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
12556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
12656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
12756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * <?cs lvar:blah > command. Evaluate expression and execute commands within.
12856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
12956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
13056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseALvarCommand(ALvarCommand node) {
13156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    setLastPosition(node.getPosition());
13256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    evaluateVariable(node.getExpression(), "[lvar expression]");
13356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
13456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
13556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
13656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * <?cs evar:blah > command. Evaluate expression and execute commands within.
13756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
13856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
13956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseAEvarCommand(AEvarCommand node) {
14056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    setLastPosition(node.getPosition());
14156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    evaluateVariable(node.getExpression(), "[evar expression]");
14256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
14356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
14456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private void evaluateVariable(PExpression expression, String stackTraceDescription) {
14556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // Evaluate expression.
14656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    Value value = expressionEvaluator.evaluate(expression);
14756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
14856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // Now parse result, into new mini template.
14956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    Template template =
15056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        templateLoader.createTemp(stackTraceDescription, value.asString(), context
15156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson            .getAutoEscapeMode());
15256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
15356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // Intepret new template.
15456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    try {
15556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      template.render(context);
15656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    } catch (IOException e) {
15756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      throw new JSilverInterpreterException(e.getMessage());
15856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
15956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
16056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
16156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
16256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * <?cs linclude!'somefile.cs' > command. Lazily includes another template (at render time).
16356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * Throw an error if file does not exist.
16456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
16556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
16656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseAHardLincludeCommand(AHardLincludeCommand node) {
16756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    setLastPosition(node.getPosition());
16856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    include(node.getExpression(), false);
16956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
17056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
17156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
17256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * <?cs linclude:'somefile.cs' > command. Lazily includes another template (at render time).
17356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * Silently ignore if the included file does not exist.
17456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
17556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
17656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseALincludeCommand(ALincludeCommand node) {
17756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    setLastPosition(node.getPosition());
17856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    include(node.getExpression(), true);
17956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
18056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
18156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
18256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * <?cs include!'somefile.cs' > command. Throw an error if file does not exist.
18356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
18456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
18556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseAHardIncludeCommand(AHardIncludeCommand node) {
18656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    setLastPosition(node.getPosition());
18756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    include(node.getExpression(), false);
18856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
18956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
19056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
19156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * <?cs include:'somefile.cs' > command. Silently ignore if the included file does not
19256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * exist.
19356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
19456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
19556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseAIncludeCommand(AIncludeCommand node) {
19656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    setLastPosition(node.getPosition());
19756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    include(node.getExpression(), true);
19856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
19956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
20056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
20156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * <?cs set:x='y' > command.
20256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
20356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
20456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseASetCommand(ASetCommand node) {
20556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    setLastPosition(node.getPosition());
20656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    String variableName = variableLocator.getVariableName(node.getVariable());
20756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
20856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    try {
20956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      Data variable = dataContext.findVariable(variableName, true);
21056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      Value value = expressionEvaluator.evaluate(node.getExpression());
21156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      variable.setValue(value.asString());
21256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      // TODO: what about nested structures?
21356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      // "set" was used to set a variable to a constant or escaped value like
21456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      // <?cs set: x = "<b>X</b>" ?> or <?cs set: y = html_escape(x) ?>
21556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      // Keep track of this so autoescaping code can take it into account.
21656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      variable.setEscapeMode(value.getEscapeMode());
21756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    } catch (UnsupportedOperationException e) {
21856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      // An error occurred - probably due to trying to modify an UnmodifiableData
21956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      throw new UnsupportedOperationException(createUnsupportedOperationMessage(node, context
22056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson          .getIncludedTemplateNames()), e);
22156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
22256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
22356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
22456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
22556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * &lt;?cs name:blah &gt; command. Writes out the name of the original variable referred to by a
22656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * given node.
22756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
22856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
22956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseANameCommand(ANameCommand node) {
23056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    setLastPosition(node.getPosition());
23156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    String variableName = variableLocator.getVariableName(node.getVariable());
23256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    Data variable = dataContext.findVariable(variableName, false);
23356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (variable != null) {
23456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      context.writeEscaped(variable.getSymlink().getName());
23556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
23656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
23756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
23856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
23956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * &lt;?cs if:blah &gt; ... &lt;?cs else &gt; ... &lt;?cs /if &gt; command.
24056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
24156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
24256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseAIfCommand(AIfCommand node) {
24356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    setLastPosition(node.getPosition());
24456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    Value value = expressionEvaluator.evaluate(node.getExpression());
24556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (value.asBoolean()) {
24656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      node.getBlock().apply(this);
24756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    } else {
24856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      node.getOtherwise().apply(this);
24956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
25056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
25156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
25256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
25356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
25456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * &lt;?cs escape:'html' &gt; command. Changes default escaping function.
25556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
25656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
25756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseAEscapeCommand(AEscapeCommand node) {
25856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    setLastPosition(node.getPosition());
25956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    Value value = expressionEvaluator.evaluate(node.getExpression());
26056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    String escapeStrategy = value.asString();
26156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
26256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    context.pushEscapingFunction(escapeStrategy);
26356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    node.getCommand().apply(this);
26456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    context.popEscapingFunction();
26556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
26656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
26756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
26856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * A fake command injected by AutoEscaper.
26956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   *
27056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * AutoEscaper determines the html context in which an include or lvar or evar command is called
27156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * and stores this context in the AAutoescapeCommand node.
27256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
27356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
27456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseAAutoescapeCommand(AAutoescapeCommand node) {
27556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    setLastPosition(node.getPosition());
27656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    Value value = expressionEvaluator.evaluate(node.getExpression());
27756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    String escapeStrategy = value.asString();
27856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
27956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    EscapeMode mode = EscapeMode.computeEscapeMode(escapeStrategy);
28056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
28156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    context.pushAutoEscapeMode(mode);
28256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    node.getCommand().apply(this);
28356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    context.popAutoEscapeMode();
28456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
28556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
28656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
28756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * &lt;?cs with:x=Something &gt; ... &lt;?cs /with &gt; command. Aliases a value within a specific
28856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * scope.
28956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
29056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
29156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseAWithCommand(AWithCommand node) {
29256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    setLastPosition(node.getPosition());
29356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    VariableLocator variableLocator = new VariableLocator(expressionEvaluator);
29456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    String withVar = variableLocator.getVariableName(node.getVariable());
29556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    Value value = expressionEvaluator.evaluate(node.getExpression());
29656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
29756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (value instanceof VariableValue) {
29856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      if (((VariableValue) value).getReference() == null) {
29956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        // With refers to a non-existent variable. Do nothing.
30056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        return;
30156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      }
30256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
30356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
30456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    dataContext.pushVariableScope();
30556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    setTempVariable(withVar, value);
30656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    node.getCommand().apply(this);
30756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    dataContext.popVariableScope();
30856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
30956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
31056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
31156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * &lt;?cs loop:10 &gt; ... &lt;?cs /loop &gt; command. Loops over a range of numbers, starting at
31256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * zero.
31356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
31456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
31556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseALoopToCommand(ALoopToCommand node) {
31656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    setLastPosition(node.getPosition());
31756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    int end = expressionEvaluator.evaluate(node.getExpression()).asNumber();
31856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
31956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // Start is always zero, increment is always 1, so end < 0 is invalid.
32056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (end < 0) {
32156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      return; // Incrementing the wrong way. Avoid infinite loop.
32256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
32356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
32456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    loop(node.getVariable(), 0, end, 1, node.getCommand());
32556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
32656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
32756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
32856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * &lt;?cs loop:0,10 &gt; ... &lt;?cs /loop &gt; command. Loops over a range of numbers.
32956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
33056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
33156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseALoopCommand(ALoopCommand node) {
33256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    setLastPosition(node.getPosition());
33356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    int start = expressionEvaluator.evaluate(node.getStart()).asNumber();
33456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    int end = expressionEvaluator.evaluate(node.getEnd()).asNumber();
33556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
33656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // Start is always zero, increment is always 1, so end < 0 is invalid.
33756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (end < start) {
33856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      return; // Incrementing the wrong way. Avoid infinite loop.
33956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
34056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
34156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    loop(node.getVariable(), start, end, 1, node.getCommand());
34256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
34356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
34456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
34556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * &lt;?cs loop:0,10,2 &gt; ... &lt;?cs /loop &gt; command. Loops over a range of numbers, with a
34656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * specific increment.
34756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
34856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
34956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseALoopIncCommand(ALoopIncCommand node) {
35056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    setLastPosition(node.getPosition());
35156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    int start = expressionEvaluator.evaluate(node.getStart()).asNumber();
35256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    int end = expressionEvaluator.evaluate(node.getEnd()).asNumber();
35356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    int incr = expressionEvaluator.evaluate(node.getIncrement()).asNumber();
35456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
35556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (incr == 0) {
35656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      return; // No increment. Avoid infinite loop.
35756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
35856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (incr > 0 && start > end) {
35956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      return; // Incrementing the wrong way. Avoid infinite loop.
36056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
36156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (incr < 0 && start < end) {
36256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      return; // Incrementing the wrong way. Avoid infinite loop.
36356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
36456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
36556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    loop(node.getVariable(), start, end, incr, node.getCommand());
36656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
36756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
36856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
36956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * &lt;?cs each:x=Stuff &gt; ... &lt;?cs /each &gt; command. Loops over child items of a data
37056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * node.
37156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
37256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
37356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseAEachCommand(AEachCommand node) {
37456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    setLastPosition(node.getPosition());
37556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    Value expression = expressionEvaluator.evaluate(node.getExpression());
37656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
37756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (expression instanceof VariableValue) {
37856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      VariableValue variableValue = (VariableValue) expression;
37956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      Data parent = variableValue.getReference();
38056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      if (parent != null) {
38156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        each(node.getVariable(), variableValue.getName(), parent, node.getCommand());
38256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      }
38356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
38456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
38556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
38656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
38756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * &lt;?cs alt:someValue &gt; ... &lt;?cs /alt &gt; command. If value exists, write it, otherwise
38856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * write the body of the command.
38956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
39056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
39156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseAAltCommand(AAltCommand node) {
39256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    setLastPosition(node.getPosition());
39356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    Value value = expressionEvaluator.evaluate(node.getExpression());
39456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (value.asBoolean()) {
39556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      writeVariable(value);
39656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    } else {
39756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      node.getCommand().apply(this);
39856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
39956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
40056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
40156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private void writeVariable(Value value) {
40256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (template.getEscapeMode().isAutoEscapingMode()) {
40356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      autoEscapeAndWriteVariable(value);
40456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    } else if (value.isPartiallyEscaped()) {
40556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      context.writeUnescaped(value.asString());
40656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    } else {
40756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      context.writeEscaped(value.asString());
40856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
40956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
41056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
41156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private void autoEscapeAndWriteVariable(Value value) {
41256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (isTrustedValue(value) || value.isPartiallyEscaped()) {
41356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      context.writeUnescaped(value.asString());
41456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    } else {
41556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      context.writeEscaped(value.asString());
41656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
41756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
41856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
41956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private boolean isTrustedValue(Value value) {
42056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // True if PropagateEscapeStatus is enabled and value has either been
42156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // escaped or contains a constant string.
42256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    return context.getAutoEscapeOptions().getPropagateEscapeStatus()
42356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        && !value.getEscapeMode().equals(EscapeMode.ESCAPE_NONE);
42456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
42556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
42656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  // ------------------------------------------------------------------------
42756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  // MACROS
42856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
42956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
43056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * &lt;?cs def:someMacro(x,y) &gt; ... &lt;?cs /def &gt; command. Define a macro (available for
43156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * the remainder of the interpreter context.
43256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
43356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
43456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseADefCommand(ADefCommand node) {
43556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    String macroName = makeWord(node.getMacro());
43656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    LinkedList<PVariable> arguments = node.getArguments();
43756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    String[] argumentNames = new String[arguments.size()];
43856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    int i = 0;
43956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    for (PVariable argument : arguments) {
44056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      if (!(argument instanceof ANameVariable)) {
44156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        throw new JSilverInterpreterException("Invalid name for macro '" + macroName
44256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson            + "' argument " + i + " : " + argument);
44356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      }
44456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      argumentNames[i++] = ((ANameVariable) argument).getWord().getText();
44556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
44656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // TODO: Should we enforce that macro args can't repeat the same
44756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // name?
44856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    context.registerMacro(macroName, new InterpretedMacro(node.getCommand(), template, macroName,
44956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        argumentNames, this, context));
45056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
45156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
45256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private String makeWord(LinkedList<TWord> words) {
45356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (words.size() == 1) {
45456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      return words.getFirst().getText();
45556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
45656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    StringBuilder result = new StringBuilder();
45756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    for (TWord word : words) {
45856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      if (result.length() > 0) {
45956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        result.append('.');
46056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      }
46156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      result.append(word.getText());
46256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
46356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    return result.toString();
46456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
46556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
46656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
46756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * &lt;?cs call:someMacro(x,y) command. Call a macro. Need to create a new variable scope to hold
46856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * the local variables defined by the parameters of the macro definition
46956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
47056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
47156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseACallCommand(ACallCommand node) {
47256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    String macroName = makeWord(node.getMacro());
47356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    Macro macro = context.findMacro(macroName);
47456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
47556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // Make sure that the number of arguments passed to the macro match the
47656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // number expected.
47756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (node.getArguments().size() != macro.getArgumentCount()) {
47856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      throw new JSilverInterpreterException("Number of arguments to macro " + macroName + " ("
47956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson          + node.getArguments().size() + ") does not match " + "number of expected arguments ("
48056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson          + macro.getArgumentCount() + ")");
48156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
48256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
48356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    int numArgs = node.getArguments().size();
48456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (numArgs > 0) {
48556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      Value[] argValues = new Value[numArgs];
48656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
48756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      // We must first evaluate the parameters we are passing or there could be
48856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      // conflicts if new argument names match existing variables.
48956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      Iterator<PExpression> argumentValues = node.getArguments().iterator();
49056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      for (int i = 0; argumentValues.hasNext(); i++) {
49156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        argValues[i] = expressionEvaluator.evaluate(argumentValues.next());
49256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      }
49356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
49456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      // No need to bother pushing and popping the variable scope stack
49556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      // if there are no new local variables to declare.
49656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      dataContext.pushVariableScope();
49756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
49856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      for (int i = 0; i < argValues.length; i++) {
49956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        setTempVariable(macro.getArgumentName(i), argValues[i]);
50056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      }
50156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
50256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    try {
50356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      macro.render(context);
50456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    } catch (IOException e) {
50556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      throw new JSilverIOException(e);
50656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
50756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (numArgs > 0) {
50856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      // No need to bother pushing and popping the variable scope stack
50956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      // if there are no new local variables to declare.
51056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      dataContext.popVariableScope();
51156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
51256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
51356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
51456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  // ------------------------------------------------------------------------
51556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  // HELPERS
51656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  //
51756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  // Much of the functionality in this section could easily be inlined,
51856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  // however it makes the rest of the interpreter much easier to understand
51956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  // and refactor with them defined here.
52056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
52156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private void each(PVariable variable, String parentName, Data items, PCommand command) {
52256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // Since HDF variables are now passed to macro parameters by path name
52356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // we need to create a path for each child when generating the
52456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // VariableValue object.
52556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    VariableLocator variableLocator = new VariableLocator(expressionEvaluator);
52656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    String eachVar = variableLocator.getVariableName(variable);
52756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    StringBuilder pathBuilder = new StringBuilder(parentName);
52856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    pathBuilder.append('.');
52956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    int length = pathBuilder.length();
53056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    dataContext.pushVariableScope();
53156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    for (Data child : items.getChildren()) {
53256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      pathBuilder.delete(length, pathBuilder.length());
53356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      pathBuilder.append(child.getName());
53456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      setTempVariable(eachVar, Value.variableValue(pathBuilder.toString(), dataContext));
53556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      command.apply(this);
53656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
53756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    dataContext.popVariableScope();
53856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
53956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
54056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private void loop(PVariable loopVar, int start, int end, int incr, PCommand command) {
54156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    VariableLocator variableLocator = new VariableLocator(expressionEvaluator);
54256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    String varName = variableLocator.getVariableName(loopVar);
54356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
54456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    dataContext.pushVariableScope();
54556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // Loop deals with counting forward or backwards.
54656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    for (int index = start; incr > 0 ? index <= end : index >= end; index += incr) {
54756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      // We reuse the same scope for efficiency and simply overwrite the
54856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      // previous value of the loop variable.
54956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      dataContext.createLocalVariableByValue(varName, String.valueOf(index), index == start,
55056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson          index == end);
55156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
55256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      command.apply(this);
55356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
55456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    dataContext.popVariableScope();
55556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
55656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
55756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
55856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * Code common to all three include commands.
55956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   *
56056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * @param expression expression representing name of file to include.
56156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * @param ignoreMissingFile {@code true} if any FileNotFound error generated by the template
56256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   *        loader should be ignored, {@code false} otherwise.
56356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
56456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private void include(PExpression expression, boolean ignoreMissingFile) {
56556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // Evaluate expression.
56656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    Value path = expressionEvaluator.evaluate(expression);
56756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
56856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    String templateName = path.asString();
56956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (!context.pushIncludeStackEntry(templateName)) {
57056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      throw new JSilverInterpreterException(createIncludeLoopErrorMessage(templateName, context
57156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson          .getIncludedTemplateNames()));
57256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
57356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
57456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    loadAndRenderIncludedTemplate(templateName, ignoreMissingFile);
57556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
57656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (!context.popIncludeStackEntry(templateName)) {
57756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      // Include stack trace is corrupted
57856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      throw new IllegalStateException("Unable to find on include stack: " + templateName);
57956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
58056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
58156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
58256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private String createIncludeLoopErrorMessage(String templateName, Iterable<String> includeStack) {
58356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    StringBuilder message = new StringBuilder();
58456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    message.append("File included twice: ");
58556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    message.append(templateName);
58656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
58756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    message.append(" Include stack:");
58856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    for (String fileName : includeStack) {
58956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      message.append("\n -> ");
59056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      message.append(fileName);
59156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
59256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    message.append("\n -> ");
59356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    message.append(templateName);
59456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    return message.toString();
59556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
59656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
59756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private String createUnsupportedOperationMessage(PCommand node, Iterable<String> includeStack) {
59856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    StringBuilder message = new StringBuilder();
59956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
60056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    message.append("exception thrown while parsing node: ");
60156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    message.append(node.toString());
60256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    message.append(" (class ").append(node.getClass().getSimpleName()).append(")");
60356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    message.append("\nTemplate include stack: ");
60456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
60556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    for (Iterator<String> iter = includeStack.iterator(); iter.hasNext();) {
60656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      message.append(iter.next());
60756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      if (iter.hasNext()) {
60856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        message.append(" -> ");
60956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      }
61056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
61156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    message.append("\n");
61256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
61356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    return message.toString();
61456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
61556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
61656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  // This method should ONLY be called from include()
61756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private void loadAndRenderIncludedTemplate(String templateName, boolean ignoreMissingFile) {
61856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // Now load new template with given name.
61956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    Template template = null;
62056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    try {
62156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      template =
62256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson          templateLoader.load(templateName, context.getResourceLoader(), context
62356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson              .getAutoEscapeMode());
62456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    } catch (RuntimeException e) {
62556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      if (ignoreMissingFile && ExceptionUtil.isFileNotFoundException(e)) {
62656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        return;
62756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      } else {
62856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        throw e;
62956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      }
63056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
63156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
63256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // Intepret loaded template.
63356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    try {
63456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      // TODO: Execute lincludes (but not includes) in a separate
63556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      // context.
63656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      template.render(context);
63756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    } catch (IOException e) {
63856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      throw new JSilverInterpreterException(e.getMessage());
63956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
64056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
64156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
64256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private void setLastPosition(PPosition position) {
64356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // Walks position node which will eventually result in calling
64456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // caseTCsOpen().
64556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    position.apply(this);
64656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
64756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
64856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
64956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * Every time a &lt;cs token is found, grab the line and position (for helpful error messages).
65056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
65156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
65256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseTCsOpen(TCsOpen node) {
65356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    int line = node.getLine();
65456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    int column = node.getPos();
65556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    context.setCurrentPosition(line, column);
65656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
65756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
65856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private void setTempVariable(String variableName, Value value) {
65956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (value instanceof VariableValue) {
66056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      // If the value is a Data variable name, then we store a reference to its
66156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      // name as discovered by the expression evaluator and resolve it each
66256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      // time for correctness.
66356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      dataContext.createLocalVariableByPath(variableName, ((VariableValue) value).getName());
66456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    } else {
66556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      dataContext.createLocalVariableByValue(variableName, value.asString(), value.getEscapeMode());
66656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
66756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
66856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
66956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson}
670