Coverage Report - org.kuali.rice.kew.docsearch.xml.StandardGenericXMLSearchableAttribute
 
Classes in this File Line Coverage Branch Coverage Complexity
StandardGenericXMLSearchableAttribute
0%
0/462
0%
0/312
10.429
StandardGenericXMLSearchableAttribute$1
N/A
N/A
10.429
StandardGenericXMLSearchableAttribute$RangeBound
0%
0/1
N/A
10.429
 
 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.docsearch.xml;
 17  
 
 18  
 import org.apache.commons.collections.CollectionUtils;
 19  
 import org.apache.commons.lang.StringUtils;
 20  
 import org.kuali.rice.core.api.impex.xml.XmlConstants;
 21  
 import org.kuali.rice.core.api.uif.DataType;
 22  
 import org.kuali.rice.core.api.uif.RemotableAbstractControl;
 23  
 import org.kuali.rice.core.api.uif.RemotableAttributeError;
 24  
 import org.kuali.rice.core.api.uif.RemotableAttributeField;
 25  
 import org.kuali.rice.core.api.uif.RemotableAttributeLookupSettings;
 26  
 import org.kuali.rice.core.api.uif.RemotableDatepicker;
 27  
 import org.kuali.rice.core.api.uif.RemotableHiddenInput;
 28  
 import org.kuali.rice.core.api.uif.RemotableRadioButtonGroup;
 29  
 import org.kuali.rice.core.api.uif.RemotableSelect;
 30  
 import org.kuali.rice.core.api.uif.RemotableTextInput;
 31  
 import org.kuali.rice.core.api.util.ConcreteKeyValue;
 32  
 import org.kuali.rice.core.api.util.KeyValue;
 33  
 import org.kuali.rice.core.api.util.xml.XmlJotter;
 34  
 import org.kuali.rice.kew.api.KewApiConstants;
 35  
 import org.kuali.rice.kew.api.WorkflowRuntimeException;
 36  
 import org.kuali.rice.kew.api.document.DocumentWithContent;
 37  
 import org.kuali.rice.kew.api.document.attribute.DocumentAttribute;
 38  
 import org.kuali.rice.kew.api.document.attribute.WorkflowAttributeDefinition;
 39  
 import org.kuali.rice.kew.api.document.search.DocumentSearchCriteria;
 40  
 import org.kuali.rice.kew.api.extension.ExtensionDefinition;
 41  
 import org.kuali.rice.kew.attribute.XMLAttributeUtils;
 42  
 import org.kuali.rice.kew.docsearch.DocumentSearchInternalUtils;
 43  
 import org.kuali.rice.kew.docsearch.SearchableAttributeValue;
 44  
 import org.kuali.rice.kew.framework.document.attribute.SearchableAttribute;
 45  
 import org.kuali.rice.kew.rule.xmlrouting.XPathHelper;
 46  
 import org.kuali.rice.kew.api.KewApiConstants;
 47  
 import org.kuali.rice.kew.util.Utilities;
 48  
 import org.kuali.rice.kim.api.group.Group;
 49  
 import org.kuali.rice.kim.api.group.GroupService;
 50  
 import org.kuali.rice.kim.api.services.KimApiServiceLocator;
 51  
 import org.kuali.rice.krad.UserSession;
 52  
 import org.kuali.rice.krad.util.GlobalVariables;
 53  
 import org.w3c.dom.Document;
 54  
 import org.w3c.dom.Element;
 55  
 import org.w3c.dom.NamedNodeMap;
 56  
 import org.w3c.dom.Node;
 57  
 import org.w3c.dom.NodeList;
 58  
 import org.xml.sax.InputSource;
 59  
 
 60  
 import javax.xml.parsers.DocumentBuilderFactory;
 61  
 import javax.xml.xpath.XPath;
 62  
 import javax.xml.xpath.XPathConstants;
 63  
 import javax.xml.xpath.XPathExpressionException;
 64  
 import java.io.BufferedReader;
 65  
 import java.io.StringReader;
 66  
 import java.util.ArrayList;
 67  
 import java.util.Collections;
 68  
 import java.util.HashMap;
 69  
 import java.util.List;
 70  
 import java.util.Map;
 71  
 import java.util.regex.Matcher;
 72  
 import java.util.regex.Pattern;
 73  
 
 74  
 
 75  
 /**
 76  
  * Implementation of a {@code SearchableAttribute} whose configuration is driven from XML.
 77  
  *
 78  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 79  
  */
 80  0
 public class StandardGenericXMLSearchableAttribute implements SearchableAttribute {
 81  
 
 82  0
         private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(StandardGenericXMLSearchableAttribute.class);
 83  
     private static final String FIELD_DEF_E = "fieldDef";
 84  
 
 85  
     @Override
 86  
     public String generateSearchContent(ExtensionDefinition extensionDefinition, String documentTypeName, WorkflowAttributeDefinition attributeDefinition) {
 87  0
                 XPath xpath = XPathHelper.newXPath();
 88  0
                 String findDocContent = "//searchingConfig/xmlSearchContent";
 89  0
         Map<String, String> propertyDefinitionMap = attributeDefinition.getPropertyDefinitionsAsMap();
 90  
                 try {
 91  0
                         Node xmlDocumentContent = (Node) xpath.evaluate(findDocContent, getConfigXML(extensionDefinition), XPathConstants.NODE);
 92  0
                         if (xmlDocumentContent != null && xmlDocumentContent.hasChildNodes()) {
 93  
                                 // Custom doc content in the searchingConfig xml.
 94  0
                                 String docContent = "";
 95  0
                                 NodeList customNodes = xmlDocumentContent.getChildNodes();
 96  0
                                 for (int i = 0; i < customNodes.getLength(); i++) {
 97  0
                                         Node childNode = customNodes.item(i);
 98  0
                                         docContent += XmlJotter.jotNode(childNode);
 99  
                                 }
 100  0
                                 String findField = "//searchingConfig/" + FIELD_DEF_E;
 101  0
                                 NodeList nodes = (NodeList) xpath.evaluate(findField, getConfigXML(extensionDefinition), XPathConstants.NODESET);
 102  0
                                 if (nodes == null || nodes.getLength() == 0) {
 103  0
                                         return "";
 104  
                                 }
 105  0
                                 for (int i = 0; i < nodes.getLength(); i++) {
 106  0
                                         Node field = nodes.item(i);
 107  0
                                         NamedNodeMap fieldAttributes = field.getAttributes();
 108  0
                                         if (propertyDefinitionMap != null && !StringUtils.isBlank(propertyDefinitionMap.get(fieldAttributes.getNamedItem("name").getNodeValue()))) {
 109  0
                                                 docContent = docContent.replaceAll("%" + fieldAttributes.getNamedItem("name").getNodeValue() + "%", propertyDefinitionMap.get(fieldAttributes.getNamedItem("name").getNodeValue()));
 110  
                                         }
 111  
                                 }
 112  0
                                 return docContent;
 113  
                         } else {
 114  
                                 // Standard doc content if no doc content is found in the searchingConfig xml.
 115  0
                                 StringBuffer documentContent = new StringBuffer("<xmlRouting>");
 116  0
                                 String findField = "//searchingConfig/" + FIELD_DEF_E;
 117  0
                                 NodeList nodes = (NodeList) xpath.evaluate(findField, getConfigXML(extensionDefinition), XPathConstants.NODESET);
 118  0
                                 if (nodes == null || nodes.getLength() == 0) {
 119  0
                                         return "";
 120  
                                 }
 121  0
                                 for (int i = 0; i < nodes.getLength(); i++) {
 122  0
                                         Node field = nodes.item(i);
 123  0
                                         NamedNodeMap fieldAttributes = field.getAttributes();
 124  0
                                         if (propertyDefinitionMap != null && !StringUtils.isBlank(propertyDefinitionMap.get(fieldAttributes.getNamedItem("name").getNodeValue()))) {
 125  0
                                                 documentContent.append("<field name=\"");
 126  0
                                                 documentContent.append(fieldAttributes.getNamedItem("name").getNodeValue());
 127  0
                                                 documentContent.append("\"><value>");
 128  0
                                                 documentContent.append(propertyDefinitionMap.get(fieldAttributes.getNamedItem("name").getNodeValue()));
 129  0
                                                 documentContent.append("</value></field>");
 130  
                                         }
 131  
                                 }
 132  0
                                 documentContent.append("</xmlRouting>");
 133  0
                                 return documentContent.toString();
 134  
                         }
 135  0
                 } catch (XPathExpressionException e) {
 136  0
                         LOG.error("error in getSearchContent ", e);
 137  0
                         throw new RuntimeException("Error trying to find xml content with xpath expression", e);
 138  0
                 } catch (Exception e) {
 139  0
                         LOG.error("error in getSearchContent attempting to find xml search content", e);
 140  0
                         throw new RuntimeException("Error trying to get xml search content.", e);
 141  
                 }
 142  
         }
 143  
 
 144  
     @Override
 145  
     public List<DocumentAttribute> extractDocumentAttributes(ExtensionDefinition extensionDefinition,
 146  
             DocumentWithContent documentWithContent) {
 147  0
                 List<DocumentAttribute> searchStorageValues = new ArrayList<DocumentAttribute>();
 148  
                 Document document;
 149  0
         String fullDocumentContent = documentWithContent.getDocumentContent().getFullContent();
 150  0
         if (StringUtils.isBlank(documentWithContent.getDocumentContent().getFullContent())) {
 151  0
             LOG.warn("Empty Document Content found for document id: " + documentWithContent.getDocument().getDocumentId());
 152  0
             return searchStorageValues;
 153  
         }
 154  
                 try {
 155  0
                         document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(
 156  
                                         new InputSource(new BufferedReader(new StringReader(fullDocumentContent))));
 157  0
                 } catch (Exception e){
 158  0
                         LOG.error("error parsing docContent: "+documentWithContent.getDocumentContent(), e);
 159  0
                         throw new RuntimeException("Error trying to parse docContent: "+documentWithContent.getDocumentContent(), e);
 160  0
                 }
 161  0
                 XPath xpath = XPathHelper.newXPath(document);
 162  0
                 String findField = "//searchingConfig/" + FIELD_DEF_E;
 163  
                 try {
 164  0
                         NodeList nodes = (NodeList) xpath.evaluate(findField, getConfigXML(extensionDefinition), XPathConstants.NODESET);
 165  0
             if (nodes == null) {
 166  0
                 LOG.error("Could not find searching configuration (<searchingConfig>) for this XMLSearchAttribute");
 167  
             } else {
 168  
 
 169  0
                             for (int i = 0; i < nodes.getLength(); i++) {
 170  0
                                     Node field = nodes.item(i);
 171  0
                                     NamedNodeMap fieldAttributes = field.getAttributes();
 172  
 
 173  0
                                     String findXpathExpressionPrefix = "//searchingConfig/" + FIELD_DEF_E + "[@name='" + fieldAttributes.getNamedItem("name").getNodeValue() + "']";
 174  0
                                     String findDataTypeXpathExpression = findXpathExpressionPrefix + "/searchDefinition/@dataType";
 175  0
                                     String findXpathExpression = findXpathExpressionPrefix + "/fieldEvaluation/xpathexpression";
 176  0
                                     String fieldDataType = null;
 177  0
                                     String xpathExpression = null;
 178  
                                     try {
 179  0
                         fieldDataType = (String) xpath.evaluate(findDataTypeXpathExpression, getConfigXML(extensionDefinition), XPathConstants.STRING);
 180  0
                                             if (org.apache.commons.lang.StringUtils.isEmpty(fieldDataType)) {
 181  0
                                                     fieldDataType = KewApiConstants.SearchableAttributeConstants.DEFAULT_SEARCHABLE_ATTRIBUTE_TYPE_NAME;
 182  
                                             }
 183  0
                                         xpathExpression = (String) xpath.evaluate(findXpathExpression, getConfigXML(extensionDefinition), XPathConstants.STRING);
 184  0
                                             if (!org.apache.commons.lang.StringUtils.isEmpty(xpathExpression)) {
 185  
 
 186  
                             try {
 187  0
                                 NodeList searchValues = (NodeList) xpath.evaluate(xpathExpression, document.getDocumentElement(), XPathConstants.NODESET);
 188  
                                 // being that this is the standard xml attribute we will return the key with an empty value
 189  
                                 // so we can find it from a doc search using this key
 190  0
                                 if (searchValues.getLength() == 0) {
 191  0
                                         DocumentAttribute searchableValue = this.setupSearchableAttributeValue(fieldDataType, fieldAttributes.getNamedItem("name").getNodeValue(), null);
 192  0
                                         if (searchableValue != null) {
 193  0
                                         searchStorageValues.add(searchableValue);
 194  
                                         }
 195  0
                                 } else {
 196  0
                                         for (int j = 0; j < searchValues.getLength(); j++) {
 197  0
                                         Node searchValue = searchValues.item(j);
 198  0
                                         String value = null;
 199  0
                                         if (searchValue.getFirstChild() != null && (!StringUtils.isEmpty(searchValue.getFirstChild().getNodeValue()))) {
 200  0
                                                 value = searchValue.getFirstChild().getNodeValue();
 201  
                                         }
 202  0
                                             DocumentAttribute searchableValue = this.setupSearchableAttributeValue(fieldDataType, fieldAttributes.getNamedItem("name").getNodeValue(), value);
 203  0
                                             if (searchableValue != null) {
 204  0
                                             searchStorageValues.add(searchableValue);
 205  
                                             }
 206  
                                     }
 207  
                                 }
 208  0
                             } catch (XPathExpressionException e) {
 209  
                                 //try for a string being returned from the expression.  This
 210  
                                 //seems like a poor way to determine our expression return type but
 211  
                                 //it's all I can come up with at the moment.
 212  0
                                 String searchValue = (String) xpath.evaluate(xpathExpression, DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(
 213  
                                                 new InputSource(new BufferedReader(new StringReader(documentWithContent.getDocumentContent().getFullContent())))).getDocumentElement(), XPathConstants.STRING);
 214  0
                                 String value = null;
 215  0
                                 if (StringUtils.isNotBlank(searchValue)) {
 216  0
                                     value = searchValue;
 217  
                                 }
 218  0
                                     DocumentAttribute searchableValue = this.setupSearchableAttributeValue(fieldDataType, fieldAttributes.getNamedItem("name").getNodeValue(), value);
 219  0
                                     if (searchableValue != null) {
 220  0
                                     searchStorageValues.add(searchableValue);
 221  
                                     }
 222  0
                             }
 223  
                                             }
 224  0
                                     } catch (XPathExpressionException e) {
 225  0
                                             LOG.error("error in isMatch ", e);
 226  0
                                             throw new RuntimeException("Error trying to find xml content with xpath expressions: " + findXpathExpression + " or " + xpathExpression, e);
 227  0
                                     } catch (Exception e){
 228  0
                                             LOG.error("error parsing docContent: " + documentWithContent.getDocumentContent(), e);
 229  0
                                             throw new RuntimeException("Error trying to parse docContent: " + documentWithContent.getDocumentContent(), e);
 230  0
                                     }
 231  
                 }
 232  
                         }
 233  0
                 } catch (XPathExpressionException e) {
 234  0
                         LOG.error("error in getSearchStorageValues ", e);
 235  0
                         throw new RuntimeException("Error trying to find xml content with xpath expression: " + findField, e);
 236  0
                 }
 237  0
                 return searchStorageValues;
 238  
         }
 239  
 
 240  
         private DocumentAttribute setupSearchableAttributeValue(String dataType, String key, String value) {
 241  0
                 SearchableAttributeValue attValue = DocumentSearchInternalUtils.getSearchableAttributeValueByDataTypeString(
 242  
                 dataType);
 243  0
                 if (attValue == null) {
 244  0
                         String errorMsg = "Cannot find a SearchableAttributeValue associated with the data type '" + dataType + "'";
 245  0
                     LOG.error("setupSearchableAttributeValue() " + errorMsg);
 246  0
                     throw new RuntimeException(errorMsg);
 247  
                 }
 248  0
         value = (value != null) ? value.trim() : null;
 249  0
         if ( (StringUtils.isNotBlank(value)) && (!attValue.isPassesDefaultValidation(value)) ) {
 250  0
             String errorMsg = "SearchableAttributeValue with the data type '" + dataType + "', key '" + key + "', and value '" + value + "' does not pass default validation and cannot be saved to the database";
 251  0
             LOG.error("setupSearchableAttributeValue() " + errorMsg);
 252  0
             throw new RuntimeException(errorMsg);
 253  
         }
 254  0
                 attValue.setSearchableAttributeKey(key);
 255  0
                 attValue.setupAttributeValue(value);
 256  0
             return attValue.toDocumentAttribute();
 257  
         }
 258  
 
 259  
     @Override
 260  
     public List<RemotableAttributeField> getSearchFields(ExtensionDefinition extensionDefinition, String documentTypeName) {
 261  
 
 262  0
         List<RemotableAttributeField> searchFields = new ArrayList<RemotableAttributeField>();
 263  0
         List<SearchableAttributeValue> searchableAttributeValues = DocumentSearchInternalUtils
 264  
                 .getSearchableAttributeValueObjectTypes();
 265  0
         NodeList fieldNodeList = getConfigXML(extensionDefinition).getElementsByTagName(FIELD_DEF_E);
 266  0
         for (int i = 0; i < fieldNodeList.getLength(); i++) {
 267  0
             Node field = fieldNodeList.item(i);
 268  0
             NamedNodeMap fieldAttributes = field.getAttributes();
 269  
             
 270  0
             boolean hasXPathExpression = false;
 271  
 
 272  0
             String attributeName = fieldAttributes.getNamedItem("name").getNodeValue();
 273  0
             String attributeTitle = fieldAttributes.getNamedItem("title").getNodeValue();
 274  0
             RemotableAttributeField.Builder fieldBuilder = RemotableAttributeField.Builder.create(attributeName);
 275  0
             fieldBuilder.setLongLabel(attributeTitle);
 276  0
             RemotableAttributeLookupSettings.Builder attributeLookupSettings = RemotableAttributeLookupSettings.Builder.create();
 277  0
             fieldBuilder.setAttributeLookupSettings(attributeLookupSettings);
 278  
 
 279  0
             for (int j = 0; j < field.getChildNodes().getLength(); j++) {
 280  0
                 Node childNode = field.getChildNodes().item(j);
 281  0
                 if ("value".equals(childNode.getNodeName())) {
 282  0
                     String defaultValue = childNode.getFirstChild().getNodeValue();
 283  0
                     fieldBuilder.setDefaultValues(Collections.singletonList(defaultValue));
 284  0
                 } else if ("display".equals(childNode.getNodeName())) {
 285  
 
 286  0
                     String typeValue = null;
 287  0
                     List<KeyValue> options = new ArrayList<KeyValue>();
 288  0
                     List<String> selectedOptions = new ArrayList<String>();
 289  
 
 290  
 
 291  0
                     for (int k = 0; k < childNode.getChildNodes().getLength(); k++) {
 292  0
                         Node displayChildNode = childNode.getChildNodes().item(k);
 293  0
                         if ("type".equals(displayChildNode.getNodeName())) {
 294  0
                             typeValue = displayChildNode.getFirstChild().getNodeValue();
 295  0
                         } else if ("meta".equals(displayChildNode.getNodeName())) {
 296  
 
 297  0
                         } else if ("values".equals(displayChildNode.getNodeName())) {
 298  0
                             NamedNodeMap valuesAttributes = displayChildNode.getAttributes();
 299  
                             // this is to allow an empty drop down choice and can probably implemented in a better way
 300  0
                             if (displayChildNode.getFirstChild() != null) {
 301  0
                                 options.add(new ConcreteKeyValue(displayChildNode.getFirstChild().getNodeValue(), valuesAttributes.getNamedItem("title").getNodeValue()));
 302  0
                                 if (valuesAttributes.getNamedItem("selected") != null) {
 303  0
                                     selectedOptions.add(displayChildNode.getFirstChild().getNodeValue());
 304  
                                 }
 305  
                             } else {
 306  0
                                 options.add(new ConcreteKeyValue("", valuesAttributes.getNamedItem("title").getNodeValue()));
 307  
                             }
 308  
                         }
 309  
                     }
 310  
 
 311  0
                     RemotableAbstractControl.Builder controlBuilder = constructControl(typeValue, options);
 312  0
                     fieldBuilder.setControl(controlBuilder);
 313  
 
 314  0
                     if ("date".equals(typeValue)) {
 315  0
                         fieldBuilder.getWidgets().add(RemotableDatepicker.Builder.create());
 316  0
                         fieldBuilder.setDataType(DataType.DATE);
 317  
                     }
 318  
 
 319  0
                     if (selectedOptions != null && !selectedOptions.isEmpty()) {
 320  0
                         fieldBuilder.setDefaultValues(selectedOptions);
 321  
                     }
 322  0
                 } else if ("visibility".equals(childNode.getNodeName())) {
 323  0
                     applyVisibility(fieldBuilder, attributeLookupSettings, (Element)childNode);
 324  0
                 } else if ("searchDefinition".equals(childNode.getNodeName())) {
 325  0
                     NamedNodeMap searchDefAttributes = childNode.getAttributes();
 326  
                     // data type operations
 327  0
                     String dataTypeValue = (searchDefAttributes.getNamedItem("dataType") == null) ? null : searchDefAttributes.getNamedItem("dataType").getNodeValue();
 328  0
                     DataType dataType = convertValueToDataType(dataTypeValue);
 329  0
                     fieldBuilder.setDataType(dataType);
 330  0
                     if (DataType.DATE == fieldBuilder.getDataType()) {
 331  0
                         fieldBuilder.getWidgets().add(RemotableDatepicker.Builder.create());
 332  
                     }
 333  
 
 334  0
                     boolean isRangeSearchField = isRangeSearchField(searchableAttributeValues, fieldBuilder.getDataType(), searchDefAttributes, childNode);
 335  0
                     if (!isRangeSearchField) {
 336  0
                         Boolean caseSensitive = getBooleanValue(searchDefAttributes, "caseSensitive");
 337  0
                         if (caseSensitive != null) {
 338  0
                             attributeLookupSettings.setCaseSensitive(caseSensitive);
 339  
                         }
 340  0
                     } else {
 341  0
                         applyAttributeRange(attributeLookupSettings, fieldBuilder, childNode);
 342  
                     }
 343  
 
 344  
 
 345  
                     /**
 346  
 
 347  
                      TODO - KULRICE-5737 - Figure out how to handle these formatters
 348  
 
 349  
                      String formatterClass = (searchDefAttributes.getNamedItem("formatterClass") == null) ? null : searchDefAttributes.getNamedItem("formatterClass").getNodeValue();
 350  
                      if (!StringUtils.isEmpty(formatterClass)) {
 351  
                      try {
 352  
                      myField.setFormatter((Formatter)Class.forName(formatterClass).newInstance());
 353  
                      } catch (InstantiationException e) {
 354  
                      LOG.error("Unable to get new instance of formatter class: " + formatterClass);
 355  
                      throw new RuntimeException("Unable to get new instance of formatter class: " + formatterClass);
 356  
                      }
 357  
                      catch (IllegalAccessException e) {
 358  
                      LOG.error("Unable to get new instance of formatter class: " + formatterClass);
 359  
                      throw new RuntimeException("Unable to get new instance of formatter class: " + formatterClass);
 360  
                      } catch (ClassNotFoundException e) {
 361  
                      LOG.error("Unable to find formatter class: " + formatterClass);
 362  
                      throw new RuntimeException("Unable to find formatter class: " + formatterClass);
 363  
                      }
 364  
                      }
 365  
 
 366  
                      */
 367  
 
 368  0
                 } else if ("resultColumn".equals(childNode.getNodeName())) {
 369  0
                     NamedNodeMap columnAttributes = childNode.getAttributes();
 370  0
                     Node showNode = columnAttributes.getNamedItem("show");
 371  0
                     boolean isColumnVisible = true;
 372  0
                     if (showNode != null && showNode.getNodeValue() != null) {
 373  0
                         isColumnVisible = Boolean.valueOf(showNode.getNodeValue());
 374  
                     }
 375  0
                     attributeLookupSettings.setInResults(isColumnVisible);
 376  0
                 } else if ("fieldEvaluation".equals(childNode.getNodeName())) {
 377  0
                     for (int k = 0; k < childNode.getChildNodes().getLength(); k++) {
 378  0
                         Node displayChildNode = childNode.getChildNodes().item(k);
 379  0
                         if ("xpathexpression".equals(displayChildNode.getNodeName())) {
 380  0
                             hasXPathExpression = true;
 381  0
                             break;
 382  
                         }
 383  
                     }
 384  0
                 } else if ("lookup".equals(childNode.getNodeName())) {
 385  0
                     XMLAttributeUtils.establishFieldLookup(fieldBuilder, childNode);
 386  
                 }
 387  
             }
 388  
                 
 389  0
             searchFields.add(fieldBuilder.build());
 390  
 
 391  
         }
 392  0
                 return searchFields;
 393  
         }
 394  
 
 395  
     private DataType convertValueToDataType(String dataTypeValue) {
 396  0
         if (StringUtils.isBlank(dataTypeValue)) {
 397  0
             return DataType.STRING;
 398  0
         } else if (KewApiConstants.SearchableAttributeConstants.DATA_TYPE_STRING.equals(dataTypeValue)) {
 399  0
             return DataType.STRING;
 400  0
         } else if (KewApiConstants.SearchableAttributeConstants.DATA_TYPE_DATE.equals(dataTypeValue)) {
 401  0
             return DataType.DATE;
 402  0
         } else if (KewApiConstants.SearchableAttributeConstants.DATA_TYPE_LONG.equals(dataTypeValue)) {
 403  0
             return DataType.LONG;
 404  0
         } else if (KewApiConstants.SearchableAttributeConstants.DATA_TYPE_FLOAT.equals(dataTypeValue)) {
 405  0
             return DataType.FLOAT;
 406  
         }
 407  0
         throw new IllegalArgumentException("Invalid dataTypeValue was given: " + dataTypeValue);
 408  
     }
 409  
 
 410  
     private boolean isRangeSearchField(List<SearchableAttributeValue> searchableAttributeValues, DataType dataType, NamedNodeMap searchDefAttributes, Node searchDefNode) {
 411  0
         for (SearchableAttributeValue attValue : searchableAttributeValues)
 412  
         {
 413  0
             DataType attributeValueDataType = convertValueToDataType(attValue.getAttributeDataType());
 414  0
             if (attributeValueDataType == dataType) {
 415  0
                 return isRangeSearchField(attValue, searchDefAttributes, searchDefNode);
 416  
             }
 417  0
         }
 418  0
         String errorMsg = "Could not find searchable attribute value for data type '" + dataType + "'";
 419  0
         LOG.error("isRangeSearchField(List, String, NamedNodeMap, Node) " + errorMsg);
 420  0
         throw new WorkflowRuntimeException(errorMsg);
 421  
     }
 422  
 
 423  
     private boolean isRangeSearchField(SearchableAttributeValue searchableAttributeValue, NamedNodeMap searchDefAttributes, Node searchDefNode) {
 424  0
         boolean allowRangedSearch = searchableAttributeValue.allowsRangeSearches();
 425  0
         Boolean rangeSearchBoolean = getBooleanValue(searchDefAttributes, "rangeSearch");
 426  0
         boolean rangeSearch = (rangeSearchBoolean != null) && rangeSearchBoolean;
 427  0
         Node rangeDefinition = getPotentialChildNode(searchDefNode, "rangeDefinition");
 428  0
         return ( (allowRangedSearch) && ((rangeDefinition != null) || (rangeSearch)) );
 429  
     }
 430  
 
 431  
     private void applyAttributeRange(RemotableAttributeLookupSettings.Builder attributeLookupSettings, RemotableAttributeField.Builder fieldBuilder, Node searchDefinitionNode) {
 432  0
         NamedNodeMap searchDefAttributes = searchDefinitionNode.getAttributes();
 433  0
         Node rangeDefinitionNode = getPotentialChildNode(searchDefinitionNode, "rangeDefinition");
 434  0
         String lowerBoundDefaultName = KewApiConstants.SearchableAttributeConstants.RANGE_LOWER_BOUND_PROPERTY_PREFIX + fieldBuilder.getName();
 435  0
         String upperBoundDefaultName = KewApiConstants.SearchableAttributeConstants.RANGE_UPPER_BOUND_PROPERTY_PREFIX + fieldBuilder.getName();
 436  0
         attributeLookupSettings.setRanged(true);
 437  0
         attributeLookupSettings.setLowerBoundName(lowerBoundDefaultName);
 438  0
         attributeLookupSettings.setUpperBoundName(upperBoundDefaultName);
 439  0
         if (rangeDefinitionNode != null) {
 440  0
             NamedNodeMap rangeDefinitionAttributes = rangeDefinitionNode.getAttributes();
 441  0
             NamedNodeMap lowerBoundNodeAttributes = getAttributesForPotentialChildNode(rangeDefinitionNode, "lower");
 442  0
             NamedNodeMap upperBoundNodeAttributes = getAttributesForPotentialChildNode(rangeDefinitionNode, "upper");
 443  
             // below methods allow for nullable attribute NamedNodeMaps
 444  0
             RangeBound lowerRangeBound = determineRangeBoundProperties(searchDefAttributes, rangeDefinitionAttributes, lowerBoundNodeAttributes);
 445  0
             if (lowerRangeBound != null) {
 446  0
                 if (lowerRangeBound.inclusive != null) {
 447  0
                     attributeLookupSettings.setLowerBoundInclusive(lowerRangeBound.inclusive);
 448  
                 }
 449  0
                 if (StringUtils.isNotBlank(lowerRangeBound.label)) {
 450  0
                     attributeLookupSettings.setLowerBoundLabel(lowerRangeBound.label);
 451  
                 }
 452  
             }
 453  0
             RangeBound upperRangeBound = determineRangeBoundProperties(searchDefAttributes, rangeDefinitionAttributes, upperBoundNodeAttributes);
 454  0
             if (upperRangeBound != null) {
 455  0
                 if (upperRangeBound.inclusive != null) {
 456  0
                     attributeLookupSettings.setUpperBoundInclusive(upperRangeBound.inclusive);
 457  
                 }
 458  0
                 if (StringUtils.isNotBlank(upperRangeBound.label)) {
 459  0
                     attributeLookupSettings.setUpperBoundLabel(upperRangeBound.label);
 460  
                 }
 461  
             }
 462  
         }
 463  0
     }
 464  
 
 465  
         private NamedNodeMap getAttributesForPotentialChildNode(Node node, String potentialChildNodeName) {
 466  0
                 Node testNode = getPotentialChildNode(node, potentialChildNodeName);
 467  0
                 return testNode != null ? testNode.getAttributes() : null;
 468  
         }
 469  
 
 470  
         private Node getPotentialChildNode(Node node, String childNodeName) {
 471  0
                 if (node != null) {
 472  0
                         for (int k = 0; k < node.getChildNodes().getLength(); k++) {
 473  0
                                 Node testNode = node.getChildNodes().item(k);
 474  0
                                 if (testNode.getNodeName().equals(childNodeName)) {
 475  0
                                         return testNode;
 476  
                                 }
 477  
                         }
 478  
                 }
 479  0
                 return null;
 480  
         }
 481  
 
 482  
     private RangeBound determineRangeBoundProperties(NamedNodeMap searchDefinitionAttributes, NamedNodeMap rangeDefinitionAttributes, NamedNodeMap rangeBoundAttributes) {
 483  0
         RangeBound rangeBound = new RangeBound();
 484  0
         rangeBound.label = getPotentialRangeBoundLabelFromAttributes(rangeBoundAttributes);
 485  0
         List<NamedNodeMap> namedNodeMapsByImportance = new ArrayList<NamedNodeMap>();
 486  0
                 namedNodeMapsByImportance.add(rangeBoundAttributes);
 487  0
                 namedNodeMapsByImportance.add(rangeDefinitionAttributes);
 488  0
                 namedNodeMapsByImportance.add(searchDefinitionAttributes);
 489  0
                 rangeBound.inclusive = getBooleanWithPotentialOverrides(namedNodeMapsByImportance, "inclusive");
 490  0
         return rangeBound;
 491  
     }
 492  
 
 493  
     private String getPotentialRangeBoundLabelFromAttributes(NamedNodeMap rangeBoundAttributes) {
 494  0
         if (rangeBoundAttributes != null) {
 495  0
             String boundLabel = (rangeBoundAttributes.getNamedItem("label") == null) ? null : rangeBoundAttributes.getNamedItem("label").getNodeValue();
 496  0
             if (!StringUtils.isBlank(boundLabel)) {
 497  0
                 return boundLabel;
 498  
             }
 499  
         }
 500  0
         return null;
 501  
     }
 502  
 
 503  
         private Boolean getBooleanWithPotentialOverrides(List<NamedNodeMap> namedNodeMapsByImportance, String attributeName) {
 504  0
         for (NamedNodeMap aNamedNodeMapsByImportance : namedNodeMapsByImportance) {
 505  0
             NamedNodeMap nodeMap = (NamedNodeMap) aNamedNodeMapsByImportance;
 506  0
             Boolean booleanValue = getBooleanValue(nodeMap, attributeName);
 507  0
             if (booleanValue != null) {
 508  0
                 return booleanValue;
 509  
             }
 510  0
         }
 511  0
                 return null;
 512  
     }
 513  
 
 514  
         private Boolean getBooleanValue(NamedNodeMap nodeMap, String attributeName) {
 515  0
                 String nodeValue = getStringValue(nodeMap, attributeName);
 516  0
                 if (nodeValue != null) {
 517  0
                         return Boolean.valueOf(nodeValue);
 518  
                 }
 519  0
                 return null;
 520  
         }
 521  
 
 522  
         private String getStringValue(NamedNodeMap nodeMap, String attributeName) {
 523  0
         if (nodeMap == null || nodeMap.getNamedItem(attributeName) == null || StringUtils.isBlank(nodeMap.getNamedItem(attributeName).getNodeValue())) {
 524  0
             return null;
 525  
         }
 526  0
         return nodeMap.getNamedItem(attributeName).getNodeValue();
 527  
         }
 528  
 
 529  
         private void applyVisibility(RemotableAttributeField.Builder fieldBuilder, RemotableAttributeLookupSettings.Builder attributeLookupSettings, Element visibilityElement) {
 530  0
                 for (int vIndex = 0; vIndex < visibilityElement.getChildNodes().getLength(); vIndex++) {
 531  0
                         Node visibilityChildNode = visibilityElement.getChildNodes().item(vIndex);
 532  0
                         if (visibilityChildNode.getNodeType() == Node.ELEMENT_NODE) {
 533  0
                                 boolean visible = true;
 534  0
                                 NamedNodeMap visibilityAttributes = visibilityChildNode.getAttributes();
 535  0
                                 Node visibleNode = visibilityAttributes.getNamedItem("visible");
 536  0
                                 if (visibleNode != null && visibleNode.getNodeValue() != null) {
 537  0
                                         visible = Boolean.valueOf(visibleNode.getNodeValue());
 538  
                                 } else {
 539  0
                                         NodeList visibilityDecls = visibilityChildNode.getChildNodes();
 540  0
                                         for (int vdIndex = 0; vdIndex < visibilityDecls.getLength(); vdIndex++) {
 541  0
                                                 Node visibilityDecl = visibilityDecls.item(vdIndex);
 542  0
                         if (visibilityDecl.getNodeType() == Node.ELEMENT_NODE) {
 543  0
                                 boolean hasIsMemberOfGroupElement = false;
 544  0
                                 String groupName = null;
 545  0
                                 String groupNamespace = null;
 546  0
                                 if (XmlConstants.IS_MEMBER_OF_GROUP.equals(visibilityDecl.getNodeName())) { // Found an "isMemberOfGroup" element.
 547  0
                                         hasIsMemberOfGroupElement = true;
 548  0
                                         groupName = Utilities.substituteConfigParameters(visibilityDecl.getTextContent()).trim();
 549  0
                                         groupNamespace = Utilities.substituteConfigParameters(((Element)visibilityDecl).getAttribute(XmlConstants.NAMESPACE)).trim();
 550  
                                 }
 551  0
                                 else if (XmlConstants.IS_MEMBER_OF_WORKGROUP.equals(visibilityDecl.getNodeName())) { // Found a deprecated "isMemberOfWorkgroup" element.
 552  0
                                         LOG.warn((new StringBuilder()).append("Rule Attribute XML is using deprecated element '").append(
 553  
                                                         XmlConstants.IS_MEMBER_OF_WORKGROUP).append("', please use '").append(XmlConstants.IS_MEMBER_OF_GROUP).append(
 554  
                                                                         "' instead.").toString());
 555  0
                                         hasIsMemberOfGroupElement = true;
 556  0
                                                             String workgroupName = Utilities.substituteConfigParameters(visibilityDecl.getFirstChild().getNodeValue());
 557  0
                                                             groupNamespace = Utilities.parseGroupNamespaceCode(workgroupName);
 558  0
                                                             groupName = Utilities.parseGroupName(workgroupName);
 559  
                                                     }
 560  0
                                                     if (hasIsMemberOfGroupElement) { // Found one of the "isMemberOf..." elements.
 561  0
                                                             UserSession session = GlobalVariables.getUserSession();
 562  0
                                                             if (session == null) {
 563  0
                                                                     throw new WorkflowRuntimeException("UserSession is null!  Attempted to render the searchable attribute outside of an established session.");
 564  
                                                             }
 565  0
                                 GroupService groupService = KimApiServiceLocator.getGroupService();
 566  
 
 567  0
                                                         Group group = groupService.getGroupByNameAndNamespaceCode(groupNamespace, groupName);
 568  0
                                         visible =  group == null ? false : groupService.isMemberOfGroup(session.getPerson().getPrincipalId(), group.getId());
 569  
                                                     }
 570  
                         }
 571  
                                         }
 572  
                                 }
 573  0
                                 String type = visibilityChildNode.getNodeName();
 574  0
                                 if ("field".equals(type) || "fieldAndColumn".equals(type)) {
 575  
                                         // if it's not visible, coerce this field to a hidden type
 576  0
                                         if (!visible) {
 577  0
                         fieldBuilder.setControl(RemotableHiddenInput.Builder.create());
 578  
                                         }
 579  
                                 }
 580  0
                                 if ("column".equals(type) || "fieldAndColumn".equals(type)) {
 581  0
                                         attributeLookupSettings.setInResults(visible);
 582  
                                 }
 583  
                         }
 584  
                 }
 585  0
         }
 586  
 
 587  
     private RemotableAbstractControl.Builder constructControl(String type, List<KeyValue> options) {
 588  
 
 589  0
         RemotableAbstractControl.Builder control = null;
 590  0
         Map<String, String> optionMap = new HashMap<String, String>();
 591  0
         for (KeyValue option : options) {
 592  0
             optionMap.put(option.getKey(), option.getValue());
 593  
         }
 594  0
         if ("text".equals(type) || "date".equals(type)) {
 595  0
                         control = RemotableTextInput.Builder.create();
 596  0
                 } else if ("select".equals(type)) {
 597  0
             control = RemotableSelect.Builder.create(optionMap);
 598  0
                 } else if ("radio".equals(type)) {
 599  0
             control = RemotableRadioButtonGroup.Builder.create(optionMap);
 600  0
                 } else if ("hidden".equals(type)) {
 601  0
             control = RemotableHiddenInput.Builder.create();
 602  0
                 } else if ("multibox".equals(type)) {
 603  0
             RemotableSelect.Builder builder = RemotableSelect.Builder.create(optionMap);
 604  0
             builder.setMultiple(true);
 605  0
             control = builder;
 606  0
         } else {
 607  0
                     throw new IllegalArgumentException("Illegal field type found: " + type);
 608  
         }
 609  0
         return control;
 610  
 
 611  
     }
 612  
 
 613  
     @Override
 614  
     public List<RemotableAttributeError> validateDocumentAttributeCriteria(ExtensionDefinition extensionDefinition, DocumentSearchCriteria documentSearchCriteria) {
 615  0
                 List<RemotableAttributeError> errors = new ArrayList<RemotableAttributeError>();
 616  
 
 617  0
                 XPath xpath = XPathHelper.newXPath();
 618  0
                 String findField = "//searchingConfig/" + FIELD_DEF_E;
 619  
                 try {
 620  0
                         NodeList nodes = (NodeList) xpath.evaluate(findField, getConfigXML(extensionDefinition), XPathConstants.NODESET);
 621  0
                         if (nodes == null) {
 622  
                                 // no field definitions is de facto valid
 623  0
                             LOG.warn("Could not find any field definitions (<" + FIELD_DEF_E + ">) or possibly a searching configuration (<searchingConfig>) for this XMLSearchAttribute");
 624  
                         } else {
 625  0
                             for (int i = 0; i < nodes.getLength(); i++) {
 626  0
                                     Node field = nodes.item(i);
 627  0
                                     NamedNodeMap fieldAttributes = field.getAttributes();
 628  0
                                         String fieldDefName = fieldAttributes.getNamedItem("name").getNodeValue();
 629  0
                     String fieldDefTitle = ((fieldAttributes.getNamedItem("title")) != null) ? fieldAttributes.getNamedItem("title").getNodeValue() : "";
 630  
 
 631  
                     // check for range search members in the parameter map
 632  0
                     boolean rangeMemberInSearchParams = false;
 633  
 
 634  0
                     Map<String, List<String>> documentAttributeValues = documentSearchCriteria.getDocumentAttributeValues();
 635  0
                     if (documentAttributeValues != null) {
 636  
 
 637  
                         // TODO - KULRICE-5630 - I'm pretty sure this *won't* work because we don't store lower and upper bound keys in the document attributes
 638  
 
 639  0
                         String lowerBoundFieldDefName = KewApiConstants.SearchableAttributeConstants.RANGE_LOWER_BOUND_PROPERTY_PREFIX + fieldDefName;
 640  0
                         String upperBoundFieldDefName = KewApiConstants.SearchableAttributeConstants.RANGE_UPPER_BOUND_PROPERTY_PREFIX + fieldDefName;
 641  0
                         List<String> lowerBoundValues = documentAttributeValues.get(lowerBoundFieldDefName);
 642  0
                         rangeMemberInSearchParams |= CollectionUtils.isNotEmpty(lowerBoundValues) && StringUtils.isNotBlank(lowerBoundValues.get(0));
 643  0
                         List<String> upperBoundValues = documentAttributeValues.get(upperBoundFieldDefName);
 644  0
                         rangeMemberInSearchParams |= CollectionUtils.isNotEmpty(upperBoundValues) && StringUtils.isNotBlank(upperBoundValues.get(0));
 645  
 
 646  0
                         List<String> testObject = documentAttributeValues.get(fieldDefName);
 647  0
                                             if (testObject != null || rangeMemberInSearchParams) {
 648  
 
 649  
                             // check to see if we need to process this field at all
 650  0
                             if (!rangeMemberInSearchParams) {
 651  0
                                 if (testObject.size() == 1) {
 652  0
                                     String stringVariable = testObject.get(0);
 653  0
                                     if (StringUtils.isBlank(stringVariable)) {
 654  
                                         // field is not multi value and is empty... skip it
 655  0
                                         continue;
 656  
                                     }
 657  0
                                 } else {
 658  0
                                     boolean allAreBlank = true;
 659  0
                                     for (String testString : testObject) {
 660  0
                                         if (StringUtils.isNotBlank(testString)) {
 661  0
                                             allAreBlank = false;
 662  0
                                             break;
 663  
                                         }
 664  
                                     }
 665  0
                                     if (allAreBlank) {
 666  
                                         // field is multivalue but all values are blank... skip it
 667  0
                                         continue;
 668  
                                     }
 669  
                                 }
 670  
                             }
 671  0
                             String findXpathExpressionPrefix = "//searchingConfig/" + FIELD_DEF_E + "[@name='" + fieldDefName + "']";
 672  0
                                                 Node searchDefNode = (Node) xpath.evaluate(findXpathExpressionPrefix + "/searchDefinition", getConfigXML(extensionDefinition), XPathConstants.NODE);
 673  0
                                                 NamedNodeMap searchDefAttributes = null;
 674  0
                                             String fieldDataType = null;
 675  0
                                                 if (searchDefNode != null) {
 676  
                                                     // get the data type from the xml
 677  0
                                                         searchDefAttributes = searchDefNode.getAttributes();
 678  0
                                                         if (searchDefAttributes.getNamedItem("dataType") != null) {
 679  0
                                                             fieldDataType = searchDefAttributes.getNamedItem("dataType").getNodeValue();
 680  
                                                         }
 681  
 
 682  
                                                 }
 683  0
                                                 if (org.apache.commons.lang.StringUtils.isEmpty(fieldDataType)) {
 684  0
                                                         fieldDataType = KewApiConstants.SearchableAttributeConstants.DEFAULT_SEARCHABLE_ATTRIBUTE_TYPE_NAME;
 685  
                                                 }
 686  
                                                 // get the searchable attribute value by using the data type
 687  0
                                                 SearchableAttributeValue attributeValue = DocumentSearchInternalUtils
 688  
                                     .getSearchableAttributeValueByDataTypeString(fieldDataType);
 689  0
                                                 if (attributeValue == null) {
 690  0
                                                         String errorMsg = "Cannot find SearchableAttributeValue for field data type '" + fieldDataType + "'";
 691  0
                                                         LOG.error("validateUserSearchInputs() " + errorMsg);
 692  0
                                                         throw new RuntimeException(errorMsg);
 693  
                                                 }
 694  
 
 695  0
                                                 if (rangeMemberInSearchParams) {
 696  
 
 697  0
                                 NamedNodeMap lowerBoundRangeAttributes = null;
 698  0
                                 NamedNodeMap upperBoundRangeAttributes = null;
 699  0
                                                         Node rangeDefinitionNode = getPotentialChildNode(searchDefNode, "rangeDefinition");
 700  0
                                                         NamedNodeMap rangeDefinitionAttributes = rangeDefinitionNode != null ? rangeDefinitionNode.getAttributes() : null;
 701  
 
 702  0
                                 String lowerBoundValue = null;
 703  0
                                 if (CollectionUtils.isNotEmpty(lowerBoundValues)) {
 704  0
                                     if (lowerBoundValues.size() > 1) {
 705  0
                                         throw new WorkflowRuntimeException("Encountered an illegal lower bound with more then one value for field: " + fieldDefName);
 706  
                                     }
 707  0
                                     lowerBoundValue = lowerBoundValues.get(0);
 708  
                                 }
 709  0
                                 String upperBoundValue = null;
 710  0
                                 if (CollectionUtils.isNotEmpty(upperBoundValues)) {
 711  0
                                     if (upperBoundValues.size() > 1) {
 712  0
                                         throw new WorkflowRuntimeException("Encountered an illegal upper bound with more then one value for field: " + fieldDefName);
 713  
                                     }
 714  0
                                     upperBoundValue = upperBoundValues.get(0);
 715  
                                 }
 716  
 
 717  0
                                                         if (StringUtils.isNotBlank(lowerBoundValue)) {
 718  0
                                     lowerBoundRangeAttributes = getAttributesForPotentialChildNode(rangeDefinitionNode, "lower");
 719  0
                                                                 errors.addAll(performValidation(extensionDefinition, attributeValue,
 720  
                                                                                 lowerBoundFieldDefName, lowerBoundValue, constructRangeFieldErrorPrefix(fieldDefTitle,lowerBoundRangeAttributes), findXpathExpressionPrefix));
 721  
                                                         }
 722  0
                                 if (StringUtils.isNotBlank(upperBoundValue)) {
 723  0
                                     upperBoundRangeAttributes = getAttributesForPotentialChildNode(rangeDefinitionNode, "upper");
 724  0
                                                                 errors.addAll(performValidation(extensionDefinition, attributeValue,
 725  
                                                                                 upperBoundFieldDefName, upperBoundValue, constructRangeFieldErrorPrefix(fieldDefTitle, upperBoundRangeAttributes), findXpathExpressionPrefix));
 726  
                                                         }
 727  0
                                 if (errors.isEmpty()) {
 728  0
                                     Boolean rangeValid = attributeValue.isRangeValid(lowerBoundValue, upperBoundValue);
 729  0
                                     if (rangeValid != null && !rangeValid) {
 730  0
                                         String lowerLabel = getPotentialRangeBoundLabelFromAttributes(lowerBoundRangeAttributes);
 731  0
                                         String upperLabel = getPotentialRangeBoundLabelFromAttributes(upperBoundRangeAttributes);
 732  0
                                         String errorMsg = "The " + fieldDefTitle + " range is incorrect.  The " + (StringUtils.isNotBlank(lowerLabel) ? lowerLabel : KewApiConstants.SearchableAttributeConstants.DEFAULT_RANGE_SEARCH_LOWER_BOUND_LABEL) + " value entered must come before the " + (StringUtils.isNotBlank(upperLabel) ? upperLabel : KewApiConstants.SearchableAttributeConstants.DEFAULT_RANGE_SEARCH_UPPER_BOUND_LABEL) + " value";
 733  0
                                         LOG.debug("validateUserSearchInputs() " + errorMsg + " :: field type '" + attributeValue.getAttributeDataType() + "'");
 734  0
                                         errors.add(RemotableAttributeError.Builder.create(fieldDefName, errorMsg).build());
 735  
                                     }
 736  
                                 }
 737  
 
 738  0
                                                 } else {
 739  0
                                 List<String> enteredValue = documentAttributeValues.get(fieldDefName);
 740  0
                                 if (enteredValue.size() == 1) {
 741  0
                                     String stringVariable = enteredValue.get(0);
 742  0
                                     errors.addAll(performValidation(extensionDefinition, attributeValue, fieldDefName, stringVariable, fieldDefTitle, findXpathExpressionPrefix));
 743  0
                                 } else {
 744  0
                                     for (String stringVariable : enteredValue) {
 745  0
                                         errors.addAll(performValidation(extensionDefinition, attributeValue, fieldDefName, stringVariable, "One value for " + fieldDefTitle, findXpathExpressionPrefix));
 746  
                                     }
 747  
 
 748  
                                 }
 749  
                                             }
 750  
                                         }
 751  
                     }
 752  
                             }
 753  
             }
 754  0
                 } catch (XPathExpressionException e) {
 755  0
                         LOG.error("error in validateUserSearchInputs ", e);
 756  0
                         throw new RuntimeException("Error trying to find xml content with xpath expression: " + findField, e);
 757  0
                 }
 758  0
                 return errors;
 759  
         }
 760  
 
 761  
     private String constructRangeFieldErrorPrefix(String fieldDefLabel, NamedNodeMap rangeBoundAttributes) {
 762  0
         String potentialLabel = getPotentialRangeBoundLabelFromAttributes(rangeBoundAttributes);
 763  0
         if ( (StringUtils.isNotBlank(potentialLabel)) && (StringUtils.isNotBlank(fieldDefLabel)) ) {
 764  0
             return fieldDefLabel + " " + potentialLabel + " Field";
 765  0
         } else if (StringUtils.isNotBlank(fieldDefLabel)) {
 766  0
             return fieldDefLabel + " Range Field";
 767  0
         } else if (StringUtils.isNotBlank(potentialLabel)) {
 768  0
             return "Range Field " + potentialLabel + " Field";
 769  
         }
 770  0
         return null;
 771  
     }
 772  
 
 773  
         private List<RemotableAttributeError> performValidation(ExtensionDefinition extensionDefinition, SearchableAttributeValue attributeValue, String fieldDefName, String enteredValue, String errorMessagePrefix, String findXpathExpressionPrefix) throws XPathExpressionException {
 774  0
                 List<RemotableAttributeError> errors = new ArrayList<RemotableAttributeError>();
 775  0
                 XPath xpath = XPathHelper.newXPath();
 776  0
                 if ( attributeValue.allowsWildcards()) {
 777  0
                         enteredValue = enteredValue.replaceAll(KewApiConstants.SearchableAttributeConstants.SEARCH_WILDCARD_CHARACTER_REGEX_ESCAPED, "");
 778  
                 }
 779  0
                 if (!attributeValue.isPassesDefaultValidation(enteredValue)) {
 780  0
             errorMessagePrefix = (StringUtils.isNotBlank(errorMessagePrefix)) ? errorMessagePrefix : "Field";
 781  0
                         String errorMsg = errorMessagePrefix + " with value '" + enteredValue + "' does not conform to standard validation for field type.";
 782  0
                         LOG.debug("validateUserSearchInputs() " + errorMsg + " :: field type '" + attributeValue.getAttributeDataType() + "'");
 783  0
                         errors.add(RemotableAttributeError.Builder.create(fieldDefName, errorMsg).build());
 784  0
                 } else {
 785  0
                         String findValidation = findXpathExpressionPrefix + "/validation/regex";
 786  0
                         String regex = (String) xpath.evaluate(findValidation, getConfigXML(extensionDefinition), XPathConstants.STRING);
 787  0
                         if (!org.apache.commons.lang.StringUtils.isEmpty(regex)) {
 788  0
                                 Pattern pattern = Pattern.compile(regex);
 789  0
                                 Matcher matcher = pattern.matcher(enteredValue);
 790  0
                                 if (!matcher.matches()) {
 791  0
                                         String findErrorMessage = findXpathExpressionPrefix + "/validation/message";
 792  0
                                         String message = (String) xpath.evaluate(findErrorMessage, getConfigXML(extensionDefinition), XPathConstants.STRING);
 793  0
                                         errors.add(RemotableAttributeError.Builder.create(fieldDefName, message).build());
 794  
                                 }
 795  
                         }
 796  
                 }
 797  0
                 return errors;
 798  
         }
 799  
 
 800  
         public Element getConfigXML(ExtensionDefinition extensionDefinition) {
 801  
                 try {
 802  0
             String xmlConfigData = extensionDefinition.getConfiguration().get(KewApiConstants.ATTRIBUTE_XML_CONFIG_DATA);
 803  0
                         return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new BufferedReader(new StringReader(xmlConfigData)))).getDocumentElement();
 804  0
                 } catch (Exception e) {
 805  0
                         String ruleAttrStr = (extensionDefinition == null ? null : extensionDefinition.getName());
 806  0
                         LOG.error("error parsing xml data from search attribute: " + ruleAttrStr, e);
 807  0
                         throw new RuntimeException("error parsing xml data from searchable attribute: " + ruleAttrStr, e);
 808  
                 }
 809  
         }
 810  
 
 811  
     /**
 812  
      * Simple structure for internal usage that includes a case sensitive indicator and label value for one end of
 813  
      * a bounded range.
 814  
      */
 815  0
     private static final class RangeBound {
 816  
         Boolean inclusive;
 817  
         String label;
 818  
     }
 819  
 
 820  
 }