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.template;
1856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
1956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.autoescape.AutoEscapeContext;
2056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.autoescape.AutoEscapeOptions;
2156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.autoescape.EscapeMode;
2256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.data.DataContext;
2356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.data.UniqueStack;
2456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.exceptions.JSilverAutoEscapingException;
2556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.exceptions.JSilverIOException;
2656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.exceptions.JSilverInterpreterException;
2756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.functions.FunctionExecutor;
2856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.resourceloader.ResourceLoader;
2956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.values.Value;
3056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
3156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport java.io.IOException;
3256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport java.util.ArrayList;
3356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport java.util.HashMap;
3456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport java.util.List;
3556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport java.util.Map;
3656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport java.util.logging.Logger;
3756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
3856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson/**
3956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Default implementation of RenderingContext.
4056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */
4156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonpublic class DefaultRenderingContext implements RenderingContext, FunctionExecutor {
4256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
4356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public static final Logger logger = Logger.getLogger(DefaultRenderingContext.class.getName());
4456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private final DataContext dataContext;
4556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private final ResourceLoader resourceLoader;
4656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private final Appendable out;
4756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private final FunctionExecutor globalFunctionExecutor;
4856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private final AutoEscapeOptions autoEscapeOptions;
4956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private final UniqueStack<String> includeStack;
5056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
5156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private List<String> escaperStack = new ArrayList<String>(8); // seems like a reasonable initial
5256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson                                                                // capacity.
5356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private String currentEscaper; // optimization to reduce List lookup.
5456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
5556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private List<Template> executionStack = new ArrayList<Template>(8);
5656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
5756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private Map<String, Macro> macros = new HashMap<String, Macro>();
5856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private List<EscapeMode> autoEscapeStack = new ArrayList<EscapeMode>();
5956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private EscapeMode autoEscapeMode;
6056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private AutoEscapeContext autoEscapeContext;
6156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private int line;
6256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private int column;
6356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private AutoEscapeContext.AutoEscapeState startingAutoEscapeState;
6456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
6556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public DefaultRenderingContext(DataContext dataContext, ResourceLoader resourceLoader,
6656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      Appendable out, FunctionExecutor globalFunctionExecutor, AutoEscapeOptions autoEscapeOptions) {
6756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    this.dataContext = dataContext;
6856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    this.resourceLoader = resourceLoader;
6956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    this.out = out;
7056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    this.globalFunctionExecutor = globalFunctionExecutor;
7156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    this.autoEscapeOptions = autoEscapeOptions;
7256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    this.autoEscapeMode = EscapeMode.ESCAPE_NONE;
7356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    this.autoEscapeContext = null;
7456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    this.includeStack = new UniqueStack<String>();
7556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
7656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
7756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
7856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * Lookup a function by name, execute it and return the results.
7956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
8056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
8156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public Value executeFunction(String name, Value... args) {
8256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    return globalFunctionExecutor.executeFunction(name, args);
8356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
8456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
8556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
8656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void escape(String name, String input, Appendable output) throws IOException {
8756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    globalFunctionExecutor.escape(name, input, output);
8856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
8956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
9056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
9156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public boolean isEscapingFunction(String name) {
9256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    return globalFunctionExecutor.isEscapingFunction(name);
9356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
9456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
9556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
9656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void pushEscapingFunction(String name) {
9756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    escaperStack.add(currentEscaper);
9856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (name == null || name.equals("")) {
9956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      currentEscaper = null;
10056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    } else {
10156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      currentEscaper = name;
10256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
10356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
10456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
10556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
10656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void popEscapingFunction() {
10756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    int len = escaperStack.size();
10856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (len == 0) {
10956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      throw new IllegalStateException("No more escaping functions to pop.");
11056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
11156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    currentEscaper = escaperStack.remove(len - 1);
11256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
11356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
11456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
11556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void writeEscaped(String text) {
11656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // If runtime auto escaping is enabled, only apply it if
11756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // we are not going to do any other default escaping on the variable.
11856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    boolean applyAutoEscape = isRuntimeAutoEscaping() && (currentEscaper == null);
11956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (applyAutoEscape) {
12056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      autoEscapeContext.setCurrentPosition(line, column);
12156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      pushEscapingFunction(autoEscapeContext.getEscapingFunctionForCurrentState());
12256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
12356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    try {
12456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      if (shouldLogEscapedVariables()) {
12556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        StringBuilder tmp = new StringBuilder();
12656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        globalFunctionExecutor.escape(currentEscaper, text, tmp);
12756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        if (!tmp.toString().equals(text)) {
12856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson          logger.warning(new StringBuilder(getLoggingPrefix()).append(" Auto-escape changed [")
12956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson              .append(text).append("] to [").append(tmp.toString()).append("]").toString());
13056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        }
13156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        out.append(tmp);
13256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      } else {
13356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        globalFunctionExecutor.escape(currentEscaper, text, out);
13456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      }
13556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    } catch (IOException e) {
13656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      throw new JSilverIOException(e);
13756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    } finally {
13856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      if (applyAutoEscape) {
13956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        autoEscapeContext.insertText();
14056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        popEscapingFunction();
14156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      }
14256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
14356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
14456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
14556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private String getLoggingPrefix() {
14656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    return "[" + getCurrentResourceName() + ":" + line + ":" + column + "]";
14756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
14856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
14956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private boolean shouldLogEscapedVariables() {
15056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    return (autoEscapeOptions != null && autoEscapeOptions.getLogEscapedVariables());
15156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
15256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
15356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
15456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void writeUnescaped(CharSequence text) {
15556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (isRuntimeAutoEscaping() && (currentEscaper == null)) {
15656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      autoEscapeContext.setCurrentPosition(line, column);
15756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      autoEscapeContext.parseData(text.toString());
15856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
15956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    try {
16056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      out.append(text);
16156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    } catch (IOException e) {
16256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      throw new JSilverIOException(e);
16356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
16456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
16556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
16656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
16756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void pushExecutionContext(Template template) {
16856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    executionStack.add(template);
16956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
17056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
17156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
17256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void popExecutionContext() {
17356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    executionStack.remove(executionStack.size() - 1);
17456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
17556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
17656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
17756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void setCurrentPosition(int line, int column) {
17856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // TODO: Should these be saved in executionStack as part
17956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // of pushExecutionContext?
18056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    this.line = line;
18156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    this.column = column;
18256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
18356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
18456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
18556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void registerMacro(String name, Macro macro) {
18656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    macros.put(name, macro);
18756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
18856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
18956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
19056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public Macro findMacro(String name) {
19156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    Macro macro = macros.get(name);
19256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (macro == null) {
19356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      throw new JSilverInterpreterException("No such macro: " + name);
19456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
19556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    return macro;
19656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
19756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
19856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
19956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public DataContext getDataContext() {
20056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    return dataContext;
20156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
20256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
20356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
20456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public ResourceLoader getResourceLoader() {
20556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    return resourceLoader;
20656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
20756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
20856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
20956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public AutoEscapeOptions getAutoEscapeOptions() {
21056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    return autoEscapeOptions;
21156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
21256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
21356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
21456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public EscapeMode getAutoEscapeMode() {
21556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (isRuntimeAutoEscaping() || (currentEscaper != null)) {
21656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      return EscapeMode.ESCAPE_NONE;
21756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    } else {
21856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      return autoEscapeMode;
21956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
22056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
22156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
22256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
22356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void pushAutoEscapeMode(EscapeMode mode) {
22456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (isRuntimeAutoEscaping()) {
22556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      throw new JSilverInterpreterException(
22656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson          "cannot call pushAutoEscapeMode while runtime auto escaping is in progress");
22756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
22856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    autoEscapeStack.add(autoEscapeMode);
22956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    autoEscapeMode = mode;
23056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
23156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
23256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
23356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void popAutoEscapeMode() {
23456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    int len = autoEscapeStack.size();
23556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (len == 0) {
23656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      throw new IllegalStateException("No more auto escaping modes to pop.");
23756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
23856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    autoEscapeMode = autoEscapeStack.remove(autoEscapeStack.size() - 1);
23956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
24056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
24156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
24256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public boolean isRuntimeAutoEscaping() {
24356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    return autoEscapeContext != null;
24456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
24556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
24656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
24756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * {@inheritDoc}
24856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   *
24956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * @throws JSilverInterpreterException if startRuntimeAutoEscaping is called while runtime
25056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   *         autoescaping is already in progress.
25156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
25256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
25356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void startRuntimeAutoEscaping() {
25456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (isRuntimeAutoEscaping()) {
25556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      throw new JSilverInterpreterException("startRuntimeAutoEscaping() is not re-entrant at "
25656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson          + getCurrentResourceName());
25756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
25856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (!autoEscapeMode.equals(EscapeMode.ESCAPE_NONE)) {
25956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      // TODO: Get the resourceName as a parameter to this function
26056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      autoEscapeContext = new AutoEscapeContext(autoEscapeMode, getCurrentResourceName());
26156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      startingAutoEscapeState = autoEscapeContext.getCurrentState();
26256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    } else {
26356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      autoEscapeContext = null;
26456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
26556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
26656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
26756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private String getCurrentResourceName() {
26856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (executionStack.size() == 0) {
26956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      return "";
27056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    } else {
27156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      return executionStack.get(executionStack.size() - 1).getDisplayName();
27256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
27356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
27456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
27556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
27656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void stopRuntimeAutoEscaping() {
27756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (autoEscapeContext != null) {
27856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      if (!startingAutoEscapeState.equals(autoEscapeContext.getCurrentState())) {
27956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        // We do not allow a macro call to change context of the rest of the template.
28056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        // Since the rest of the template has already been auto-escaped at parse time
28156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        // with the assumption that the macro call will not modify the context.
28256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        throw new JSilverAutoEscapingException("Macro starts in context " + startingAutoEscapeState
28356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson            + " but ends in different context " + autoEscapeContext.getCurrentState(),
28456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson            autoEscapeContext.getResourceName());
28556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      }
28656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
28756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    autoEscapeContext = null;
28856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
28956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
29056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
29156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public boolean pushIncludeStackEntry(String templateName) {
29256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    return includeStack.push(templateName);
29356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
29456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
29556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
29656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public boolean popIncludeStackEntry(String templateName) {
29756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    return templateName.equals(includeStack.pop());
29856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
29956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
30056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
30156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public Iterable<String> getIncludedTemplateNames() {
30256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    return includeStack;
30356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
30456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson}
305