/* * Copyright (C) 2010 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.clearsilver.jsilver.syntax; import com.google.clearsilver.jsilver.autoescape.EscapeMode; import com.google.clearsilver.jsilver.exceptions.JSilverBadSyntaxException; import com.google.clearsilver.jsilver.exceptions.JSilverIOException; import com.google.clearsilver.jsilver.syntax.lexer.Lexer; import com.google.clearsilver.jsilver.syntax.lexer.LexerException; import com.google.clearsilver.jsilver.syntax.node.Start; import com.google.clearsilver.jsilver.syntax.node.Switch; import com.google.clearsilver.jsilver.syntax.parser.Parser; import com.google.clearsilver.jsilver.syntax.parser.ParserException; import java.io.IOException; import java.io.PushbackReader; import java.io.Reader; import java.util.Arrays; /** * Parses a JSilver text template into an abstract syntax tree (AST). *

* Acts as a facade around SableCC generated code. The simplest way to process the resulting tree is * to use a visitor by extending * {@link com.google.clearsilver.jsilver.syntax.analysis.DepthFirstAdapter} and passing it to * {@link Start#apply(com.google.clearsilver.jsilver.syntax.node.Switch)}. *

*

Example:

* *
 * SyntaxTreeBuilder builder = new SyntaxTreeBuilder();
 * Start tree = builder.parse(myTemplate, "some-template.cs");
 * // Dump out the tree
 * tree.apply(new SyntaxTreeDumper(System.out));
 * 
* */ public class SyntaxTreeBuilder { public SyntaxTreeBuilder() {} /** * Size of buffer in PushbackReader... needs to be large enough to parse CS opening tag and push * back if it is not valid. e.g. "<?csX" : not a tag, so pushback. */ private static final int PUSHBACK_SIZE = " *
  • Type resultion makes the abstract tree concrete and must come first. *
  • Sequence optimization simplifies the tree and should come before most other optimizations. *
  • Inline rewriting to remove data nodes from 'inline' sections. This should come before any * optimization of variables. *
  • Var optimization simplifies complex var expressions and must come after both type * resolution and sequence optimization. * */ protected final Switch typeResolver = new TypeResolver(); protected final Switch sequenceOptimizer = new SequenceOptimizer(); protected final Switch inlineRewriter = new InlineRewriter(); protected final Switch varOptimizer = new VarOptimizer(Arrays.asList("html", "js", "url")); /** * Perform any additional processing on the tree. EscapeMode and templateName are required by * AutoEscaper. * * @param root The AST to post process. * @param escapeMode The escaping mode to apply to the given AST. If this is not * EscapeMode.ESCAPE_NONE, AutoEscaper will be called on the AST. * @param templateName The name of template being processed. Passed to AutoEscaper, which uses it * when displaying error messages. */ protected void process(Start root, EscapeMode escapeMode, String templateName) { root.apply(typeResolver); root.apply(sequenceOptimizer); root.apply(inlineRewriter); // Temporarily disabled ('cos it doesn't quite work) // root.apply(varOptimizer); if (!escapeMode.equals(EscapeMode.ESCAPE_NONE)) { // AutoEscaper contains per-AST context like HTML parser object. // Therefore, instantiating a new AutoEscaper each time. root.apply(new AutoEscaper(escapeMode, templateName)); } } /** * @param templateName Used for meaningful error messages. * @param escapeMode Run {@link AutoEscaper} on the abstract syntax tree created from template. */ public TemplateSyntaxTree parse(Reader input, String templateName, EscapeMode escapeMode) throws JSilverIOException, JSilverBadSyntaxException { try { PushbackReader pushbackReader = new PushbackReader(input, PUSHBACK_SIZE); Lexer lexer = new Lexer(pushbackReader); Parser parser = new Parser(lexer); Start root = parser.parse(); process(root, escapeMode, templateName); return new TemplateSyntaxTree(root); } catch (IOException exception) { throw new JSilverIOException(exception); } catch (ParserException exception) { throw new JSilverBadSyntaxException(exception.getMessage(), exception.getToken().getText(), templateName, exception.getToken().getLine(), exception.getToken().getPos(), exception); } catch (LexerException exception) { throw new JSilverBadSyntaxException(exception.getMessage(), null, templateName, JSilverBadSyntaxException.UNKNOWN_POSITION, JSilverBadSyntaxException.UNKNOWN_POSITION, exception); } } }