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
14 import org.jaxen.BaseXPath;
15 import org.jaxen.JaxenException;
16 import org.jaxen.SimpleVariableContext;
17 import org.jaxen.XPath;
18
19 import java.io.PrintStream;
20 import java.io.PrintWriter;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Map.Entry;
24
25 /***
26 * Rule that tries to match an XPath expression against a DOM
27 * view of the AST of a "compilation unit".
28 * <p/>
29 * This rule needs a property "xpath".
30 */
31 public class XPathRule extends CommonAbstractRule {
32
33 public static Class loadClass(ClassLoader classloader, String xpath, String name) {
34 if (xpath.indexOf('|') != -1) {
35
36 return XPathRule.class;
37 }
38 String part = xpath.trim();
39
40
41 if (!part.matches("(?s)////w+//W.*")) {
42
43 return XPathRule.class;
44 }
45
46 String tail = part.replaceFirst("^////w+", "");
47 String nodeName = part.substring(2, part.indexOf(tail));
48
49 return DynamicXPathRule.loadClass(classloader, nodeName);
50 }
51
52 private XPath xpath;
53 private boolean regexpFunctionRegistered;
54
55 /***
56 * Evaluate the AST with compilationUnit as root-node, against
57 * the XPath expression found as property with name "xpath".
58 * All matches are reported as violations.
59 *
60 * @param compilationUnit the Node that is the root of the AST to be checked
61 * @param data
62 */
63 public void evaluate(Node compilationUnit, RuleContext data) {
64 try {
65 initializeXPathExpression();
66 List results = xpath.selectNodes(compilationUnit);
67 for (Iterator i = results.iterator(); i.hasNext();) {
68 SimpleNode n = (SimpleNode) i.next();
69 if (n instanceof ASTVariableDeclaratorId && getBooleanProperty("pluginname")) {
70 addViolation(data, n, n.getImage());
71 } else {
72 addViolation(data, (SimpleNode) n, getMessage());
73 }
74 }
75 } catch (JaxenException ex) {
76 throwJaxenAsRuntime(ex);
77 }
78 }
79
80 private void initializeXPathExpression() throws JaxenException {
81 if (xpath != null) {
82 return;
83 }
84
85 if (!regexpFunctionRegistered) {
86 MatchesFunction.registerSelfInSimpleContext();
87 regexpFunctionRegistered = true;
88 }
89
90 xpath = new BaseXPath(getStringProperty("xpath"), new DocumentNavigator());
91 if (properties.size() > 1) {
92 SimpleVariableContext vc = new SimpleVariableContext();
93 for (Iterator i = properties.entrySet().iterator(); i.hasNext();) {
94 Entry e = (Entry) i.next();
95 if (!"xpath".equals(e.getKey())) {
96 vc.setVariableValue((String) e.getKey(), e.getValue());
97 }
98 }
99 xpath.setVariableContext(vc);
100 }
101 }
102
103 private static void throwJaxenAsRuntime(final JaxenException ex) {
104 throw new RuntimeException() {
105 public void printStackTrace() {
106 super.printStackTrace();
107 ex.printStackTrace();
108 }
109
110 public void printStackTrace(PrintWriter writer) {
111 super.printStackTrace(writer);
112 ex.printStackTrace(writer);
113 }
114
115 public void printStackTrace(PrintStream stream) {
116 super.printStackTrace(stream);
117 ex.printStackTrace(stream);
118 }
119
120 public String getMessage() {
121 return super.getMessage() + ex.getMessage();
122 }
123 };
124 }
125
126 /***
127 * Apply the rule to all compilation units.
128 */
129 public void apply(List astCompilationUnits, RuleContext ctx) {
130 for (Iterator i = astCompilationUnits.iterator(); i.hasNext();) {
131 evaluate((Node) i.next(), ctx);
132 }
133 }
134 }