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