Home | History | Annotate | Download | only in syntax
      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 
     17 package com.google.clearsilver.jsilver.syntax;
     18 
     19 import com.google.clearsilver.jsilver.autoescape.EscapeMode;
     20 import com.google.clearsilver.jsilver.exceptions.JSilverBadSyntaxException;
     21 import com.google.clearsilver.jsilver.exceptions.JSilverIOException;
     22 import com.google.clearsilver.jsilver.syntax.lexer.Lexer;
     23 import com.google.clearsilver.jsilver.syntax.lexer.LexerException;
     24 import com.google.clearsilver.jsilver.syntax.node.Start;
     25 import com.google.clearsilver.jsilver.syntax.node.Switch;
     26 import com.google.clearsilver.jsilver.syntax.parser.Parser;
     27 import com.google.clearsilver.jsilver.syntax.parser.ParserException;
     28 
     29 import java.io.IOException;
     30 import java.io.PushbackReader;
     31 import java.io.Reader;
     32 import 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  */
     52 public 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