SyntaxTreeBuilder.java revision 56ed4167b942ec265f9cee70ac4d71d10b3835ce
1/*
2 * Copyright (C) 2010 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.clearsilver.jsilver.syntax;
18
19import com.google.clearsilver.jsilver.autoescape.EscapeMode;
20import com.google.clearsilver.jsilver.exceptions.JSilverBadSyntaxException;
21import com.google.clearsilver.jsilver.exceptions.JSilverIOException;
22import com.google.clearsilver.jsilver.syntax.lexer.Lexer;
23import com.google.clearsilver.jsilver.syntax.lexer.LexerException;
24import com.google.clearsilver.jsilver.syntax.node.Start;
25import com.google.clearsilver.jsilver.syntax.node.Switch;
26import com.google.clearsilver.jsilver.syntax.parser.Parser;
27import com.google.clearsilver.jsilver.syntax.parser.ParserException;
28
29import java.io.IOException;
30import java.io.PushbackReader;
31import java.io.Reader;
32import java.util.Arrays;
33
34/**
35 * Parses a JSilver text template into an abstract syntax tree (AST).
36 * <p/>
37 * Acts as a facade around SableCC generated code. The simplest way to process the resulting tree is
38 * to use a visitor by extending
39 * {@link com.google.clearsilver.jsilver.syntax.analysis.DepthFirstAdapter} and passing it to
40 * {@link Start#apply(com.google.clearsilver.jsilver.syntax.node.Switch)}.
41 * <p/>
42 * <h3>Example:</h3>
43 *
44 * <pre>
45 * SyntaxTreeBuilder builder = new SyntaxTreeBuilder();
46 * Start tree = builder.parse(myTemplate, "some-template.cs");
47 * // Dump out the tree
48 * tree.apply(new SyntaxTreeDumper(System.out));
49 * </pre>
50 *
51 */
52public class SyntaxTreeBuilder {
53
54  public SyntaxTreeBuilder() {}
55
56  /**
57   * Size of buffer in PushbackReader... needs to be large enough to parse CS opening tag and push
58   * back if it is not valid. e.g. "&lt;?csX" : not a tag, so pushback.
59   */
60  private static final int PUSHBACK_SIZE = "<?cs ".length();
61
62  /**
63   * Syntax tree optimizers, declared in the order they must be applied:
64   * <ol>
65   * <li>Type resultion makes the abstract tree concrete and must come first.
66   * <li>Sequence optimization simplifies the tree and should come before most other optimizations.
67   * <li>Inline rewriting to remove data nodes from 'inline' sections. This should come before any
68   * optimization of variables.
69   * <li>Var optimization simplifies complex var expressions and must come after both type
70   * resolution and sequence optimization.
71   * </ol>
72   */
73  protected final Switch typeResolver = new TypeResolver();
74  protected final Switch sequenceOptimizer = new SequenceOptimizer();
75  protected final Switch inlineRewriter = new InlineRewriter();
76  protected final Switch varOptimizer = new VarOptimizer(Arrays.asList("html", "js", "url"));
77
78  /**
79   * Perform any additional processing on the tree. EscapeMode and templateName are required by
80   * AutoEscaper.
81   *
82   * @param root The AST to post process.
83   * @param escapeMode The escaping mode to apply to the given AST. If this is not
84   *        EscapeMode.ESCAPE_NONE, AutoEscaper will be called on the AST.
85   * @param templateName The name of template being processed. Passed to AutoEscaper, which uses it
86   *        when displaying error messages.
87   */
88  protected void process(Start root, EscapeMode escapeMode, String templateName) {
89    root.apply(typeResolver);
90    root.apply(sequenceOptimizer);
91    root.apply(inlineRewriter);
92    // Temporarily disabled ('cos it doesn't quite work)
93    // root.apply(varOptimizer);
94
95    if (!escapeMode.equals(EscapeMode.ESCAPE_NONE)) {
96      // AutoEscaper contains per-AST context like HTML parser object.
97      // Therefore, instantiating a new AutoEscaper each time.
98      root.apply(new AutoEscaper(escapeMode, templateName));
99    }
100  }
101
102  /**
103   * @param templateName Used for meaningful error messages.
104   * @param escapeMode Run {@link AutoEscaper} on the abstract syntax tree created from template.
105   */
106  public TemplateSyntaxTree parse(Reader input, String templateName, EscapeMode escapeMode)
107      throws JSilverIOException, JSilverBadSyntaxException {
108    try {
109      PushbackReader pushbackReader = new PushbackReader(input, PUSHBACK_SIZE);
110      Lexer lexer = new Lexer(pushbackReader);
111      Parser parser = new Parser(lexer);
112      Start root = parser.parse();
113      process(root, escapeMode, templateName);
114      return new TemplateSyntaxTree(root);
115    } catch (IOException exception) {
116      throw new JSilverIOException(exception);
117    } catch (ParserException exception) {
118      throw new JSilverBadSyntaxException(exception.getMessage(), exception.getToken().getText(),
119          templateName, exception.getToken().getLine(), exception.getToken().getPos(), exception);
120    } catch (LexerException exception) {
121      throw new JSilverBadSyntaxException(exception.getMessage(), null, templateName,
122          JSilverBadSyntaxException.UNKNOWN_POSITION, JSilverBadSyntaxException.UNKNOWN_POSITION,
123          exception);
124    }
125  }
126}
127