1 package net.sourceforge.pmd.lang.rule.xpath;
2
3 import java.util.ArrayList;
4 import java.util.HashMap;
5 import java.util.List;
6 import java.util.Map;
7
8 import net.sf.saxon.om.ValueRepresentation;
9 import net.sf.saxon.sxpath.AbstractStaticContext;
10 import net.sf.saxon.sxpath.IndependentContext;
11 import net.sf.saxon.sxpath.XPathDynamicContext;
12 import net.sf.saxon.sxpath.XPathEvaluator;
13 import net.sf.saxon.sxpath.XPathExpression;
14 import net.sf.saxon.sxpath.XPathStaticContext;
15 import net.sf.saxon.sxpath.XPathVariable;
16 import net.sf.saxon.trans.XPathException;
17 import net.sf.saxon.value.BooleanValue;
18 import net.sf.saxon.value.Int64Value;
19 import net.sf.saxon.value.StringValue;
20 import net.sourceforge.pmd.PropertyDescriptor;
21 import net.sourceforge.pmd.RuleContext;
22 import net.sourceforge.pmd.lang.ast.Node;
23 import net.sourceforge.pmd.lang.ast.xpath.saxon.DocumentNode;
24 import net.sourceforge.pmd.lang.ast.xpath.saxon.ElementNode;
25 import net.sourceforge.pmd.lang.rule.properties.BooleanProperty;
26 import net.sourceforge.pmd.lang.rule.properties.EnumeratedProperty;
27 import net.sourceforge.pmd.lang.rule.properties.IntegerProperty;
28 import net.sourceforge.pmd.lang.rule.properties.PropertyDescriptorWrapper;
29 import net.sourceforge.pmd.lang.rule.properties.StringProperty;
30 import net.sourceforge.pmd.lang.xpath.Initializer;
31
32
33
34
35 public class SaxonXPathRuleQuery extends AbstractXPathRuleQuery {
36
37
38 private XPathExpression xpathExpression;
39 private List<XPathVariable> xpathVariables;
40
41
42
43
44 @Override
45 public boolean isSupportedVersion(String version) {
46 return XPATH_1_0_COMPATIBILITY.equals(version) || XPATH_2_0.equals(version);
47 }
48
49
50
51
52 @Override
53 @SuppressWarnings("unchecked")
54 public List<Node> evaluate(Node node, RuleContext data) {
55 initializeXPathExpression();
56
57 List<Node> results = new ArrayList<Node>();
58 try {
59
60 DocumentNode documentNode = getDocumentNode(node);
61
62
63 ElementNode rootElementNode = documentNode.nodeToElementNode.get(node);
64
65
66 XPathDynamicContext xpathDynamicContext = xpathExpression.createDynamicContext(rootElementNode);
67
68
69 for (XPathVariable xpathVariable : xpathVariables) {
70 String name = xpathVariable.getVariableQName().getLocalName();
71 for (Map.Entry<PropertyDescriptor<?>, Object> entry : super.properties.entrySet()) {
72 if (name.equals(entry.getKey().name())) {
73 PropertyDescriptor<?> propertyDescriptor = entry.getKey();
74 if (propertyDescriptor instanceof PropertyDescriptorWrapper) {
75 propertyDescriptor = ((PropertyDescriptorWrapper) propertyDescriptor)
76 .getPropertyDescriptor();
77 }
78 Object value = entry.getValue();
79 ValueRepresentation valueRepresentation;
80
81
82
83 if (propertyDescriptor instanceof StringProperty) {
84 valueRepresentation = new StringValue((String) value);
85 } else if (propertyDescriptor instanceof BooleanProperty) {
86 valueRepresentation = BooleanValue.get(((Boolean) value).booleanValue());
87 } else if (propertyDescriptor instanceof IntegerProperty) {
88 valueRepresentation = Int64Value.makeIntegerValue((Integer) value);
89 } else if (propertyDescriptor instanceof EnumeratedProperty) {
90 if (value instanceof String) {
91 valueRepresentation = new StringValue((String) value);
92 } else {
93 throw new RuntimeException(
94 "Unable to create ValueRepresentaton for non-String EnumeratedProperty value: "
95 + value);
96 }
97 } else {
98 throw new RuntimeException("Unable to create ValueRepresentaton for PropertyDescriptor: "
99 + propertyDescriptor);
100 }
101 xpathDynamicContext.setVariable(xpathVariable, valueRepresentation);
102 }
103 }
104 }
105
106 List<ElementNode> nodes = xpathExpression.evaluate(xpathDynamicContext);
107 for (ElementNode elementNode : nodes) {
108 results.add((Node) elementNode.getUnderlyingNode());
109 }
110 } catch (XPathException e) {
111 throw new RuntimeException(super.xpath + " had problem: " + e.getMessage(), e);
112 }
113 return results;
114 }
115
116 private static final Map<Node, DocumentNode> CACHE = new HashMap<Node, DocumentNode>();
117
118 private DocumentNode getDocumentNode(Node node) {
119
120 Node root = node;
121 while (root.jjtGetParent() != null) {
122 root = root.jjtGetParent();
123 }
124
125
126
127 DocumentNode documentNode;
128 synchronized (CACHE) {
129 documentNode = CACHE.get(root);
130 if (documentNode == null) {
131 documentNode = new DocumentNode(root);
132 if (CACHE.size() > 20) {
133 CACHE.clear();
134 }
135 CACHE.put(root, documentNode);
136 }
137 }
138 return documentNode;
139 }
140
141 private void initializeXPathExpression() {
142 if (xpathExpression != null) {
143 return;
144 }
145 try {
146 XPathEvaluator xpathEvaluator = new XPathEvaluator();
147 XPathStaticContext xpathStaticContext = xpathEvaluator.getStaticContext();
148
149
150 if (XPATH_1_0_COMPATIBILITY.equals(version)) {
151 ((AbstractStaticContext) xpathStaticContext).setBackwardsCompatibilityMode(true);
152 }
153
154
155 Initializer.initialize((IndependentContext) xpathStaticContext);
156
157
158
159
160 xpathVariables = new ArrayList<XPathVariable>();
161 for (PropertyDescriptor<?> propertyDescriptor : super.properties.keySet()) {
162 String name = propertyDescriptor.name();
163 if (!"xpath".equals(name)) {
164 XPathVariable xpathVariable = xpathStaticContext.declareVariable(null, name);
165 xpathVariables.add(xpathVariable);
166 }
167 }
168
169
170
171
172 xpathExpression = xpathEvaluator.createExpression(super.xpath);
173 } catch (XPathException e) {
174 throw new RuntimeException(e);
175 }
176 }
177 }