1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.kew.xml.xstream;
17
18 import java.util.ArrayList;
19 import java.util.Iterator;
20 import java.util.List;
21
22 import javax.xml.xpath.XPath;
23 import javax.xml.xpath.XPathConstants;
24 import javax.xml.xpath.XPathExpressionException;
25
26 import org.apache.commons.lang.StringUtils;
27 import org.kuali.rice.kew.rule.xmlrouting.XPathHelper;
28 import org.w3c.dom.NamedNodeMap;
29 import org.w3c.dom.Node;
30 import org.w3c.dom.NodeList;
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 public class XStreamSafeEvaluator {
53
54 private static final String MATCH_ANY = "//";
55 private static final String MATCH_ROOT = "/";
56 private static final String MATCH_CURRENT = ".";
57 private static final String XSTREAM_REFERENCE_ATTRIBUTE = "reference";
58 private XPath xpath;
59
60 public XStreamSafeEvaluator() {}
61
62 public XStreamSafeEvaluator(XPath xpath) {
63 this.xpath = xpath;
64 }
65
66
67
68
69
70
71 public NodeList evaluate(String xPathExpression, Node rootSearchNode) throws XPathExpressionException {
72 XPath xpathEval = this.getXpath();
73 List segments = new ArrayList();
74 parseExpression(segments, xPathExpression, true);
75 SimpleNodeList nodes = new SimpleNodeList();
76 nodes.getList().add(rootSearchNode);
77 for (Iterator iterator = segments.iterator(); iterator.hasNext();) {
78 SimpleNodeList newNodeList = new SimpleNodeList();
79 XPathSegment expression = (XPathSegment) iterator.next();
80 for (Iterator nodeIterator = nodes.getList().iterator(); nodeIterator.hasNext();) {
81 Node node = (Node)nodeIterator.next();
82 node = resolveNodeReference(xpathEval, node);
83 if (node != null) {
84 NodeList evalSet = (NodeList)xpathEval.evaluate(expression.getXPathExpression(), node, XPathConstants.NODESET);
85 if (evalSet != null) {
86 for (int nodeIndex = 0; nodeIndex < evalSet.getLength(); nodeIndex++) {
87 Node newNode = evalSet.item(nodeIndex);
88 newNodeList.getList().add(newNode);
89 }
90 }
91 }
92 }
93 nodes = newNodeList;
94 }
95
96
97
98 SimpleNodeList newNodes = new SimpleNodeList();
99 for (Iterator iterator = nodes.getList().iterator(); iterator.hasNext();) {
100 Node node = (Node) iterator.next();
101 newNodes.getList().add(resolveNodeReference(xpathEval, node));
102 }
103 return newNodes;
104 }
105
106
107
108
109 private void parseExpression(List segments, String xPathExpression, boolean isInitialSegment) throws XPathExpressionException {
110 if (StringUtils.isEmpty(xPathExpression)) {
111 return;
112 }
113 XPathSegment segment = isInitialSegment ? parseInitialSegment(xPathExpression) : parseNextSegment(xPathExpression);
114 segments.add(segment);
115 parseExpression(segments, xPathExpression.substring(segment.getLength()), false);
116 }
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149 private XPathSegment parseInitialSegment(String xPathExpression) throws XPathExpressionException {
150
151 if (xPathExpression.startsWith(MATCH_CURRENT+MATCH_ANY)) {
152 throw new XPathExpressionException("XStream safe evaluator currenlty does not support expressions that start with " +MATCH_CURRENT+MATCH_ANY);
153 }
154
155
156
157 int operatorLength = 2;
158 int firstIndex = xPathExpression.indexOf(MATCH_CURRENT+MATCH_ROOT);
159 if (firstIndex != 0) {
160 firstIndex = xPathExpression.indexOf(MATCH_ANY);
161 if (firstIndex != 0) {
162 operatorLength = 1;
163 firstIndex = xPathExpression.indexOf(MATCH_ROOT);
164 }
165 }
166
167
168 if (firstIndex != 0) {
169 throw new XPathExpressionException("Could not locate an appropriate ./, /, or // operator at the begginingg of the xpath segment: " + xPathExpression);
170 }
171 int nextIndex = xPathExpression.indexOf(MATCH_ROOT, operatorLength);
172 if (nextIndex == -1) {
173 nextIndex = xPathExpression.length();
174 }
175 return new XPathSegment(xPathExpression.substring(0, operatorLength),
176 xPathExpression.substring(operatorLength, nextIndex), true);
177 }
178
179
180
181
182
183
184
185 private XPathSegment parseNextSegment(String xPathExpression) throws XPathExpressionException {
186 if (!xPathExpression.startsWith(MATCH_ROOT)) {
187 throw new XPathExpressionException("Illegal xPath segment, the given segment is not a valid segment and should start with a '"+MATCH_ROOT+"'. Value was: " + xPathExpression);
188 }
189 int operatorLength = MATCH_ROOT.length();
190 int nextIndex = xPathExpression.indexOf(MATCH_ROOT, operatorLength);
191 if (nextIndex == -1) {
192 nextIndex = xPathExpression.length();
193 }
194 return new XPathSegment(MATCH_CURRENT+MATCH_ROOT, xPathExpression.substring(operatorLength, nextIndex), false);
195 }
196
197
198
199
200
201
202
203
204 private Node resolveNodeReference(XPath xpath, Node node) throws XPathExpressionException{
205 NamedNodeMap attributes = node.getAttributes();
206 if (attributes != null) {
207 Node referenceNode = attributes.getNamedItem(XSTREAM_REFERENCE_ATTRIBUTE);
208 if (referenceNode != null) {
209 node = (Node)xpath.evaluate(referenceNode.getNodeValue(), node, XPathConstants.NODE);
210 if (node != null) {
211 node = resolveNodeReference(xpath, node);
212 } else {
213 throw new XPathExpressionException("Could not locate the node for the given XStream references expression: '" + referenceNode.getNodeValue() + "'");
214 }
215 }
216 }
217 return node;
218 }
219
220
221
222
223 private class XPathSegment {
224 private final String operator;
225 private final String value;
226 private final boolean isInitialSegment;
227 public XPathSegment(String operator, String value, boolean isInitialSegment) {
228 this.operator = operator;
229 this.value = value;
230
231 this.isInitialSegment = isInitialSegment;
232 }
233 public int getLength() {
234
235 if (!isInitialSegment) {
236 return operator.length() + value.length() - 1;
237 }
238 return operator.length() + value.length();
239 }
240
241
242
243
244 public String getXPathExpression() {
245 return operator+value;
246 }
247 }
248
249
250
251
252
253 private class SimpleNodeList implements NodeList {
254 private List nodes = new ArrayList();
255 public Node item(int index) {
256 return (Node)nodes.get(index);
257 }
258 public int getLength() {
259 return nodes.size();
260 }
261 public List getList() {
262 return nodes;
263 }
264 }
265
266 public XPath getXpath() {
267 if (this.xpath == null) {
268 return XPathHelper.newXPath();
269 }
270 return xpath;
271 }
272
273 public void setXpath(XPath xpath) {
274 this.xpath = xpath;
275 }
276
277 }