1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.kew.role;
17
18 import org.apache.commons.lang.StringUtils;
19 import org.kuali.rice.core.api.exception.RiceRuntimeException;
20 import org.kuali.rice.core.api.util.xml.XmlJotter;
21 import org.kuali.rice.kew.api.KewApiConstants;
22 import org.kuali.rice.kew.api.extension.ExtensionDefinition;
23 import org.kuali.rice.kew.engine.RouteContext;
24 import org.kuali.rice.kew.rule.XmlConfiguredAttribute;
25 import org.kuali.rice.kew.rule.xmlrouting.XPathHelper;
26 import org.w3c.dom.Document;
27 import org.w3c.dom.Element;
28 import org.w3c.dom.Node;
29 import org.w3c.dom.NodeList;
30 import org.xml.sax.InputSource;
31
32 import javax.xml.xpath.XPath;
33 import javax.xml.xpath.XPathConstants;
34 import javax.xml.xpath.XPathExpressionException;
35 import java.io.StringReader;
36 import java.util.ArrayList;
37 import java.util.HashMap;
38 import java.util.List;
39 import java.util.Map;
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125 public class XPathQualifierResolver implements QualifierResolver, XmlConfiguredAttribute {
126 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(XPathQualifierResolver.class);
127
128 private ExtensionDefinition extensionDefinition;
129
130 public List<Map<String, String>> resolve(RouteContext context) {
131 ResolverConfig config = parseResolverConfig();
132 Document xmlContent = context.getDocumentContent().getDocument();
133 XPath xPath = XPathHelper.newXPath();
134 boolean isCompoundMap = config.getExpressionMap().size() > 1;
135 try {
136 List<Map<String, String>> maps = new ArrayList<Map<String, String>>();
137 NodeList baseElements = (NodeList)xPath.evaluate(config.getBaseXPathExpression(), xmlContent, XPathConstants.NODESET);
138 if (LOG.isDebugEnabled()) {
139 LOG.debug("Found " + baseElements.getLength() + " baseElements to parse for Map<String, String>s using document XML:" + XmlJotter.jotDocument(xmlContent));
140 }
141 for (int index = 0; index < baseElements.getLength(); index++) {
142 Node baseNode = baseElements.item(index);
143 if (isCompoundMap) {
144 handleCompoundMap(baseNode, maps, config, xPath);
145 } else {
146 handleSimpleMap(baseNode, maps, config, xPath);
147 }
148 }
149 return maps;
150 } catch (XPathExpressionException e) {
151 throw new RiceRuntimeException("Encountered an issue executing XPath.", e);
152 }
153 }
154
155 protected void handleCompoundMap(Node baseNode, List<Map<String, String>> maps, ResolverConfig config, XPath xPath) throws XPathExpressionException {
156 Map<String, String> map = new HashMap<String, String>();
157 for (String attributeName : config.getExpressionMap().keySet()) {
158 String xPathExpression = config.getExpressionMap().get(attributeName);
159 NodeList attributes = (NodeList)xPath.evaluate(xPathExpression, baseNode, XPathConstants.NODESET);
160 if (attributes.getLength() > 1) {
161 throw new RiceRuntimeException("Found more than more XPath result for an attribute in a compound attribute set for attribute: " + attributeName + " with expression " + xPathExpression);
162 } else if (attributes.getLength() != 0) {
163 String attributeValue = ((Element)attributes.item(0)).getTextContent();
164 if (LOG.isDebugEnabled()) {
165 LOG.debug("Adding values to compound Map<String, String>: " + attributeName + "::" + attributeValue);
166 }
167 map.put(attributeName, attributeValue);
168 }
169 }
170 maps.add(map);
171 }
172
173 protected void handleSimpleMap(Node baseNode, List<Map<String, String>> maps, ResolverConfig config, XPath xPath) throws XPathExpressionException {
174 String attributeName = config.getExpressionMap().keySet().iterator().next();
175 String xPathExpression = config.getExpressionMap().get(attributeName);
176 NodeList attributes = (NodeList)xPath.evaluate(xPathExpression, baseNode, XPathConstants.NODESET);
177 for (int index = 0; index < attributes.getLength(); index++) {
178 Element attributeElement = (Element)attributes.item(index);
179 Map<String, String> map = new HashMap<String, String>();
180 String attributeValue = attributeElement.getTextContent();
181 if (LOG.isDebugEnabled()) {
182 LOG.debug("Adding values to simple Map<String, String>: " + attributeName + "::" + attributeValue);
183 }
184 map.put(attributeName, attributeValue);
185 maps.add(map);
186 }
187 }
188
189 protected ResolverConfig parseResolverConfig() {
190 if (extensionDefinition.getConfiguration() != null
191 && extensionDefinition.getConfiguration().get(KewApiConstants.ATTRIBUTE_XML_CONFIG_DATA) == null) {
192 throw new RiceRuntimeException("Failed to locate a RuleAttribute for the given XPathQualifierResolver");
193 }
194 try {
195 ResolverConfig resolverConfig = new ResolverConfig();
196 String xmlConfig = extensionDefinition.getConfiguration().get(KewApiConstants.ATTRIBUTE_XML_CONFIG_DATA);
197 XPath xPath = XPathHelper.newXPath();
198 String baseExpression = xPath.evaluate("//resolverConfig/baseXPathExpression", new InputSource(new StringReader(xmlConfig)));
199 if (!StringUtils.isEmpty(baseExpression)) {
200 resolverConfig.setBaseXPathExpression(baseExpression);
201 }
202
203
204
205 NodeList qualifiers = (NodeList)xPath.evaluate("//resolverConfig/attributes", new InputSource(new StringReader(xmlConfig)), XPathConstants.NODESET);
206 NodeList qualifiersLegacy = (NodeList)xPath.evaluate("//resolverConfig/qualifier", new InputSource(new StringReader(xmlConfig)), XPathConstants.NODESET);
207
208 if ((qualifiers == null || qualifiers.getLength() == 0) && (qualifiersLegacy == null || qualifiersLegacy.getLength() == 0)) {
209 throw new RiceRuntimeException("Invalid qualifier resolver configuration. Must contain at least one qualifier!");
210 }
211
212 for (int index = 0; index < qualifiers.getLength(); index++) {
213 Element qualifierElement = (Element)qualifiers.item(index);
214 String name = qualifierElement.getAttribute("name");
215 NodeList expressions = qualifierElement.getElementsByTagName("xPathExpression");
216 if (expressions.getLength() != 1) {
217 throw new RiceRuntimeException("There should only be a single xPathExpression per qualifier");
218 }
219 Element expressionElement = (Element)expressions.item(0);
220 resolverConfig.getExpressionMap().put(name, expressionElement.getTextContent());
221 }
222
223
224 for (int index = 0; index < qualifiersLegacy.getLength(); index++) {
225 Element qualifierElement = (Element)qualifiersLegacy.item(index);
226 String name = qualifierElement.getAttribute("name");
227 NodeList expressions = qualifierElement.getElementsByTagName("xPathExpression");
228 if (expressions.getLength() != 1) {
229 throw new RiceRuntimeException("There should only be a single xPathExpression per qualifier");
230 }
231 Element expressionElement = (Element)expressions.item(0);
232 resolverConfig.getExpressionMap().put(name, expressionElement.getTextContent());
233 }
234 if (LOG.isDebugEnabled()) {
235 LOG.debug("Using Resolver Config Settings: " + resolverConfig.toString());
236 }
237 return resolverConfig;
238 } catch (XPathExpressionException e) {
239 throw new RiceRuntimeException("Encountered an error parsing resolver config.", e);
240 }
241 }
242
243 @Override
244 public void setExtensionDefinition(ExtensionDefinition ruleAttribute) {
245 extensionDefinition = ruleAttribute;
246 }
247
248 class ResolverConfig {
249 private String baseXPathExpression = "/";
250 private Map<String, String> expressionMap = new HashMap<String, String>();
251 public String getBaseXPathExpression() {
252 return this.baseXPathExpression;
253 }
254 public void setBaseXPathExpression(String baseXPathExpression) {
255 this.baseXPathExpression = baseXPathExpression;
256 }
257 public Map<String, String> getExpressionMap() {
258 return this.expressionMap;
259 }
260 public void setExpressionMap(Map<String, String> expressionMap) {
261 this.expressionMap = expressionMap;
262 }
263 @Override
264 public String toString() {
265 StringBuffer sb = new StringBuffer();
266 sb.append( '\n' );
267 sb.append("ResolverConfig Parameters\n");
268 sb.append( " baseXPathExpression: " + baseXPathExpression + "\n" );
269 sb.append( " expressionMap:\n" );
270 for (Map.Entry<String, String> entry : expressionMap.entrySet()) {
271 sb.append( " " + entry.getKey() + ": " + entry.getValue() + "\n" );
272 }
273 return sb.toString();
274 }
275 }
276
277 }