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 }