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