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