1
2
3
4 package net.sourceforge.pmd.lang.ast;
5
6 import java.util.ArrayList;
7 import java.util.Iterator;
8 import java.util.List;
9
10 import javax.xml.parsers.DocumentBuilder;
11 import javax.xml.parsers.DocumentBuilderFactory;
12 import javax.xml.parsers.ParserConfigurationException;
13
14 import net.sourceforge.pmd.lang.ast.xpath.Attribute;
15 import net.sourceforge.pmd.lang.ast.xpath.DocumentNavigator;
16 import net.sourceforge.pmd.lang.dfa.DataFlowNode;
17 import net.sourceforge.pmd.lang.ast.GenericToken;
18
19 import org.jaxen.BaseXPath;
20 import org.jaxen.JaxenException;
21 import org.w3c.dom.Document;
22 import org.w3c.dom.Element;
23
24 public abstract class AbstractNode implements Node {
25
26 protected Node parent;
27 protected Node[] children;
28 protected int childIndex;
29 protected int id;
30
31 private String image;
32 protected int beginLine = -1;
33 protected int endLine;
34 protected int beginColumn = -1;
35 protected int endColumn;
36 private DataFlowNode dataFlowNode;
37 private Object userData;
38
39 public AbstractNode(int id) {
40 this.id = id;
41 }
42
43 public AbstractNode(int id, int theBeginLine, int theEndLine, int theBeginColumn, int theEndColumn) {
44 this(id);
45
46 beginLine = theBeginLine;
47 endLine = theEndLine;
48 beginColumn = theBeginColumn;
49 endColumn = theEndColumn;
50 }
51
52 public boolean isSingleLine() {
53 return beginLine == endLine;
54 }
55
56 public void jjtOpen() {
57
58 }
59
60 public void jjtClose() {
61
62 }
63
64 public void jjtSetParent(Node parent) {
65 this.parent = parent;
66 }
67
68 public Node jjtGetParent() {
69 return parent;
70 }
71
72 public void jjtAddChild(Node child, int index) {
73 if (children == null) {
74 children = new Node[index + 1];
75 } else if (index >= children.length) {
76 Node[] newChildren = new Node[index + 1];
77 System.arraycopy(children, 0, newChildren, 0, children.length);
78 children = newChildren;
79 }
80 children[index] = child;
81 child.jjtSetChildIndex(index);
82 }
83 public void jjtSetChildIndex(int index) {
84 childIndex = index;
85 }
86 public int jjtGetChildIndex() {
87 return childIndex;
88 }
89
90 public Node jjtGetChild(int index) {
91 return children[index];
92 }
93
94 public int jjtGetNumChildren() {
95 return children == null ? 0 : children.length;
96 }
97
98 public int jjtGetId() {
99 return id;
100 }
101
102
103
104
105
106 @Override
107 public abstract String toString();
108
109 public String getImage() {
110 return image;
111 }
112
113 public void setImage(String image) {
114 this.image = image;
115 }
116
117 public boolean hasImageEqualTo(String image) {
118 return this.getImage() != null && this.getImage().equals(image);
119 }
120
121 public int getBeginLine() {
122 return beginLine;
123 }
124
125 public void testingOnly__setBeginLine(int i) {
126 this.beginLine = i;
127 }
128
129 public int getBeginColumn() {
130 if (beginColumn != -1) {
131 return beginColumn;
132 } else {
133 if (children != null && children.length > 0) {
134 return children[0].getBeginColumn();
135 } else {
136 throw new RuntimeException("Unable to determine beginning line of Node.");
137 }
138 }
139 }
140
141 public void testingOnly__setBeginColumn(int i) {
142 this.beginColumn = i;
143 }
144
145 public int getEndLine() {
146 return endLine;
147 }
148
149 public void testingOnly__setEndLine(int i) {
150 this.endLine = i;
151 }
152
153 public int getEndColumn() {
154 return endColumn;
155 }
156
157 public void testingOnly__setEndColumn(int i) {
158 this.endColumn = i;
159 }
160
161 public DataFlowNode getDataFlowNode() {
162 if (this.dataFlowNode == null) {
163 if (this.parent != null) {
164 return parent.getDataFlowNode();
165 }
166 return null;
167 }
168 return dataFlowNode;
169 }
170
171 public void setDataFlowNode(DataFlowNode dataFlowNode) {
172 this.dataFlowNode = dataFlowNode;
173 }
174
175
176
177
178
179
180
181
182 public Node getNthParent(int n) {
183 if (n <= 0) {
184 throw new IllegalArgumentException();
185 }
186 Node result = this.jjtGetParent();
187 for (int i = 1; i < n; i++) {
188 if (result == null) {
189 return null;
190 }
191 result = result.jjtGetParent();
192 }
193 return result;
194 }
195
196
197
198
199
200
201
202 public <T> T getFirstParentOfType(Class<T> parentType) {
203 Node parentNode = jjtGetParent();
204 while (parentNode != null && parentNode.getClass() != parentType) {
205 parentNode = parentNode.jjtGetParent();
206 }
207 return (T) parentNode;
208 }
209
210
211
212
213
214
215
216 public <T> List<T> getParentsOfType(Class<T> parentType) {
217 List<T> parents = new ArrayList<T>();
218 Node parentNode = jjtGetParent();
219 while (parentNode != null) {
220 if (parentNode.getClass() == parentType) {
221 parents.add((T) parentNode);
222 }
223 parentNode = parentNode.jjtGetParent();
224 }
225 return parents;
226 }
227
228
229
230
231 public <T> List<T> findDescendantsOfType(Class<T> targetType) {
232 List<T> list = new ArrayList<T>();
233 findDescendantsOfType(this, targetType, list, true);
234 return list;
235 }
236
237
238
239
240 public <T> void findDescendantsOfType(Class<T> targetType, List<T> results, boolean crossBoundaries) {
241 findDescendantsOfType(this, targetType, results, crossBoundaries);
242 }
243
244 private static <T> void findDescendantsOfType(Node node, Class<T> targetType, List<T> results,
245 boolean crossFindBoundaries) {
246
247 if (!crossFindBoundaries && node.isFindBoundary()) {
248 return;
249 }
250
251 int n = node.jjtGetNumChildren();
252 for (int i = 0; i < n; i++) {
253 Node child = node.jjtGetChild(i);
254 if (child.getClass() == targetType) {
255 results.add((T) child);
256 }
257
258 findDescendantsOfType(child, targetType, results, crossFindBoundaries);
259 }
260 }
261
262
263
264
265 public <T> List<T> findChildrenOfType(Class<T> targetType) {
266 List<T> list = new ArrayList<T>();
267 int n = jjtGetNumChildren();
268 for (int i = 0; i < n; i++) {
269 Node child = jjtGetChild(i);
270 if (child.getClass() == targetType) {
271 list.add((T) child);
272 }
273 }
274 return list;
275 }
276
277 public boolean isFindBoundary() {
278 return false;
279 }
280
281 public Document getAsDocument() {
282 try {
283 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
284 DocumentBuilder db = dbf.newDocumentBuilder();
285 Document document = db.newDocument();
286 appendElement(document);
287 return document;
288 } catch (ParserConfigurationException pce) {
289 throw new RuntimeException(pce);
290 }
291 }
292
293 protected void appendElement(org.w3c.dom.Node parentNode) {
294 DocumentNavigator docNav = new DocumentNavigator();
295 Document ownerDocument = parentNode.getOwnerDocument();
296 if (ownerDocument == null) {
297
298 ownerDocument = (Document) parentNode;
299 }
300 String elementName = docNav.getElementName(this);
301 Element element = ownerDocument.createElement(elementName);
302 parentNode.appendChild(element);
303 for (Iterator<Attribute> iter = docNav.getAttributeAxisIterator(this); iter.hasNext();) {
304 Attribute attr = iter.next();
305 element.setAttribute(attr.getName(), attr.getStringValue());
306 }
307 for (Iterator<Node> iter = docNav.getChildAxisIterator(this); iter.hasNext();) {
308 AbstractNode child = (AbstractNode) iter.next();
309 child.appendElement(element);
310 }
311 }
312
313
314
315
316 public <T> T getFirstDescendantOfType(Class<T> descendantType) {
317 return getFirstDescendantOfType(descendantType, this);
318 }
319
320
321
322
323 public <T> T getFirstChildOfType(Class<T> childType) {
324 int n = jjtGetNumChildren();
325 for (int i = 0; i < n; i++) {
326 Node child = jjtGetChild(i);
327 if (child.getClass() == childType) {
328 return (T) child;
329 }
330 }
331 return null;
332 }
333
334 private static <T> T getFirstDescendantOfType(Class<T> descendantType, Node node) {
335 int n = node.jjtGetNumChildren();
336 for (int i = 0; i < n; i++) {
337 Node n1 = node.jjtGetChild(i);
338 if (n1.getClass() == descendantType) {
339 return (T) n1;
340 }
341 T n2 = getFirstDescendantOfType(descendantType, n1);
342 if (n2 != null) {
343 return n2;
344 }
345 }
346 return null;
347 }
348
349
350
351
352 public final <T> boolean hasDescendantOfType(Class<T> type) {
353 return getFirstDescendantOfType(type) != null;
354 }
355
356
357
358
359
360
361 public final boolean hasDecendantOfAnyType(Class<?>... types) {
362 for (Class<?> type : types) {
363 if (hasDescendantOfType(type)) {
364 return true;
365 }
366 }
367 return false;
368 }
369
370
371
372
373 public List findChildNodesWithXPath(String xpathString) throws JaxenException {
374 return new BaseXPath(xpathString, new DocumentNavigator()).selectNodes(this);
375 }
376
377
378
379
380 public boolean hasDescendantMatchingXPath(String xpathString) {
381 try {
382 return !findChildNodesWithXPath(xpathString).isEmpty();
383 } catch (JaxenException e) {
384 throw new RuntimeException("XPath expression " + xpathString + " failed: " + e.getLocalizedMessage(), e);
385 }
386 }
387
388
389
390
391 public Object getUserData() {
392 return userData;
393 }
394
395
396
397
398 public void setUserData(Object userData) {
399 this.userData = userData;
400 }
401
402 protected GenericToken firstToken, lastToken;
403
404 public GenericToken jjtGetFirstToken() {
405 return firstToken;
406 }
407
408 public void jjtSetFirstToken(GenericToken token) {
409 this.firstToken = token;
410 }
411
412 public GenericToken jjtGetLastToken() {
413 return lastToken;
414 }
415
416 public void jjtSetLastToken(GenericToken token) {
417 this.lastToken = token;
418 }
419 }