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.JSilverIOException;
21import com.google.clearsilver.jsilver.syntax.analysis.DepthFirstAdapter;
22import com.google.clearsilver.jsilver.syntax.node.EOF;
23import com.google.clearsilver.jsilver.syntax.node.Node;
24import com.google.clearsilver.jsilver.syntax.node.Start;
25import com.google.clearsilver.jsilver.syntax.node.Token;
26
27import java.io.BufferedReader;
28import java.io.FileReader;
29import java.io.IOException;
30import java.io.Reader;
31
32/**
33 * Dumps the syntax tree to text. Useful for debugging and understanding how the tree is structured.
34 */
35public class SyntaxTreeDumper extends DepthFirstAdapter {
36
37  private final Appendable out;
38
39  private final String newLine = System.getProperty("line.separator");
40
41  private int indent;
42
43  public SyntaxTreeDumper(Appendable out) {
44    this.out = out;
45  }
46
47  /**
48   * Dumps to System.out.
49   */
50  public SyntaxTreeDumper() {
51    this(System.out);
52  }
53
54  @Override
55  public void defaultIn(Node node) {
56    write(nodeName(node) + " {");
57    indent++;
58  }
59
60  @Override
61  public void defaultOut(Node node) {
62    indent--;
63    write("}");
64  }
65
66  @Override
67  public void defaultCase(Node node) {
68    write(nodeName(node));
69  }
70
71  private String nodeName(Node node) {
72    if (node instanceof Start || node instanceof EOF) {
73      return node.getClass().getSimpleName();
74    } else if (node instanceof Token) {
75      Token token = (Token) node;
76      String tokenType = token.getClass().getSimpleName().substring(1);
77      return tokenType + " [line:" + token.getLine() + ",pos:" + token.getPos() + "] \""
78          + escape(token.getText()) + "\"";
79    } else {
80      // Turn PSomeProduction, AConcreteSomeProduction
81      // Into SomeProduction, Concrete
82      String p = node.getClass().getSuperclass().getSimpleName().substring(1);
83      String a = node.getClass().getSimpleName().substring(1);
84      a = a.substring(0, a.length() - p.length());
85      return "<" + a + ">" + p;
86    }
87
88  }
89
90  private String escape(String text) {
91    StringBuilder result = new StringBuilder();
92    for (int i = 0; i < text.length(); i++) {
93      char c = text.charAt(i);
94      switch (c) {
95        case '\\':
96          result.append("\\\\");
97          break;
98        case '"':
99          result.append("\\\"");
100          break;
101        case '\n':
102          result.append("\\n");
103          break;
104        case '\r':
105          result.append("\\r");
106          break;
107        case '\t':
108          result.append("\\t");
109          break;
110        default:
111          result.append(c);
112      }
113    }
114    return result.toString();
115  }
116
117  private void write(String text) {
118    try {
119      // Write to temp string in case output isn't buffered.
120      StringBuilder line = new StringBuilder();
121      for (int i = 0; i < indent; i++) {
122        line.append("  ");
123      }
124      line.append(text);
125      line.append(newLine);
126      out.append(line);
127    } catch (IOException e) {
128      throw new JSilverIOException(e);
129    }
130  }
131
132  /**
133   * Simple command line tool for parsing a template and dumping out the AST.
134   */
135  public static void main(String[] args) throws IOException {
136    if (args.length == 0) {
137      System.err.println("Provide filename of template.");
138      return;
139    }
140    String filename = args[0];
141    Reader reader = new BufferedReader(new FileReader(filename));
142    try {
143      SyntaxTreeBuilder builder = new SyntaxTreeBuilder();
144      TemplateSyntaxTree tree = builder.parse(reader, filename, EscapeMode.ESCAPE_NONE);
145      tree.apply(new SyntaxTreeDumper(System.out));
146    } finally {
147      reader.close();
148    }
149  }
150
151}
152