Coverage Report - org.kuali.rice.kew.rule.xmlrouting.StandardGenericXMLRuleAttribute
 
Classes in this File Line Coverage Branch Coverage Complexity
StandardGenericXMLRuleAttribute
0%
0/277
0%
0/142
4.516
StandardGenericXMLRuleAttribute$1
0%
0/3
N/A
4.516
StandardGenericXMLRuleAttribute$2
0%
0/3
N/A
4.516
StandardGenericXMLRuleAttribute$3
0%
0/6
0%
0/2
4.516
StandardGenericXMLRuleAttribute$ErrorGenerator
N/A
N/A
4.516
 
 1  
 /*
 2  
  * Copyright 2005-2007 The Kuali Foundation
 3  
  *
 4  
  *
 5  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 6  
  * you may not use this file except in compliance with the License.
 7  
  * You may obtain a copy of the License at
 8  
  *
 9  
  * http://www.opensource.org/licenses/ecl2.php
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 package org.kuali.rice.kew.rule.xmlrouting;
 18  
 
 19  
 import org.apache.commons.lang.StringUtils;
 20  
 import org.kuali.rice.core.util.ConcreteKeyValue;
 21  
 import org.kuali.rice.core.util.KeyValue;
 22  
 import org.kuali.rice.core.util.xml.XmlJotter;
 23  
 import org.kuali.rice.kew.api.WorkflowRuntimeException;
 24  
 import org.kuali.rice.kew.attribute.XMLAttributeUtils;
 25  
 import org.kuali.rice.kew.exception.WorkflowServiceErrorImpl;
 26  
 import org.kuali.rice.kew.routeheader.DocumentContent;
 27  
 import org.kuali.rice.kew.rule.RuleExtension;
 28  
 import org.kuali.rice.kew.rule.RuleExtensionValue;
 29  
 import org.kuali.rice.kew.rule.WorkflowAttributeValidationError;
 30  
 import org.kuali.rice.kew.rule.WorkflowAttributeXmlValidator;
 31  
 import org.kuali.rice.kew.rule.bo.RuleAttribute;
 32  
 import org.kuali.rice.kns.web.ui.Field;
 33  
 import org.kuali.rice.kns.web.ui.Row;
 34  
 import org.w3c.dom.Element;
 35  
 import org.w3c.dom.NamedNodeMap;
 36  
 import org.w3c.dom.Node;
 37  
 import org.w3c.dom.NodeList;
 38  
 import org.xml.sax.InputSource;
 39  
 
 40  
 import javax.xml.parsers.DocumentBuilderFactory;
 41  
 import javax.xml.xpath.XPath;
 42  
 import javax.xml.xpath.XPathConstants;
 43  
 import javax.xml.xpath.XPathExpressionException;
 44  
 import java.io.BufferedReader;
 45  
 import java.io.StringReader;
 46  
 import java.util.*;
 47  
 import java.util.regex.Pattern;
 48  
 
 49  
 
 50  
 /**
 51  
  * A generic WorkflowAttribute implementation that can be defined completely by XML.
 52  
  * <ol>
 53  
  *   <li>This attribute implementation takes "properties" defined on the the {@link org.kuali.rice.kew.dto.WorkflowAttributeDefinitionDTO}
 54  
  *       and maps them to the param map of {@link GenericXMLRuleAttribute}, which relate directly to a set of fields defined by the
 55  
  *       XML <code>&lt;routingConfig&gt;</code> configuration.</li>
 56  
  *   <li>Application of the properties defined on the WorkflowAttributeDefinition
 57  
  *       to the actual attribute is performed in  {@link org.kuali.rice.core.framework.resourceloader.ObjectDefinitionResolver#invokeProperties(Object, java.util.Collection)}</li>
 58  
  *   <li>These params are then used to perform one of either EITHER:
 59  
  *     <ul>
 60  
  *       <li>Replace parameters of the syntax <code>%<i>field name</i>%</code> in the doc content if doc content is
 61  
  *           defined in the <code>&lt;xmlDocumentContent&gt;</code></li>
 62  
  *       <li>Generate a generic doc content, containing the parameter key/value pairs, e.g.:
 63  
  *           <blockquote>
 64  
  *           <code><pre>
 65  
  *             &lt;xmlrouting&gt;
 66  
  *               &lt;field name="color"&gt;&lt;value&gt;red&lt;/value&gt;&lt;/field&gt;
 67  
  *               &lt;field name="shape"&gt;&lt;value&gt;circle&lt;/value&gt;&lt;/field&gt;
 68  
  *             &lt;/xmlrouting&gt;
 69  
  *           </pre></code>
 70  
  *           </blockquote>
 71  
  *       </li>
 72  
  *     </ul>
 73  
  *     Currently, only parameters that match fields configured in the routingConfig are honored (the others are ignored)
 74  
  *     (NOTE: to make this even more reusable we might want to consider generating content for all parameters, even those that
 75  
  *      do not have corresponding fields)
 76  
  *   </li>
 77  
  *   <li>The routingConfig element defines a set of <code>fieldDef</code>s, each of which may have an <code>xpathexpression</code> for field evaluation.
 78  
  *       This <code>xpathexpression</code> is used to determine whether the attribute's {@link #isMatch(DocumentContent, List)} will
 79  
  *       succeed.  Each fieldDef may also have a <code>validation</code> element which supplies a regular expression against which
 80  
  *       to validate the field value (given by the param map)</li>
 81  
  * </ol>
 82  
  *
 83  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 84  
  */
 85  0
 public class StandardGenericXMLRuleAttribute implements GenericXMLRuleAttribute, WorkflowAttributeXmlValidator {
 86  0
     private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(StandardGenericXMLRuleAttribute.class);
 87  
 
 88  
     private static final String FIELD_DEF_E = "fieldDef";
 89  
 
 90  0
     private boolean evaluateForMissingExtensions = false;
 91  
 
 92  
     private NodeList getFields(XPath xpath, Element root, String[] types) throws XPathExpressionException {
 93  0
         final String OR = " or ";
 94  0
         StringBuffer findField = new StringBuffer("//routingConfig/" + FIELD_DEF_E);
 95  0
         if (types != null && types.length > 0) {
 96  0
             findField.append("[");
 97  0
             for (String type : types) {
 98  0
                 findField.append("@workflowType='" + type + "'" + OR);
 99  
                 // missing workflowType is equivalent ("defaults") to ALL
 100  0
                 if ("ALL".equals(type)) {
 101  0
                     findField.append("not(@workflowType)" + OR);
 102  
                 }
 103  
             }
 104  0
             if (types.length > 0) {
 105  
                 // remove trailing " or "
 106  0
                 findField.setLength(findField.length() - OR.length());
 107  
             }
 108  0
             findField.append("]");
 109  
         }
 110  
 
 111  
         try {
 112  0
             return (NodeList) xpath.evaluate(findField.toString(), root, XPathConstants.NODESET);
 113  0
         } catch (XPathExpressionException e) {
 114  0
             LOG.error("Error evaluating expression: '" + findField + "'");
 115  0
             throw e;
 116  
         }
 117  
     }
 118  
 
 119  
     private List<Row> getRows(Element root, String[] types) {
 120  0
         List<Row> rows = new ArrayList<Row>();
 121  0
         XPath xpath = XPathHelper.newXPath();
 122  
         NodeList fieldNodeList;
 123  
         try {
 124  0
             fieldNodeList = getFields(xpath, root, types);
 125  0
         } catch (XPathExpressionException e) {
 126  0
             LOG.error("Error evaluating fields expression");
 127  0
             return rows;
 128  0
         }
 129  0
         if (fieldNodeList != null) {
 130  0
             for (int i = 0; i < fieldNodeList.getLength(); i++) {
 131  0
                 Node field = fieldNodeList.item(i);
 132  0
                 NamedNodeMap fieldAttributes = field.getAttributes();
 133  
 
 134  0
                 List<Field> fields = new ArrayList<Field>();
 135  0
                 Field myField = new Field(fieldAttributes.getNamedItem("title").getNodeValue(), "", "", false, fieldAttributes.getNamedItem("name").getNodeValue(), "", false, false, null, "");
 136  0
                 String quickfinderService = null;
 137  0
                 for (int j = 0; j < field.getChildNodes().getLength(); j++) {
 138  0
                     Node childNode = field.getChildNodes().item(j);
 139  0
                     if ("value".equals(childNode.getNodeName())) {
 140  0
                         myField.setPropertyValue(childNode.getFirstChild().getNodeValue());
 141  0
                     } else if ("display".equals(childNode.getNodeName())) {
 142  0
                         List<KeyValue> options = new ArrayList<KeyValue>();
 143  0
                         List<String> selectedOptions = new ArrayList<String>();
 144  0
                         for (int k = 0; k < childNode.getChildNodes().getLength(); k++) {
 145  0
                             Node displayChildNode = childNode.getChildNodes().item(k);
 146  0
                             if ("type".equals(displayChildNode.getNodeName())) {
 147  0
                                 myField.setFieldType(convertTypeToFieldType(displayChildNode.getFirstChild().getNodeValue()));
 148  0
                             } else if ("meta".equals(displayChildNode.getNodeName())) {
 149  
                                 // i don't think the rule creation support things in this node.
 150  
                                 // i don't think the flex Routing report supports things in this node.
 151  0
                             } else if ("values".equals(displayChildNode.getNodeName())) {
 152  0
                                 NamedNodeMap valuesAttributes = displayChildNode.getAttributes();
 153  0
                                 String optionValue = "";
 154  
                                 // if element is empty then child will be null
 155  0
                                 Node firstChild = displayChildNode.getFirstChild();
 156  0
                                 if (firstChild != null) {
 157  0
                                         optionValue = firstChild.getNodeValue();
 158  
                                 }
 159  0
                                 if (valuesAttributes.getNamedItem("selected") != null) {
 160  0
                                     selectedOptions.add(optionValue);
 161  
                                 }
 162  0
                                 String title = "";
 163  0
                                 Node titleAttribute = valuesAttributes.getNamedItem("title");
 164  0
                                 if (titleAttribute != null) {
 165  0
                                         title = titleAttribute.getNodeValue();
 166  
                                     }
 167  0
                                     options.add(new ConcreteKeyValue(optionValue, title));
 168  
                             }
 169  
                         }
 170  0
                         if (!options.isEmpty()) {
 171  0
                             myField.setFieldValidValues(options);
 172  0
                             if (!selectedOptions.isEmpty()) {
 173  
                                 //if (Field.MULTI_VALUE_FIELD_TYPES.contains(myField.getFieldType())) {
 174  
                                 //    String[] newSelectedOptions = new String[selectedOptions.size()];
 175  
                                 //    int k = 0;
 176  
                                 //    for (Iterator iter = selectedOptions.iterator(); iter.hasNext();) {
 177  
                                 //        String option = (String) iter.next();
 178  
                                 //        newSelectedOptions[k] = option;
 179  
                                 //        k++;
 180  
                                 //    }
 181  
                                 //    myField.setPropertyValues(newSelectedOptions);
 182  
                                 //} else {
 183  
                                 //
 184  0
                                     myField.setPropertyValue((String)selectedOptions.get(0));
 185  
                                 //}
 186  
                             }
 187  
                         }
 188  0
                     } else if ("lookup".equals(childNode.getNodeName())) {
 189  0
                                                 XMLAttributeUtils.establishFieldLookup(myField, childNode);
 190  
                                         } 
 191  
                 }
 192  0
                 fields.add(myField);
 193  0
                 rows.add(new Row(fields));
 194  
             }
 195  
         }
 196  0
         return rows;
 197  
     }
 198  
 
 199  
     private static String convertTypeToFieldType(String type) {
 200  0
         if ("text".equals(type)) {
 201  0
             return Field.TEXT;
 202  0
         } else if ("select".equals(type)) {
 203  0
             return Field.DROPDOWN;
 204  0
         } else if ("radio".equals(type)) {
 205  0
             return Field.RADIO;
 206  0
         } else if ("quickfinder".equals(type)) {
 207  0
             return Field.QUICKFINDER;
 208  
         }
 209  0
         return null;
 210  
     }
 211  
 
 212  
     private static interface ErrorGenerator {
 213  
         Object generateInvalidFieldError(Node field, String fieldName, String message);
 214  
         Object generateMissingFieldError(Node field, String fieldName, String message);
 215  
     }
 216  
 
 217  
     private RuleAttribute ruleAttribute;
 218  0
     private Map paramMap = new HashMap();
 219  0
     private List ruleRows = new ArrayList();
 220  0
     private List routingDataRows = new ArrayList();
 221  
     private boolean required;
 222  
 
 223  0
     public StandardGenericXMLRuleAttribute() {
 224  0
     }
 225  
 
 226  
     public void setRuleAttribute(RuleAttribute ruleAttribute) {
 227  0
         this.ruleAttribute = ruleAttribute;
 228  0
     }
 229  
 
 230  
 //    public boolean isMatch(DocumentContent docContent, List ruleExtensions) {
 231  
 //        XPath xpath = XPathHelper.newXPath(docContent.getDocument());
 232  
 //        WorkflowFunctionResolver resolver = XPathHelper.extractFunctionResolver(xpath);
 233  
 //        for (Iterator iter = ruleExtensions.iterator(); iter.hasNext();) {
 234  
 //            RuleExtension extension = (RuleExtension) iter.next();
 235  
 //            if (extension.getRuleTemplateAttribute().getRuleAttribute().getName().equals(ruleAttribute.getName())) {
 236  
 //                List extensions = new ArrayList();
 237  
 //                extensions.add(extension);
 238  
 //                resolver.setRuleExtensions(extensions);
 239  
 //                //xpath.setXPathFunctionResolver(resolver);
 240  
 //                for (Iterator iterator = extension.getExtensionValues().iterator(); iterator.hasNext();) {
 241  
 //                    RuleExtensionValue value = (RuleExtensionValue) iterator.next();
 242  
 //                    String findXpathExpression = "//routingConfig/" + FIELD_DEF_E + "[@name='" + value.getKey() + "']/fieldEvaluation/xpathexpression";
 243  
 //                    String xpathExpression = null;
 244  
 //                    try {
 245  
 //                        xpathExpression = (String) xpath.evaluate(findXpathExpression, getConfigXML(), XPathConstants.STRING);
 246  
 //                        LOG.debug("routingConfig XPath expression: " + xpathExpression);
 247  
 //                        if (!org.apache.commons.lang.StringUtils.isEmpty(xpathExpression)) {
 248  
 //                            LOG.debug("DocContent: " + docContent.getDocContent());
 249  
 //                            Boolean match = (Boolean) xpath.evaluate(xpathExpression, docContent.getDocument(), XPathConstants.BOOLEAN);
 250  
 //                            LOG.debug("routingConfig match? " + match);
 251  
 //                            if (match != null && !match.booleanValue()) {
 252  
 //                                return false;
 253  
 //                            }
 254  
 //                        }
 255  
 //                    } catch (XPathExpressionException e) {
 256  
 //                        LOG.error("error in isMatch ", e);
 257  
 //                        throw new RuntimeException("Error trying to find xml content with xpath expressions: " + findXpathExpression + " or " + xpathExpression, e);
 258  
 //                    }
 259  
 //                }
 260  
 //                resolver.setRuleExtensions(null);
 261  
 //            }
 262  
 //        }
 263  
 //        String findXpathExpression = "//routingConfig/globalEvaluations/xpathexpression";
 264  
 //        String xpathExpression = "";
 265  
 //        try {
 266  
 //            NodeList xpathExpressions = (NodeList) xpath.evaluate(findXpathExpression, getConfigXML(), XPathConstants.NODESET);
 267  
 //            for (int i = 0; i < xpathExpressions.getLength(); i++) {
 268  
 //                Node xpathNode = xpathExpressions.item(i);
 269  
 //                xpathExpression = xpathNode.getFirstChild().getNodeValue();
 270  
 //                LOG.debug("global XPath expression: " + xpathExpression);
 271  
 //                if (!org.apache.commons.lang.StringUtils.isEmpty(xpathExpression)) {
 272  
 //                    LOG.debug("DocContent: " + docContent.getDocContent());
 273  
 //                    Boolean match = (Boolean) xpath.evaluate(xpathExpression, docContent.getDocument(), XPathConstants.BOOLEAN);
 274  
 //                    LOG.debug("Global match? " + match);
 275  
 //                    if (match != null && !match.booleanValue()) {
 276  
 //                        return false;
 277  
 //                    }
 278  
 //                }
 279  
 //            }
 280  
 //        } catch (XPathExpressionException e) {
 281  
 //            LOG.error("error in isMatch ", e);
 282  
 //            throw new RuntimeException("Error trying to find xml content with xpath expressions: " + findXpathExpression, e);
 283  
 //        }
 284  
 //        return true;
 285  
 //    }
 286  
 
 287  
     public boolean isMatch(DocumentContent docContent, List ruleExtensions) {
 288  0
         XPath xpath = XPathHelper.newXPath(docContent.getDocument());
 289  0
         WorkflowFunctionResolver resolver = XPathHelper.extractFunctionResolver(xpath);
 290  0
         resolver.setRuleExtensions(ruleExtensions);
 291  0
         List<String> xPathExpressionsToEvaluate = extractExpressionsToEvaluate(xpath, docContent, ruleExtensions);
 292  0
         for (String xPathExpressionToEvaluate : xPathExpressionsToEvaluate) {
 293  0
             if (LOG.isDebugEnabled()) {
 294  0
                 LOG.debug("Evaluating xPath expression: " + xPathExpressionToEvaluate);
 295  
             }
 296  
             try {
 297  0
                 Boolean match = (Boolean) xpath.evaluate(xPathExpressionToEvaluate, docContent.getDocument(), XPathConstants.BOOLEAN);
 298  0
                 if (LOG.isDebugEnabled()) {
 299  0
                     LOG.debug("Expression match result: " + match);
 300  
                 }
 301  0
                 if (match != null && !match.booleanValue()) {
 302  0
                     return false;
 303  
                 }
 304  0
             } catch (XPathExpressionException e) {
 305  0
                 LOG.error("Error in isMatch ", e);
 306  0
                 throw new RuntimeException("Error trying to evalute xml content with xpath expression: " + xPathExpressionToEvaluate, e);
 307  0
             }
 308  
         }
 309  0
         return true;
 310  
     }
 311  
 
 312  
     /**
 313  
      * Extracts the xPath expressions that should be evaluated in order to determine whether or not the rule matches.  THis should take
 314  
      * into account the value of evaluateForMissingExtensions.
 315  
      */
 316  
     protected List<String> extractExpressionsToEvaluate(XPath xpath, DocumentContent docContent, List ruleExtensions) {
 317  0
         List<String> expressionsToEvaluate = new ArrayList<String>(ruleExtensions.size() + 1);
 318  0
         Element configXml = getConfigXML();
 319  0
         String findFieldExpressions = "//routingConfig/" + FIELD_DEF_E + "/fieldEvaluation/xpathexpression";
 320  
         try {
 321  0
             NodeList xPathExpressions = (NodeList) xpath.evaluate(findFieldExpressions, configXml, XPathConstants.NODESET);
 322  0
             for (int index = 0; index < xPathExpressions.getLength(); index++) {
 323  0
                 Element expressionElement = (Element) xPathExpressions.item(index);
 324  0
                 String expression = expressionElement.getTextContent();
 325  0
                 if (!isEvaluateForMissingExtensions()) {
 326  0
                     Node parentNode = expressionElement.getParentNode().getParentNode();
 327  0
                     Node fieldAttribute = parentNode.getAttributes().getNamedItem("name");
 328  0
                     if (fieldAttribute == null || StringUtils.isEmpty(fieldAttribute.getNodeValue())) {
 329  0
                         throw new WorkflowRuntimeException("Could not determine field name defined on fieldDef for xpath expression: " + expression);
 330  
                     }
 331  0
                     String fieldName = fieldAttribute.getNodeValue();
 332  0
                     boolean foundExtension = false;
 333  0
                     outer:for (Iterator iterator = ruleExtensions.iterator(); iterator.hasNext();) {
 334  0
                         RuleExtension ruleExtension = (RuleExtension) iterator.next();
 335  0
                         if (ruleExtension.getRuleTemplateAttribute().getRuleAttribute().getName().equals(ruleAttribute.getName())) {
 336  0
                             for (RuleExtensionValue ruleExtensionValue : ruleExtension.getExtensionValues()) {
 337  0
                                 if (fieldName.equals(ruleExtensionValue.getKey())) {
 338  0
                                     foundExtension = true;
 339  0
                                     break outer;
 340  
                                 }
 341  
                             }
 342  
                         }
 343  0
                     }
 344  0
                     if (!foundExtension) {
 345  
                         // if the rule does not have an extension value for the xpath expression on the corresponding field def, let's skip it
 346  0
                         continue;
 347  
                     }
 348  
                 }
 349  
 
 350  0
                 if (!StringUtils.isEmpty(expression)) {
 351  0
                     if (LOG.isDebugEnabled()) {
 352  0
                         LOG.debug("Adding routingConfig XPath expression: " + expression);
 353  
                     }
 354  0
                     expressionsToEvaluate.add(expression);
 355  
                 }
 356  
             }
 357  0
         } catch (XPathExpressionException e) {
 358  0
             throw new WorkflowRuntimeException("Failed to evalute XPath expression for fieldDefs: " + findFieldExpressions);
 359  0
         }
 360  0
         String findGlobalExpressions = "//routingConfig/globalEvaluations/xpathexpression";
 361  
         try {
 362  0
             NodeList xPathExpressions = (NodeList) xpath.evaluate(findGlobalExpressions, configXml, XPathConstants.NODESET);
 363  0
             for (int index = 0; index < xPathExpressions.getLength(); index++) {
 364  0
                 Element expressionElement = (Element) xPathExpressions.item(index);
 365  0
                 String expression = XmlJotter.jotNode(expressionElement);
 366  0
                 if (!StringUtils.isEmpty(expression)) {
 367  0
                     if (LOG.isDebugEnabled()) {
 368  0
                         LOG.debug("Adding global XPath expression: " + expression);
 369  
                     }
 370  0
                     expressionsToEvaluate.add(expression);
 371  
                 }
 372  
             }
 373  0
         } catch (XPathExpressionException e) {
 374  0
             throw new WorkflowRuntimeException("Failed to evalute global XPath expression: " + findGlobalExpressions);
 375  0
         }
 376  0
         return expressionsToEvaluate;
 377  
     }
 378  
 
 379  
     public List getRuleRows() {
 380  0
         if (ruleRows.isEmpty()) {
 381  0
             ruleRows = getRows(getConfigXML(), new String[] { "ALL", "RULE" });
 382  
         }
 383  0
         return ruleRows;
 384  
     }
 385  
 
 386  
     private String getValidationErrorMessage(XPath xpath, Element root, String fieldName) throws XPathExpressionException {
 387  0
         String findErrorMessage = "//routingConfig/" + FIELD_DEF_E + "[@name='" + fieldName + "']/validation/message";
 388  0
         return (String) xpath.evaluate(findErrorMessage, root, XPathConstants.STRING);
 389  
     }
 390  
 
 391  
     private List validate(Element root, String[] types, Map map, ErrorGenerator errorGenerator) throws XPathExpressionException {
 392  0
         List errors = new ArrayList();
 393  0
         XPath xpath = XPathHelper.newXPath();
 394  
 
 395  0
         NodeList nodes = getFields(xpath, root, types);
 396  0
         for (int i = 0; i < nodes.getLength(); i++) {
 397  0
             Node field = nodes.item(i);
 398  0
             NamedNodeMap fieldAttributes = field.getAttributes();
 399  0
             String fieldName = fieldAttributes.getNamedItem("name").getNodeValue();
 400  
 
 401  0
             LOG.debug("evaluating field: " + fieldName);
 402  0
             String findValidation = "//routingConfig/" + FIELD_DEF_E + "[@name='" + fieldName + "']/validation";
 403  
 
 404  0
             Node validationNode = (Node) xpath.evaluate(findValidation, root, XPathConstants.NODE);
 405  0
             boolean fieldIsRequired = false;
 406  0
             if (validationNode != null) {
 407  0
                 NamedNodeMap validationAttributes = validationNode.getAttributes();
 408  0
                 Node reqAttribNode = validationAttributes.getNamedItem("required");
 409  0
                 fieldIsRequired = reqAttribNode != null && "true".equalsIgnoreCase(reqAttribNode.getNodeValue());
 410  
             }
 411  
 
 412  0
             String findRegex = "//routingConfig/" + FIELD_DEF_E + "[@name='" + fieldName + "']/validation/regex";
 413  
 
 414  0
             String regex = null;
 415  0
             Node regexNode = (Node) xpath.evaluate(findRegex, root, XPathConstants.NODE);
 416  
 
 417  0
             if (regexNode != null && regexNode.getFirstChild() != null) {
 418  0
                 regex = regexNode.getFirstChild().getNodeValue();
 419  0
                 if (regex == null) {
 420  0
                     throw new RuntimeException("Null regex text node");
 421  
                 }
 422  
             }/* else {
 423  
                 if (fieldIsRequired) {
 424  
                     fieldIsOnlyRequired = true;
 425  
                     LOG.debug("Setting empty regex to .+ as field is required");
 426  
                     // NOTE: ok, so technically .+ is not the same as checking merely
 427  
                     // for existence, because a field can be extant but "empty"
 428  
                     // however this has no relevance to the user as an empty field
 429  
                     // is for all intents and purposes non-existent (not-filled-in)
 430  
                     // so let's just use this regex to simplify the logic and
 431  
                     // pass everything through a regex check
 432  
                     regex = ".+";
 433  
                 } else {
 434  
                     LOG.debug("Setting empty regex to .* as field is NOT required");
 435  
                     regex = ".*";
 436  
                 }
 437  
             }*/
 438  
 
 439  0
             LOG.debug("regex for field '" + fieldName + "': '" + regex + "'");
 440  
 
 441  0
             String fieldValue = null;
 442  0
             if (map != null) {
 443  0
                 fieldValue = (String) map.get(fieldName);
 444  
             }
 445  
 
 446  0
             LOG.debug("field value: " + fieldValue);
 447  
 
 448  
             // fix up non-existent value for regex purposes only
 449  0
             if (fieldValue == null) {
 450  0
                 fieldValue = "";
 451  
             }
 452  
 
 453  0
             if (regex == null){
 454  0
                 if (fieldIsRequired) {
 455  0
                     if (fieldValue.length() == 0) {
 456  0
                         errors.add(errorGenerator.generateMissingFieldError(field, fieldName, getValidationErrorMessage(xpath, root, fieldName)));
 457  
                     }
 458  
                 }
 459  
             } else {
 460  0
                 if (!Pattern.compile(regex).matcher(fieldValue).matches()) {
 461  0
                     LOG.debug("field value does not match validation regex");
 462  0
                     errors.add(errorGenerator.generateInvalidFieldError(field, fieldName, getValidationErrorMessage(xpath, root, fieldName)));
 463  
                 }
 464  
             }
 465  
         }
 466  0
         return errors;
 467  
     }
 468  
 
 469  
     public List getRoutingDataRows() {
 470  0
         if (routingDataRows.isEmpty()) {
 471  0
             routingDataRows = getRows(getConfigXML(), new String[] { "ALL", "REPORT" });
 472  
         }
 473  0
         return routingDataRows;
 474  
     }
 475  
 
 476  
     public String getDocContent() {
 477  0
         XPath xpath = XPathHelper.newXPath();
 478  0
         final String findDocContent = "//routingConfig/xmlDocumentContent";
 479  
         try {
 480  0
             Node xmlDocumentContent = (Node) xpath.evaluate(findDocContent, getConfigXML(), XPathConstants.NODE);
 481  
 
 482  0
             NodeList nodes = getFields(xpath, getConfigXML(), new String[] { "ALL", "REPORT", "RULE" });
 483  
 //            if (nodes == null || nodes.getLength() == 0) {
 484  
 //                return "";
 485  
 //            }
 486  
 
 487  0
             if (xmlDocumentContent != null && xmlDocumentContent.hasChildNodes()) {
 488  
                 // Custom doc content in the routingConfig xml.
 489  0
                 String documentContent = "";
 490  0
                 NodeList customNodes = xmlDocumentContent.getChildNodes();
 491  0
                 for (int i = 0; i < customNodes.getLength(); i++) {
 492  0
                     Node childNode = customNodes.item(i);
 493  0
                     documentContent += XmlJotter.jotNode(childNode);
 494  
                 }
 495  
 
 496  0
                 for (int i = 0; i < nodes.getLength(); i++) {
 497  0
                     Node field = nodes.item(i);
 498  0
                     NamedNodeMap fieldAttributes = field.getAttributes();
 499  0
                     String fieldName = fieldAttributes.getNamedItem("name").getNodeValue();
 500  0
                     LOG.debug("Replacing field '" + fieldName + "'");
 501  0
                     Map map = getParamMap();
 502  0
                     String fieldValue = (String) map.get(fieldName);
 503  0
                     if (map != null && !org.apache.commons.lang.StringUtils.isEmpty(fieldValue)) {
 504  0
                         LOG.debug("Replacing %" + fieldName + "% with field value: '" + fieldValue + "'");
 505  0
                         documentContent = documentContent.replaceAll("%" + fieldName + "%", fieldValue);
 506  
                     } else {
 507  0
                         LOG.debug("Field map is null or fieldValue is empty");
 508  
                     }
 509  
                 }
 510  0
                 return documentContent;
 511  
             } else {
 512  
                 // Standard doc content if no doc content is found in the routingConfig xml.
 513  0
                 StringBuffer documentContent = new StringBuffer("<xmlRouting>");
 514  0
                 for (int i = 0; i < nodes.getLength(); i++) {
 515  0
                     Node field = nodes.item(i);
 516  0
                     NamedNodeMap fieldAttributes = field.getAttributes();
 517  0
                     String fieldName = fieldAttributes.getNamedItem("name").getNodeValue();
 518  0
                     Map map = getParamMap();
 519  0
                     if (map != null && !org.apache.commons.lang.StringUtils.isEmpty((String) map.get(fieldName))) {
 520  0
                         documentContent.append("<field name=\"");
 521  0
                         documentContent.append(fieldName);
 522  0
                         documentContent.append("\"><value>");
 523  0
                         documentContent.append((String) map.get(fieldName));
 524  0
                         documentContent.append("</value></field>");
 525  
                     }
 526  
                 }
 527  0
                 documentContent.append("</xmlRouting>");
 528  0
                 return documentContent.toString();
 529  
             }
 530  0
         } catch (XPathExpressionException e) {
 531  0
             LOG.error("error in getDocContent ", e);
 532  0
             throw new RuntimeException("Error trying to find xml content with xpath expression", e);
 533  0
         } catch (Exception e) {
 534  0
             LOG.error("error in getDocContent attempting to find xml doc content", e);
 535  0
             throw new RuntimeException("Error trying to get xml doc content.", e);
 536  
         }
 537  
     }
 538  
 
 539  
     public List getRuleExtensionValues() {
 540  0
         List extensionValues = new ArrayList();
 541  
 
 542  0
         XPath xpath = XPathHelper.newXPath();
 543  
         try {
 544  0
             NodeList nodes = getFields(xpath, getConfigXML(), new String[] { "ALL", "RULE" });
 545  0
             for (int i = 0; i < nodes.getLength(); i++) {
 546  0
                 Node field = nodes.item(i);
 547  0
                 NamedNodeMap fieldAttributes = field.getAttributes();
 548  0
                 String fieldName = fieldAttributes.getNamedItem("name").getNodeValue();
 549  0
                 Map map = getParamMap();
 550  0
                 if (map != null && !org.apache.commons.lang.StringUtils.isEmpty((String) map.get(fieldName))) {
 551  0
                     RuleExtensionValue value = new RuleExtensionValue();
 552  0
                     value.setKey(fieldName);
 553  0
                     value.setValue((String) map.get(fieldName));
 554  0
                     extensionValues.add(value);
 555  
                 }
 556  
             }
 557  0
         } catch (XPathExpressionException e) {
 558  0
             LOG.error("error in getRuleExtensionValues ", e);
 559  0
             throw new RuntimeException("Error trying to find xml content with xpath expression", e);
 560  0
         }
 561  0
         return extensionValues;
 562  
     }
 563  
 
 564  
     public List validateRoutingData(Map paramMap) {
 565  0
         this.paramMap = paramMap;
 566  
         try {
 567  0
             return validate(getConfigXML(), new String[] { "ALL", "REPORT" }, paramMap, new ErrorGenerator() {
 568  
                 public Object generateInvalidFieldError(Node field, String fieldName, String message) {
 569  0
                     return new WorkflowAttributeValidationError("routetemplate.xmlattribute.error", message);
 570  
                 }
 571  
                 public Object generateMissingFieldError(Node field, String fieldName, String message) {
 572  0
                     return new WorkflowAttributeValidationError("routetemplate.xmlattribute.required.error", field.getAttributes().getNamedItem("title").getNodeValue());
 573  
                 }
 574  
             });
 575  0
         } catch (XPathExpressionException e) {
 576  0
             LOG.error("error in validateRoutingData ", e);
 577  0
             throw new RuntimeException("Error trying to find xml content with xpath expression", e);
 578  
         }
 579  
     }
 580  
 
 581  
     public List validateRuleData(Map paramMap) {
 582  0
         this.paramMap = paramMap;
 583  
         try {
 584  0
             return validate(getConfigXML(), new String[] { "ALL", "RULE" }, paramMap, new ErrorGenerator() {
 585  
                 public Object generateInvalidFieldError(Node field, String fieldName, String message) {
 586  0
                     return new WorkflowServiceErrorImpl("Xml attribute error.", "routetemplate.xmlattribute.error", message);
 587  
                 }
 588  
                 public Object generateMissingFieldError(Node field, String fieldName, String message) {
 589  0
                     return new WorkflowServiceErrorImpl("Xml attribute error.", "routetemplate.xmlattribute.required.error", field.getAttributes().getNamedItem("title").getNodeValue());
 590  
                 }
 591  
             });
 592  0
         } catch (XPathExpressionException e) {
 593  0
             LOG.error("error in validateRoutingData ", e);
 594  0
             throw new RuntimeException("Error trying to find xml content with xpath expression", e);
 595  
         }
 596  
     }
 597  
 
 598  
     public void setRequired(boolean required) {
 599  0
         this.required = required;
 600  0
     }
 601  
 
 602  
     public boolean isRequired() {
 603  0
         return required;
 604  
     }
 605  
 
 606  
     public Element getConfigXML() {
 607  
         try {
 608  0
             return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new BufferedReader(new StringReader(ruleAttribute.getXmlConfigData())))).getDocumentElement();
 609  0
         } catch (Exception e) {
 610  0
             String str = ruleAttribute == null ? "null" : ruleAttribute.getName();
 611  0
             LOG.error("error parsing xml data from rule attribute: " + str, e);
 612  0
             throw new RuntimeException("error parsing xml data from rule attribute: " + str, e);
 613  
         }
 614  
     }
 615  
 
 616  
     // TODO: possibly simplify this even further by making WorkflowAttributeValidationError a WorkflowServiceError, and
 617  
     // dispense with custom error generators...
 618  
     public List validateClientRoutingData() {
 619  0
         LOG.debug("validating client routing data");
 620  
         try {
 621  0
             return validate(getConfigXML(), new String[] { "ALL", "RULE" }, getParamMap(), new ErrorGenerator() {
 622  
                 public Object generateInvalidFieldError(Node field, String fieldName, String message) {
 623  0
                     if (org.apache.commons.lang.StringUtils.isEmpty(message)) {
 624  0
                         message = "invalid field value";
 625  
                     } else {
 626  0
                         LOG.info("Message: '" + message + "'");
 627  
                     }
 628  0
                     return new WorkflowAttributeValidationError(fieldName, message);
 629  
                 }
 630  
                 public Object generateMissingFieldError(Node field, String fieldName, String message) {
 631  0
                     return new WorkflowAttributeValidationError(fieldName, "Attribute is required; " + message);
 632  
                 }
 633  
             });
 634  0
         } catch (XPathExpressionException e) {
 635  0
             LOG.error("error in validateClientRoutingData ", e);
 636  0
             throw new RuntimeException("Error trying to find xml content with xpath expression", e);
 637  
         }
 638  
     }
 639  
 
 640  
     public Map getParamMap() {
 641  0
         return paramMap;
 642  
     }
 643  
 
 644  
     public void setParamMap(Map paramMap) {
 645  0
         this.paramMap = paramMap;
 646  0
     }
 647  
 
 648  
     /**
 649  
      * @return the evaluateForMissingExtensions
 650  
      */
 651  
     public boolean isEvaluateForMissingExtensions() {
 652  0
         return this.evaluateForMissingExtensions;
 653  
     }
 654  
 
 655  
     /**
 656  
      * Sets whether or not to evaluate expressions if the extension corresponding to that expressions is not present on the rule.
 657  
      * The correspondence is made by comparing the name of the field declared on the fieldDef element and the name of the
 658  
      * rule extension key.  If this value is set to true then all xpath expressions defined on all fieldDefs will be evaluated
 659  
      * regardless of whether or not the rule has a corresponding extension value.
 660  
      *
 661  
      * <p>By default this is false to preserve backward compatible behavior.
 662  
      */
 663  
     public void setEvaluateForMissingExtensions(boolean evaluateForMissingExtensions) {
 664  0
         this.evaluateForMissingExtensions = evaluateForMissingExtensions;
 665  0
     }
 666  
 
 667  
 
 668  
 }