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