| /* |
| * 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). |
| * <p/> |
| * 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)}. |
| * <p/> |
| * <h3>Example:</h3> |
| * |
| * <pre> |
| * SyntaxTreeBuilder builder = new SyntaxTreeBuilder(); |
| * Start tree = builder.parse(myTemplate, "some-template.cs"); |
| * // Dump out the tree |
| * tree.apply(new SyntaxTreeDumper(System.out)); |
| * </pre> |
| * |
| */ |
| 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 = "<?cs ".length(); |
| |
| /** |
| * Syntax tree optimizers, declared in the order they must be applied: |
| * <ol> |
| * <li>Type resultion makes the abstract tree concrete and must come first. |
| * <li>Sequence optimization simplifies the tree and should come before most other optimizations. |
| * <li>Inline rewriting to remove data nodes from 'inline' sections. This should come before any |
| * optimization of variables. |
| * <li>Var optimization simplifies complex var expressions and must come after both type |
| * resolution and sequence optimization. |
| * </ol> |
| */ |
| 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); |
| } |
| } |
| } |