View Javadoc

1   /***
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.rules;
5   
6   import net.sourceforge.pmd.CommonAbstractRule;
7   import net.sourceforge.pmd.RuleContext;
8   import net.sourceforge.pmd.ast.ASTVariableDeclaratorId;
9   import net.sourceforge.pmd.ast.Node;
10  import net.sourceforge.pmd.ast.SimpleNode;
11  import net.sourceforge.pmd.jaxen.DocumentNavigator;
12  import net.sourceforge.pmd.jaxen.MatchesFunction;
13  import org.jaxen.BaseXPath;
14  import org.jaxen.JaxenException;
15  import org.jaxen.SimpleVariableContext;
16  import org.jaxen.XPath;
17  
18  import java.io.PrintStream;
19  import java.io.PrintWriter;
20  import java.util.Iterator;
21  import java.util.List;
22  import java.util.Map.Entry;
23  
24  /***
25   * Rule that tries to match an XPath expression against a DOM
26   * view of the AST of a "compilation unit".
27   * <p/>
28   * This rule needs a property "xpath".
29   */
30  public class XPathRule extends CommonAbstractRule {
31  
32      private XPath xpath;
33      private boolean regexpFunctionRegistered;
34  
35      /***
36       * Evaluate the AST with compilationUnit as root-node, against
37       * the XPath expression found as property with name "xpath".
38       * All matches are reported as violations.
39       *
40       * @param compilationUnit the Node that is the root of the AST to be checked
41       * @param data
42       * @return
43       */
44      public void evaluate(Node compilationUnit, RuleContext data) {
45          try {
46              initializeXPathExpression();
47              List results = xpath.selectNodes(compilationUnit);
48              for (Iterator i = results.iterator(); i.hasNext();) {
49                  SimpleNode n = (SimpleNode) i.next();
50                  if (n instanceof ASTVariableDeclaratorId && getBooleanProperty("pluginname")) {
51                      addViolation(data, n, n.getImage());
52                  } else {
53                      addViolation(data, (SimpleNode) n, getMessage());
54                  }
55              }
56          } catch (JaxenException ex) {
57              throwJaxenAsRuntime(ex);
58          }
59      }
60  
61      private void initializeXPathExpression() throws JaxenException {
62          if (xpath != null) {
63              return;
64          }
65  
66          if (!regexpFunctionRegistered) {
67              MatchesFunction.registerSelfInSimpleContext();
68              regexpFunctionRegistered = true;
69          }
70  
71          xpath = new BaseXPath(getStringProperty("xpath"), new DocumentNavigator());
72          if (properties.size() > 1) {
73              SimpleVariableContext vc = new SimpleVariableContext();
74              for (Iterator i = properties.entrySet().iterator(); i.hasNext();) {
75                  Entry e = (Entry) i.next();
76                  if (!"xpath".equals(e.getKey())) {
77                      vc.setVariableValue((String) e.getKey(), e.getValue());
78                  }
79              }
80              xpath.setVariableContext(vc);
81          }
82      }
83  
84      private static void throwJaxenAsRuntime(final JaxenException ex) {
85          throw new RuntimeException() {
86              public void printStackTrace() {
87                  super.printStackTrace();
88                  ex.printStackTrace();
89              }
90  
91              public void printStackTrace(PrintWriter writer) {
92                  super.printStackTrace(writer);
93                  ex.printStackTrace(writer);
94              }
95  
96              public void printStackTrace(PrintStream stream) {
97                  super.printStackTrace(stream);
98                  ex.printStackTrace(stream);
99              }
100 
101             public String getMessage() {
102                 return super.getMessage() + ex.getMessage();
103             }
104         };
105     }
106 
107     /***
108      * Apply the rule to all compilation units.
109      */
110     public void apply(List astCompilationUnits, RuleContext ctx) {
111         for (Iterator i = astCompilationUnits.iterator(); i.hasNext();) {
112             evaluate((Node) i.next(), ctx);
113         }
114     }
115 }