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 }