View Javadoc

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       * Subclasses should implement this method to return a name usable with
90       * XPathRule for evaluating Element Names.
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; //TODO wise?
153 	}
154 	return dataFlowNode;
155     }
156 
157     public void setDataFlowNode(DataFlowNode dataFlowNode) {
158 	this.dataFlowNode = dataFlowNode;
159     }
160 
161     /**
162      * Returns the n-th parent or null if there are not <code>n</code> ancestors
163      *
164      * @param n how many ancestors to iterate over.
165      * @return the n-th parent or null.
166      * @throws IllegalArgumentException if <code>n</code> is not positive.
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      * Traverses up the tree to find the first parent instance of type parentType
184      *
185      * @param parentType class which you want to find.
186      * @return Node of type parentType.  Returns null if none found.
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      * Traverses up the tree to find all of the parent instances of type parentType
198      *
199      * @param parentType classes which you want to find.
200      * @return List of parentType instances found.
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      * {@inheritDoc}
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      * {@inheritDoc}
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      * {@inheritDoc}
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 	    //If the parentNode is a Document itself, it's ownerDocument is null
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      * {@inheritDoc}
301      */
302     public <T> T getFirstDescendantOfType(Class<T> descendantType) {
303 	return getFirstDescendantOfType(descendantType, this);
304     }
305 
306     /**
307      * {@inheritDoc}
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      * {@inheritDoc}
337      */
338     public final <T> boolean hasDescendantOfType(Class<T> type) {
339 	return getFirstDescendantOfType(type) != null;
340     }
341 
342     /**
343      * 
344      * @param types
345      * @return boolean
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      * {@inheritDoc}
356      */
357     public List findChildNodesWithXPath(String xpathString) throws JaxenException {
358         return new BaseXPath(xpathString, new DocumentNavigator()).selectNodes(this);
359     }
360 
361     /**
362      * {@inheritDoc}
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      * {@inheritDoc}
374      */
375     public Object getUserData() {
376         return userData;
377     }
378 
379     /**
380      * {@inheritDoc}
381      */
382     public void setUserData(Object userData) {
383         this.userData = userData;
384     }
385 }