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 ? 0 : 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 (T) parentNode;
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((T) parentNode);
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((T) child);
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((T) child);
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 = (Document) parentNode;
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 = (AbstractNode) iter.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 (T) child;
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 (T) n1;
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 xpathString) throws 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 }
|