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 }