Coverage Report - org.kuali.rice.kew.role.XPathQualifierResolver
 
Classes in this File Line Coverage Branch Coverage Complexity
XPathQualifierResolver
0%
0/71
0%
0/34
3.7
XPathQualifierResolver$ResolverConfig
0%
0/17
0%
0/2
3.7
 
 1  
 /**
 2  
  * Copyright 2005-2011 The Kuali Foundation
 3  
  *
 4  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  * http://www.opensource.org/licenses/ecl2.php
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 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.extension.ExtensionDefinition;
 22  
 import org.kuali.rice.kew.engine.RouteContext;
 23  
 import org.kuali.rice.kew.rule.xmlrouting.XPathHelper;
 24  
 import org.w3c.dom.Document;
 25  
 import org.w3c.dom.Element;
 26  
 import org.w3c.dom.Node;
 27  
 import org.w3c.dom.NodeList;
 28  
 import org.xml.sax.InputSource;
 29  
 
 30  
 import javax.xml.xpath.XPath;
 31  
 import javax.xml.xpath.XPathConstants;
 32  
 import javax.xml.xpath.XPathExpressionException;
 33  
 import java.io.StringReader;
 34  
 import java.util.ArrayList;
 35  
 import java.util.HashMap;
 36  
 import java.util.List;
 37  
 import java.util.Map;
 38  
 
 39  
 /**
 40  
  * Resolves qualifiers based on XPath configuration in the resolver's attribute.
 41  
  * 
 42  
  * <p>An example of the xml processed by this attribute follows:
 43  
  * 
 44  
  * <p><pre>
 45  
  * <resolverConfig>
 46  
  *   <baseXPathExpression>/xmlData/chartOrg</baseXPathExpression>
 47  
  *   <qualifier name="chart">
 48  
  *     <xPathExpression>./chart</xPathExpression>
 49  
  *   </qualifier>
 50  
  *   <qualifier name="org">
 51  
  *     <xPathExpression>./org</xPathExpression>
 52  
  *   </qualifier>
 53  
  * </resolverConfig>
 54  
  * </pre>
 55  
  * 
 56  
  * <p>There are 2 different types of qualifier resolvers, those that resolve compound
 57  
  * attribute sets and those that resolve simple attribute sets.  A simple attribute
 58  
  * set is one which includes only a single "qualifier" specification.  The example above
 59  
  * is compound because it includes both chart and org.
 60  
  * 
 61  
  * <p>When dealing with compound attribute sets, the baseXPathExpression is used to
 62  
  * define grouping for these compound sets.  It is therefore required that inside each
 63  
  * resulting element retrieved from the baseXPathExpression, there is only a single instance
 64  
  * of each qualifier.  If this is not the case, an error will be thrown.  For the example
 65  
  * above, the following XML would be evaluated successfully:
 66  
  * 
 67  
  * <p><pre>
 68  
  * <xmlData>
 69  
  *   <chartOrg>
 70  
  *     <chart>BL</chart>
 71  
  *     <org>BUS</org>
 72  
  *   </chartOrg>
 73  
  *   <chartOrg>
 74  
  *     <chart>IN</chart>
 75  
  *     <org>MED</org>
 76  
  *   </chartOrg>
 77  
  * </xmlData>
 78  
  * </pre>
 79  
  * 
 80  
  * <p>This would return 2 attributes sets, each with a chart and org in it.  The following
 81  
  * XML would cause the XPathQualifierResolver to throw an exception during processing.
 82  
  * 
 83  
  * <p><pre>
 84  
  * <xmlData>
 85  
  *   <chartOrg>
 86  
  *     <chart>BL</chart>
 87  
  *     <org>BUS</org>
 88  
  *     <chart>IN</chart>
 89  
  *     <org>MED</org>
 90  
  *   </chartOrg>
 91  
  * </xmlData>
 92  
  * </pre>
 93  
  * 
 94  
  * <p>In this case the resolver has no knowledge of how to group chart and org together.
 95  
  * What follows is an example of a resolver using a simple attribute set:
 96  
  * 
 97  
  * <p><pre>
 98  
  * <resolverConfig>
 99  
  *   <baseXPathExpression>/xmlData/accountNumbers</baseXPathExpression>
 100  
  *   <qualifier name="accountNumber">
 101  
  *     <xPathExpression>./accountNumber</xPathExpression>
 102  
  *   </qualifier>
 103  
  * </resolverConfig>
 104  
  * </pre>
 105  
  * 
 106  
  * <p>In this example, the following XML would return a List containing an Map<String, String>
 107  
  * for each account number when resolved.
 108  
  * 
 109  
  * <p><pre>
 110  
  * <xmlData>
 111  
  *   <accountNumbers>
 112  
  *     <accountNumber>12345</accountNumber>
 113  
  *     <accountNumber>54321</accountNumber>
 114  
  *     <accountNumber>102030</accountNumber>
 115  
  *     <accountNumber>302010</accountNumber>
 116  
  *   </accountNumbers>
 117  
  * </xmlData>
 118  
  * 
 119  
  * <p>The baseXPathExpression is optional and defaults to the root of the document if not specified.
 120  
  * 
 121  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 122  
  */
 123  0
 public class XPathQualifierResolver implements QualifierResolver {
 124  0
     private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(XPathQualifierResolver.class);
 125  
 
 126  
         private ExtensionDefinition extensionDefinition;
 127  
     String xmlConfigData;
 128  
         
 129  
         public List<Map<String, String>> resolve(RouteContext context) {
 130  0
                         ResolverConfig config = parseResolverConfig();
 131  0
                         Document xmlContent = context.getDocumentContent().getDocument();
 132  0
                         XPath xPath = XPathHelper.newXPath();
 133  0
                         boolean isCompoundMap = config.getExpressionMap().size() > 1;
 134  
                         try {
 135  0
                                 List<Map<String, String>> maps = new ArrayList<Map<String, String>>();
 136  0
                                 NodeList baseElements = (NodeList)xPath.evaluate(config.getBaseXPathExpression(), xmlContent, XPathConstants.NODESET);
 137  0
                                 if (LOG.isDebugEnabled()) {
 138  0
                                         LOG.debug("Found " + baseElements.getLength() + " baseElements to parse for Map<String, String>s using document XML:" + XmlJotter.jotDocument(xmlContent));
 139  
                                 }
 140  0
                                 for (int index = 0; index < baseElements.getLength(); index++) {
 141  0
                                         Node baseNode = baseElements.item(index);
 142  0
                                         if (isCompoundMap) {
 143  0
                                                 handleCompoundMap(baseNode, maps, config, xPath);
 144  
                                         } else {
 145  0
                                                 handleSimpleMap(baseNode, maps, config, xPath);
 146  
                                         }
 147  
                                 }
 148  0
                                 return maps;
 149  0
                         } catch (XPathExpressionException e) {
 150  0
                                 throw new RiceRuntimeException("Encountered an issue executing XPath.", e);
 151  
                         }
 152  
         }
 153  
         
 154  
         protected void handleCompoundMap(Node baseNode, List<Map<String, String>> maps, ResolverConfig config, XPath xPath) throws XPathExpressionException {
 155  0
                 Map<String, String> map = new HashMap<String, String>();
 156  0
                 for (String attributeName : config.getExpressionMap().keySet()) {
 157  0
                         String xPathExpression = config.getExpressionMap().get(attributeName);
 158  0
                         NodeList attributes = (NodeList)xPath.evaluate(xPathExpression, baseNode, XPathConstants.NODESET);
 159  0
                         if (attributes.getLength() > 1) {
 160  0
                                 throw new RiceRuntimeException("Found more than more XPath result for an attribute in a compound attribute set for attribute: " + attributeName + " with expression " + xPathExpression);
 161  0
                         } else if (attributes.getLength() != 0) {
 162  0
                                 String attributeValue = ((Element)attributes.item(0)).getTextContent();
 163  0
                                 if (LOG.isDebugEnabled()) {
 164  0
                                         LOG.debug("Adding values to compound Map<String, String>: " + attributeName + "::" + attributeValue);
 165  
                                 }
 166  0
                                 map.put(attributeName, attributeValue);
 167  
                         }
 168  0
                 }
 169  0
                 maps.add(map);
 170  0
         }
 171  
         
 172  
         protected void handleSimpleMap(Node baseNode, List<Map<String, String>> maps, ResolverConfig config, XPath xPath) throws XPathExpressionException {
 173  0
                 String attributeName = config.getExpressionMap().keySet().iterator().next();
 174  0
                 String xPathExpression = config.getExpressionMap().get(attributeName);
 175  0
                 NodeList attributes = (NodeList)xPath.evaluate(xPathExpression, baseNode, XPathConstants.NODESET);
 176  0
                 for (int index = 0; index < attributes.getLength(); index++) {
 177  0
                         Element attributeElement = (Element)attributes.item(index);
 178  0
                         Map<String, String> map = new HashMap<String, String>();
 179  0
                         String attributeValue = attributeElement.getTextContent();
 180  0
                         if (LOG.isDebugEnabled()) {
 181  0
                                 LOG.debug("Adding values to simple Map<String, String>: " + attributeName + "::" + attributeValue);
 182  
                         }
 183  0
                         map.put(attributeName, attributeValue);
 184  0
                         maps.add(map);
 185  
                 }
 186  0
         }
 187  
 
 188  
         public void setXmlConfigData (String xmlConfigData) {
 189  0
                 this.xmlConfigData = xmlConfigData;
 190  0
         }
 191  
         
 192  
         protected ResolverConfig parseResolverConfig() {
 193  0
                 if (xmlConfigData == null) {
 194  0
                         throw new RiceRuntimeException("Failed to locate a RuleAttribute for the given XPathQualifierResolver");
 195  
                 }
 196  
                 try {
 197  0
                         ResolverConfig resolverConfig = new ResolverConfig();
 198  0
                         String xmlConfig = xmlConfigData;
 199  0
                         XPath xPath = XPathHelper.newXPath();
 200  0
                         String baseExpression = xPath.evaluate("//resolverConfig/baseXPathExpression", new InputSource(new StringReader(xmlConfig)));
 201  0
                         if (!StringUtils.isEmpty(baseExpression)) {
 202  0
                                 resolverConfig.setBaseXPathExpression(baseExpression);
 203  
                         }
 204  0
                         NodeList qualifiers = (NodeList)xPath.evaluate("//resolverConfig/qualifier", new InputSource(new StringReader(xmlConfig)), XPathConstants.NODESET);
 205  0
                         if (qualifiers == null || qualifiers.getLength() == 0) {
 206  0
                                 throw new RiceRuntimeException("Invalid qualifier resolver configuration.  Must contain at least one qualifier!");
 207  
                         }
 208  0
                         for (int index = 0; index < qualifiers.getLength(); index++) {
 209  0
                                 Element qualifierElement = (Element)qualifiers.item(index);
 210  0
                                 String name = qualifierElement.getAttribute("name");
 211  0
                                 NodeList expressions = qualifierElement.getElementsByTagName("xPathExpression");
 212  0
                                 if (expressions.getLength() != 1) {
 213  0
                                         throw new RiceRuntimeException("There should only be a single xPathExpression per qualifier");
 214  
                                 }
 215  0
                                 Element expressionElement = (Element)expressions.item(0);
 216  0
                                 resolverConfig.getExpressionMap().put(name, expressionElement.getTextContent());
 217  
                         }
 218  0
                         if (LOG.isDebugEnabled()) {
 219  0
                                 LOG.debug("Using Resolver Config Settings: " + resolverConfig.toString());
 220  
                         }
 221  0
                         return resolverConfig;
 222  0
                 } catch (XPathExpressionException e) {
 223  0
                         throw new RiceRuntimeException("Encountered an error parsing resolver config.", e);
 224  
                 }
 225  
         }
 226  
         
 227  0
         class ResolverConfig {
 228  0
                 private String baseXPathExpression = "/";
 229  0
                 private Map<String, String> expressionMap = new HashMap<String, String>();
 230  
                 public String getBaseXPathExpression() {
 231  0
                         return this.baseXPathExpression;
 232  
                 }
 233  
                 public void setBaseXPathExpression(String baseXPathExpression) {
 234  0
                         this.baseXPathExpression = baseXPathExpression;
 235  0
                 }
 236  
                 public Map<String, String> getExpressionMap() {
 237  0
                         return this.expressionMap;
 238  
                 }
 239  
                 public void setExpressionMap(Map<String, String> expressionMap) {
 240  0
                         this.expressionMap = expressionMap;
 241  0
                 }
 242  
                 @Override
 243  
                 public String toString() {
 244  0
                         StringBuffer sb = new StringBuffer();
 245  0
                         sb.append(  '\n' );
 246  0
                         sb.append("ResolverConfig Parameters\n");
 247  0
                         sb.append( "      baseXPathExpression: " + baseXPathExpression + "\n" );
 248  0
                         sb.append( "      expressionMap:\n" );
 249  0
                         for (Map.Entry<String, String> entry : expressionMap.entrySet()) {
 250  0
                                 sb.append( "            " + entry.getKey() + ": " + entry.getValue() + "\n" );
 251  
                         }
 252  0
                         return sb.toString();
 253  
                 }
 254  
         }
 255  
 
 256  
 }