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