AbstractNode.java
001 package net.sourceforge.pmd.lang.ast;
002 
003 import java.util.ArrayList;
004 import java.util.Iterator;
005 import java.util.List;
006 
007 import javax.xml.parsers.DocumentBuilder;
008 import javax.xml.parsers.DocumentBuilderFactory;
009 import javax.xml.parsers.ParserConfigurationException;
010 
011 import net.sourceforge.pmd.lang.ast.xpath.Attribute;
012 import net.sourceforge.pmd.lang.ast.xpath.DocumentNavigator;
013 import net.sourceforge.pmd.lang.dfa.DataFlowNode;
014 
015 import org.jaxen.BaseXPath;
016 import org.jaxen.JaxenException;
017 import org.w3c.dom.Document;
018 import org.w3c.dom.Element;
019 
020 public abstract class AbstractNode implements Node {
021 
022     protected Node parent;
023     protected Node[] children;
024     protected int id;
025 
026     private String image;
027     protected int beginLine = -1;
028     protected int endLine;
029     protected int beginColumn = -1;
030     protected int endColumn;
031     private DataFlowNode dataFlowNode;
032 
033     public AbstractNode(int id) {
034   this.id = id;
035     }
036 
037     public void jjtOpen() {
038     }
039 
040     public void jjtClose() {
041     }
042 
043     public void jjtSetParent(Node parent) {
044   this.parent = parent;
045     }
046 
047     public Node jjtGetParent() {
048   return parent;
049     }
050 
051     public void jjtAddChild(Node child, int index) {
052   if (children == null) {
053       children = new Node[index + 1];
054   else if (index >= children.length) {
055       Node newChildren[] new Node[index + 1];
056       System.arraycopy(children, 0, newChildren, 0, children.length);
057       children = newChildren;
058   }
059   children[index= child;
060     }
061 
062     public Node jjtGetChild(int index) {
063   return children[index];
064     }
065 
066     public int jjtGetNumChildren() {
067   return children == null : children.length;
068     }
069 
070     public int jjtGetId() {
071   return id;
072     }
073 
074     /**
075      * Subclasses should implement this method to return a name usable with
076      * XPathRule for evaluating Element Names.
077      */
078     @Override
079     public abstract String toString();
080 
081     public String getImage() {
082   return image;
083     }
084 
085     public void setImage(String image) {
086   this.image = image;
087     }
088 
089     public boolean hasImageEqualTo(String image) {
090   return this.image != null && this.image.equals(image);
091     }
092 
093     public int getBeginLine() {
094   return beginLine;
095     }
096 
097     public void testingOnly__setBeginLine(int i) {
098   this.beginLine = i;
099     }
100 
101     public int getBeginColumn() {
102   if (beginColumn != -1) {
103       return beginColumn;
104   else {
105       if (children != null && children.length > 0) {
106     return children[0].getBeginColumn();
107       else {
108     throw new RuntimeException("Unable to determine begining line of Node.");
109       }
110   }
111     }
112 
113     public void testingOnly__setBeginColumn(int i) {
114   this.beginColumn = i;
115     }
116 
117     public int getEndLine() {
118   return endLine;
119     }
120 
121     public void testingOnly__setEndLine(int i) {
122   this.endLine = i;
123     }
124 
125     public int getEndColumn() {
126   return endColumn;
127     }
128 
129     public void testingOnly__setEndColumn(int i) {
130   this.endColumn = i;
131     }
132 
133     public DataFlowNode getDataFlowNode() {
134   if (this.dataFlowNode == null) {
135       if (this.parent != null) {
136     return parent.getDataFlowNode();
137       }
138       return null//TODO wise?
139   }
140   return dataFlowNode;
141     }
142 
143     public void setDataFlowNode(DataFlowNode dataFlowNode) {
144   this.dataFlowNode = dataFlowNode;
145     }
146 
147     /**
148      * Returns the n-th parent or null if there are not <code>n</code> ancestors
149      *
150      @param n how many ancestors to iterate over.
151      @return the n-th parent or null.
152      @throws IllegalArgumentException if <code>n</code> is not positive.
153      */
154     public Node getNthParent(int n) {
155         if (n <= 0) {
156             throw new IllegalArgumentException();
157         }
158         Node result = this.jjtGetParent();
159         for (int i = 1; i < n; i++) {
160             if (result == null) {
161                 return null;
162             }
163             result = result.jjtGetParent();
164         }
165         return result;
166     }
167 
168     /**
169      * Traverses up the tree to find the first parent instance of type parentType
170      *
171      @param parentType class which you want to find.
172      @return Node of type parentType.  Returns null if none found.
173      */
174     public <T> T getFirstParentOfType(Class<T> parentType) {
175   Node parentNode = jjtGetParent();
176   while (parentNode != null && parentNode.getClass() != parentType) {
177       parentNode = parentNode.jjtGetParent();
178   }
179   return (TparentNode;
180     }
181 
182     /**
183      * Traverses up the tree to find all of the parent instances of type parentType
184      *
185      @param parentType classes which you want to find.
186      @return List of parentType instances found.
187      */
188     public <T> List<T> getParentsOfType(Class<T> parentType) {
189   List<T> parents = new ArrayList<T>();
190   Node parentNode = jjtGetParent();
191   while (parentNode != null) {
192       if (parentNode.getClass() == parentType) {
193     parents.add((TparentNode);
194       }
195       parentNode = parentNode.jjtGetParent();
196   }
197   return parents;
198     }
199 
200     /**
201      * {@inheritDoc}
202      */
203     public <T> List<T> findDescendantsOfType(Class<T> targetType) {
204   List<T> list = new ArrayList<T>();
205   findDescendantsOfType(this, targetType, list, true);
206   return list;
207     }
208 
209     /**
210      * {@inheritDoc}
211      */
212     public <T> void findDescendantsOfType(Class<T> targetType, List<T> results, boolean crossBoundaries) {
213   findDescendantsOfType(this, targetType, results, crossBoundaries);
214     }
215 
216     private static <T> void findDescendantsOfType(Node node, Class<T> targetType, List<T> results,
217       boolean crossFindBoundaries) {
218 
219   if (!crossFindBoundaries && node.isFindBoundary()) {
220       return;
221   }
222 
223   int n = node.jjtGetNumChildren();
224   for (int i = 0; i < n; i++) {
225       Node child = node.jjtGetChild(i);
226       if (child.getClass() == targetType) {
227     results.add((Tchild);
228       }
229 
230       findDescendantsOfType(child, targetType, results, crossFindBoundaries);
231   }
232     }
233 
234     /**
235      * {@inheritDoc}
236      */
237     public <T> List<T> findChildrenOfType(Class<T> targetType) {
238   List<T> list = new ArrayList<T>();
239   int n = jjtGetNumChildren();
240   for (int i = 0; i < n; i++) {
241       Node child = jjtGetChild(i);
242       if (child.getClass() == targetType) {
243     list.add((Tchild);
244       }
245   }
246   return list;
247     }
248 
249     public boolean isFindBoundary() {
250   return false;
251     }
252 
253     public Document getAsDocument() {
254   try {
255       DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
256       DocumentBuilder db = dbf.newDocumentBuilder();
257       Document document = db.newDocument();
258       appendElement(document);
259       return document;
260   catch (ParserConfigurationException pce) {
261       throw new RuntimeException(pce);
262   }
263     }
264 
265     protected void appendElement(org.w3c.dom.Node parentNode) {
266   DocumentNavigator docNav = new DocumentNavigator();
267   Document ownerDocument = parentNode.getOwnerDocument();
268   if (ownerDocument == null) {
269       //If the parentNode is a Document itself, it's ownerDocument is null
270       ownerDocument = (DocumentparentNode;
271   }
272   String elementName = docNav.getElementName(this);
273   Element element = ownerDocument.createElement(elementName);
274   parentNode.appendChild(element);
275   for (Iterator<Attribute> iter = docNav.getAttributeAxisIterator(this); iter.hasNext();) {
276       Attribute attr = iter.next();
277       element.setAttribute(attr.getName(), attr.getStringValue());
278   }
279   for (Iterator<Node> iter = docNav.getChildAxisIterator(this); iter.hasNext();) {
280       AbstractNode child = (AbstractNodeiter.next();
281       child.appendElement(element);
282   }
283     }
284 
285     /**
286      * {@inheritDoc}
287      */
288     public <T> T getFirstDescendantOfType(Class<T> descendantType) {
289   return getFirstDescendantOfType(descendantType, this);
290     }
291 
292     /**
293      * {@inheritDoc}
294      */
295     public <T> T getFirstChildOfType(Class<T> childType) {
296   int n = jjtGetNumChildren();
297   for (int i = 0; i < n; i++) {
298       Node child = jjtGetChild(i);
299       if (child.getClass() == childType) {
300     return (Tchild;
301       }
302   }
303   return null;
304     }
305 
306     private static <T> T getFirstDescendantOfType(Class<T> descendantType, Node node) {
307   int n = node.jjtGetNumChildren();
308   for (int i = 0; i < n; i++) {
309       Node n1 = node.jjtGetChild(i);
310       if (n1.getClass() == descendantType) {
311     return (Tn1;
312       }
313       T n2 = getFirstDescendantOfType(descendantType, n1);
314       if (n2 != null) {
315     return n2;
316       }
317   }
318   return null;
319     }
320 
321     /**
322      * {@inheritDoc}
323      */
324     public final <T> boolean hasDescendantOfType(Class<T> type) {
325   return getFirstDescendantOfType(type!= null;
326     }
327 
328     /**
329      * {@inheritDoc}
330      */
331     public List findChildNodesWithXPath(String xpathStringthrows JaxenException {
332         return new BaseXPath(xpathString, new DocumentNavigator()).selectNodes(this);
333     }
334 
335     /**
336      * {@inheritDoc}
337      */
338     public boolean hasDescendantMatchingXPath(String xpathString) {
339         try {
340             return !findChildNodesWithXPath(xpathString).isEmpty();
341         catch (JaxenException e) {
342             throw new RuntimeException("XPath expression " + xpathString + " failed: " + e.getLocalizedMessage(), e);
343         }
344     }
345 }