View Javadoc
1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.lang.rule;
5   
6   import static net.sourceforge.pmd.lang.rule.xpath.XPathRuleQuery.XPATH_1_0;
7   import static net.sourceforge.pmd.lang.rule.xpath.XPathRuleQuery.XPATH_1_0_COMPATIBILITY;
8   import static net.sourceforge.pmd.lang.rule.xpath.XPathRuleQuery.XPATH_2_0;
9   
10  import java.util.List;
11  
12  import net.sourceforge.pmd.PropertySource;
13  import net.sourceforge.pmd.RuleContext;
14  import net.sourceforge.pmd.lang.ast.Node;
15  import net.sourceforge.pmd.lang.rule.properties.EnumeratedProperty;
16  import net.sourceforge.pmd.lang.rule.properties.StringProperty;
17  import net.sourceforge.pmd.lang.rule.xpath.JaxenXPathRuleQuery;
18  import net.sourceforge.pmd.lang.rule.xpath.SaxonXPathRuleQuery;
19  import net.sourceforge.pmd.lang.rule.xpath.XPathRuleQuery;
20  import net.sourceforge.pmd.util.StringUtil;
21  
22  /**
23   * Rule that tries to match an XPath expression against a DOM view of an AST.
24   * <p/>
25   * This rule needs a "xpath" property value in order to function.
26   */
27  public class XPathRule extends AbstractRule {
28  
29  	public static final StringProperty XPATH_DESCRIPTOR = new StringProperty("xpath", "XPath expression", "", 1.0f);
30  	public static final EnumeratedProperty<String> VERSION_DESCRIPTOR = new EnumeratedProperty<>("version",
31  			"XPath specification version", 
32  			new String[] { XPATH_1_0, XPATH_1_0_COMPATIBILITY, XPATH_2_0 },
33  			new String[] { XPATH_1_0, XPATH_1_0_COMPATIBILITY, XPATH_2_0 }, 
34  			0, 2.0f);
35  
36  	private XPathRuleQuery xpathRuleQuery;
37  
38  	public XPathRule() {
39  		definePropertyDescriptor(XPATH_DESCRIPTOR);
40  		definePropertyDescriptor(VERSION_DESCRIPTOR);
41  	}
42  
43  	public XPathRule(String xPath) {
44  		this();
45  		setXPath(xPath);
46  	}
47  	
48  	public void setXPath(String xPath) {
49  		setProperty(XPathRule.XPATH_DESCRIPTOR, xPath);
50  	}
51  	
52  	public void setVersion(String version) {
53  		setProperty(XPathRule.VERSION_DESCRIPTOR, version);
54  	}
55  	
56  	/**
57  	 * Apply the rule to all nodes.
58  	 */
59  	public void apply(List<? extends Node> nodes, RuleContext ctx) {
60  		for (Node node : nodes) {
61  			evaluate(node, ctx);
62  		}
63  	}
64  
65  	/**
66  	 * Evaluate the XPath query with the AST node.
67  	 * All matches are reported as violations.
68  	 *
69  	 * @param node The Node that to be checked.
70  	 * @param data The RuleContext.
71  	 */
72  	public void evaluate(Node node, RuleContext data) {
73  		init();
74  		List<Node> nodes = xpathRuleQuery.evaluate(node, data);
75  		if (nodes != null) {
76  			for (Node n : nodes) {
77  				addViolation(data, n, n.getImage());
78  			}
79  		}
80  
81  	}
82  
83  	@Override
84  	public List<String> getRuleChainVisits() {
85  		if (init()) {
86  			for (String nodeName : xpathRuleQuery.getRuleChainVisits()) {
87  				super.addRuleChainVisit(nodeName);
88  			}
89  		}
90  		return super.getRuleChainVisits();
91  	}
92  
93  	private boolean init() {
94  		if (xpathRuleQuery == null) {
95  			String xpath = getProperty(XPATH_DESCRIPTOR);
96  			String version = (String) getProperty(VERSION_DESCRIPTOR);
97  			if (XPATH_1_0.equals(version)) {
98  				xpathRuleQuery = new JaxenXPathRuleQuery();
99  			} else {
100 				xpathRuleQuery = new SaxonXPathRuleQuery();
101 			}
102 			xpathRuleQuery.setXPath(xpath);
103 			xpathRuleQuery.setVersion(version);
104 			xpathRuleQuery.setProperties(this.getPropertiesByPropertyDescriptor());
105 			return true;
106 		}
107 		return false;
108 	}
109 
110 
111 	public boolean hasXPathExpression() {		
112 		String xPath = getProperty(XPATH_DESCRIPTOR);
113 		return StringUtil.isNotEmpty(xPath);
114 	}
115 
116 	/**
117 	 * @see PropertySource#dysfunctionReason()
118 	 */
119 	public String dysfunctionReason() {
120 		return hasXPathExpression() ? null : "Missing xPath expression";
121 	}
122 }