1
2
3
4 package net.sourceforge.pmd.lang.ecmascript.ast;
5
6 import java.lang.reflect.Constructor;
7 import java.lang.reflect.InvocationTargetException;
8 import java.util.HashMap;
9 import java.util.List;
10 import java.util.Map;
11 import java.util.Stack;
12
13 import net.sourceforge.pmd.lang.ast.Node;
14
15 import org.mozilla.javascript.ast.ArrayComprehension;
16 import org.mozilla.javascript.ast.ArrayComprehensionLoop;
17 import org.mozilla.javascript.ast.ArrayLiteral;
18 import org.mozilla.javascript.ast.Assignment;
19 import org.mozilla.javascript.ast.AstNode;
20 import org.mozilla.javascript.ast.AstRoot;
21 import org.mozilla.javascript.ast.Block;
22 import org.mozilla.javascript.ast.BreakStatement;
23 import org.mozilla.javascript.ast.CatchClause;
24 import org.mozilla.javascript.ast.Comment;
25 import org.mozilla.javascript.ast.ConditionalExpression;
26 import org.mozilla.javascript.ast.ContinueStatement;
27 import org.mozilla.javascript.ast.DoLoop;
28 import org.mozilla.javascript.ast.ElementGet;
29 import org.mozilla.javascript.ast.EmptyExpression;
30 import org.mozilla.javascript.ast.ExpressionStatement;
31 import org.mozilla.javascript.ast.ForInLoop;
32 import org.mozilla.javascript.ast.ForLoop;
33 import org.mozilla.javascript.ast.FunctionCall;
34 import org.mozilla.javascript.ast.FunctionNode;
35 import org.mozilla.javascript.ast.IfStatement;
36 import org.mozilla.javascript.ast.InfixExpression;
37 import org.mozilla.javascript.ast.KeywordLiteral;
38 import org.mozilla.javascript.ast.Label;
39 import org.mozilla.javascript.ast.LabeledStatement;
40 import org.mozilla.javascript.ast.LetNode;
41 import org.mozilla.javascript.ast.Name;
42 import org.mozilla.javascript.ast.NewExpression;
43 import org.mozilla.javascript.ast.NodeVisitor;
44 import org.mozilla.javascript.ast.NumberLiteral;
45 import org.mozilla.javascript.ast.ObjectLiteral;
46 import org.mozilla.javascript.ast.ObjectProperty;
47 import org.mozilla.javascript.ast.ParenthesizedExpression;
48 import org.mozilla.javascript.ast.ParseProblem;
49 import org.mozilla.javascript.ast.PropertyGet;
50 import org.mozilla.javascript.ast.RegExpLiteral;
51 import org.mozilla.javascript.ast.ReturnStatement;
52 import org.mozilla.javascript.ast.Scope;
53 import org.mozilla.javascript.ast.StringLiteral;
54 import org.mozilla.javascript.ast.SwitchCase;
55 import org.mozilla.javascript.ast.SwitchStatement;
56 import org.mozilla.javascript.ast.ThrowStatement;
57 import org.mozilla.javascript.ast.TryStatement;
58 import org.mozilla.javascript.ast.UnaryExpression;
59 import org.mozilla.javascript.ast.VariableDeclaration;
60 import org.mozilla.javascript.ast.VariableInitializer;
61 import org.mozilla.javascript.ast.WhileLoop;
62 import org.mozilla.javascript.ast.WithStatement;
63 import org.mozilla.javascript.ast.XmlDotQuery;
64 import org.mozilla.javascript.ast.XmlExpression;
65 import org.mozilla.javascript.ast.XmlMemberGet;
66 import org.mozilla.javascript.ast.XmlString;
67
68 public class EcmascriptTreeBuilder implements NodeVisitor {
69
70 protected static final Map<Class<? extends AstNode>, Constructor<? extends EcmascriptNode>> NODE_TYPE_TO_NODE_ADAPTER_TYPE = new HashMap<Class<? extends AstNode>, Constructor<? extends EcmascriptNode>>();
71 static {
72 register(ArrayComprehension.class, ASTArrayComprehension.class);
73 register(ArrayComprehensionLoop.class, ASTArrayComprehensionLoop.class);
74 register(ArrayLiteral.class, ASTArrayLiteral.class);
75 register(Assignment.class, ASTAssignment.class);
76 register(AstRoot.class, ASTAstRoot.class);
77 register(Block.class, ASTBlock.class);
78 register(BreakStatement.class, ASTBreakStatement.class);
79 register(CatchClause.class, ASTCatchClause.class);
80 register(Comment.class, ASTComment.class);
81 register(ConditionalExpression.class, ASTConditionalExpression.class);
82 register(ContinueStatement.class, ASTContinueStatement.class);
83 register(DoLoop.class, ASTDoLoop.class);
84 register(ElementGet.class, ASTElementGet.class);
85 register(EmptyExpression.class, ASTEmptyExpression.class);
86 register(ExpressionStatement.class, ASTExpressionStatement.class);
87 register(ForInLoop.class, ASTForInLoop.class);
88 register(ForLoop.class, ASTForLoop.class);
89 register(FunctionCall.class, ASTFunctionCall.class);
90 register(FunctionNode.class, ASTFunctionNode.class);
91 register(IfStatement.class, ASTIfStatement.class);
92 register(InfixExpression.class, ASTInfixExpression.class);
93 register(KeywordLiteral.class, ASTKeywordLiteral.class);
94 register(Label.class, ASTLabel.class);
95 register(LabeledStatement.class, ASTLabeledStatement.class);
96 register(LetNode.class, ASTLetNode.class);
97 register(Name.class, ASTName.class);
98 register(NewExpression.class, ASTNewExpression.class);
99 register(NumberLiteral.class, ASTNumberLiteral.class);
100 register(ObjectLiteral.class, ASTObjectLiteral.class);
101 register(ObjectProperty.class, ASTObjectProperty.class);
102 register(ParenthesizedExpression.class, ASTParenthesizedExpression.class);
103 register(PropertyGet.class, ASTPropertyGet.class);
104 register(RegExpLiteral.class, ASTRegExpLiteral.class);
105 register(ReturnStatement.class, ASTReturnStatement.class);
106 register(Scope.class, ASTScope.class);
107 register(StringLiteral.class, ASTStringLiteral.class);
108 register(SwitchCase.class, ASTSwitchCase.class);
109 register(SwitchStatement.class, ASTSwitchStatement.class);
110 register(ThrowStatement.class, ASTThrowStatement.class);
111 register(TryStatement.class, ASTTryStatement.class);
112 register(UnaryExpression.class, ASTUnaryExpression.class);
113 register(VariableDeclaration.class, ASTVariableDeclaration.class);
114 register(VariableInitializer.class, ASTVariableInitializer.class);
115 register(WhileLoop.class, ASTWhileLoop.class);
116 register(WithStatement.class, ASTWithStatement.class);
117 register(XmlDotQuery.class, ASTXmlDotQuery.class);
118 register(XmlExpression.class, ASTXmlExpression.class);
119 register(XmlMemberGet.class, ASTXmlMemberGet.class);
120 register(XmlString.class, ASTXmlString.class);
121 }
122
123 protected static void register(Class<? extends AstNode> nodeType, Class<? extends EcmascriptNode> nodeAdapterType) {
124 try {
125 NODE_TYPE_TO_NODE_ADAPTER_TYPE.put(nodeType, nodeAdapterType.getConstructor(nodeType));
126 } catch (SecurityException e) {
127 throw new RuntimeException(e);
128 } catch (NoSuchMethodException e) {
129 throw new RuntimeException(e);
130 }
131 }
132
133 protected List<ParseProblem> parseProblems;
134 protected Map<ParseProblem, TrailingCommaNode> parseProblemToNode = new HashMap<ParseProblem, TrailingCommaNode>();
135
136
137 protected Stack<Node> nodes = new Stack<Node>();
138
139
140 protected Stack<AstNode> parents = new Stack<AstNode>();
141
142 public EcmascriptTreeBuilder(List<ParseProblem> parseProblems) {
143 this.parseProblems = parseProblems;
144 }
145
146 protected EcmascriptNode createNodeAdapter(AstNode node) {
147 try {
148 Constructor<? extends EcmascriptNode> constructor = NODE_TYPE_TO_NODE_ADAPTER_TYPE.get(node.getClass());
149 if (constructor == null) {
150 throw new IllegalArgumentException("There is no Node adapter class registered for the Node class: "
151 + node.getClass());
152 }
153 return constructor.newInstance(node);
154 } catch (InstantiationException e) {
155 throw new RuntimeException(e);
156 } catch (IllegalAccessException e) {
157 throw new RuntimeException(e);
158 } catch (InvocationTargetException e) {
159 throw new RuntimeException(e.getTargetException());
160 }
161 }
162
163 public EcmascriptNode build(AstNode astNode) {
164 EcmascriptNode node = buildInternal(astNode);
165
166
167 for (TrailingCommaNode trailingCommaNode : parseProblemToNode.values()) {
168 trailingCommaNode.setTrailingComma(true);
169 }
170
171 return node;
172 }
173
174 protected EcmascriptNode buildInternal(AstNode astNode) {
175
176 EcmascriptNode node = createNodeAdapter(astNode);
177
178
179 Node parent = nodes.isEmpty() ? null : nodes.peek();
180 if (parent != null) {
181 parent.jjtAddChild(node, parent.jjtGetNumChildren());
182 node.jjtSetParent(parent);
183 }
184
185 handleParseProblems(node);
186
187
188 nodes.push(node);
189 parents.push(astNode);
190 astNode.visit(this);
191 nodes.pop();
192 parents.pop();
193
194 return node;
195 }
196
197 public boolean visit(AstNode node) {
198 if (parents.peek() == node) {
199 return true;
200 } else {
201 buildInternal(node);
202 return false;
203 }
204 }
205
206 private void handleParseProblems(EcmascriptNode node) {
207 if (node instanceof TrailingCommaNode) {
208 TrailingCommaNode trailingCommaNode = (TrailingCommaNode) node;
209 int nodeStart = node.getNode().getAbsolutePosition();
210 int nodeEnd = nodeStart + node.getNode().getLength() - 1;
211 for (ParseProblem parseProblem : parseProblems) {
212
213 int problemStart = parseProblem.getFileOffset();
214 int commaPosition = parseProblem.getFileOffset() + parseProblem.getLength() - 1;
215 if (nodeStart <= commaPosition && commaPosition <= nodeEnd) {
216 if ("Trailing comma is not legal in an ECMA-262 object initializer".equals(parseProblem.getMessage())) {
217
218
219 EcmascriptNode currentNode = (EcmascriptNode) parseProblemToNode.get(parseProblem);
220 if (currentNode == null || node.getNode().getLength() < currentNode.getNode().getLength()) {
221 parseProblemToNode.put(parseProblem, trailingCommaNode);
222 }
223 }
224 }
225 }
226 }
227 }
228 }