Coverage Report - org.kuali.student.common.validator.DefaultValidatorImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
DefaultValidatorImpl
55%
298/537
41%
179/434
7.2
 
 1  
 /**
 2  
  * Copyright 2010 The Kuali Foundation Licensed under the Educational Community License, Version 2.0 (the "License"); you may
 3  
  * not use this file except in compliance with the License. You may obtain a copy of the License at
 4  
  * http://www.osedu.org/licenses/ECL-2.0 Unless required by applicable law or agreed to in writing, software distributed
 5  
  * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 6  
  * implied. See the License for the specific language governing permissions and limitations under the License.
 7  
  */
 8  
 
 9  
 package org.kuali.student.common.validator;
 10  
 
 11  
 import java.lang.reflect.InvocationTargetException;
 12  
 import java.util.ArrayList;
 13  
 import java.util.Collection;
 14  
 import java.util.Date;
 15  
 import java.util.HashMap;
 16  
 import java.util.Iterator;
 17  
 import java.util.List;
 18  
 import java.util.Map;
 19  
 import java.util.Stack;
 20  
 
 21  
 import org.apache.commons.beanutils.PropertyUtils;
 22  
 import org.apache.log4j.Logger;
 23  
 import org.kuali.student.common.dictionary.dto.CaseConstraint;
 24  
 import org.kuali.student.common.dictionary.dto.CommonLookupParam;
 25  
 import org.kuali.student.common.dictionary.dto.Constraint;
 26  
 import org.kuali.student.common.dictionary.dto.DataType;
 27  
 import org.kuali.student.common.dictionary.dto.FieldDefinition;
 28  
 import org.kuali.student.common.dictionary.dto.LookupConstraint;
 29  
 import org.kuali.student.common.dictionary.dto.MustOccurConstraint;
 30  
 import org.kuali.student.common.dictionary.dto.ObjectStructureDefinition;
 31  
 import org.kuali.student.common.dictionary.dto.RequiredConstraint;
 32  
 import org.kuali.student.common.dictionary.dto.ValidCharsConstraint;
 33  
 import org.kuali.student.common.dictionary.dto.WhenConstraint;
 34  
 import org.kuali.student.common.messages.dto.Message;
 35  
 import org.kuali.student.common.messages.service.MessageService;
 36  
 import org.kuali.student.common.search.dto.SearchParam;
 37  
 import org.kuali.student.common.search.dto.SearchRequest;
 38  
 import org.kuali.student.common.search.dto.SearchResult;
 39  
 import org.kuali.student.common.search.service.SearchDispatcher;
 40  
 import org.kuali.student.common.util.MessageUtils;
 41  
 import org.kuali.student.common.validation.dto.ValidationResultInfo;
 42  
 import org.kuali.student.common.validation.dto.ValidationResultInfo.ErrorLevel;
 43  
 
 44  18
 public class DefaultValidatorImpl extends BaseAbstractValidator {
 45  1
     final static Logger LOG = Logger.getLogger(DefaultValidatorImpl.class);
 46  
 
 47  18
     private MessageService messageService = null;
 48  
 
 49  
     private SearchDispatcher searchDispatcher;
 50  
 
 51  18
     private String messageLocaleKey = "en";
 52  
 
 53  18
     private String messageGroupKey = "validation";
 54  
 
 55  18
     private DateParser dateParser = new ServerDateParser();
 56  
 
 57  18
     private boolean serverSide = true;
 58  
 
 59  
     public MessageService getMessageService() {
 60  0
         return messageService;
 61  
     }
 62  
 
 63  
     public void setMessageService(MessageService messageService) {
 64  9
         this.messageService = messageService;
 65  9
     }
 66  
 
 67  
     public String getMessageLocaleKey() {
 68  0
         return messageLocaleKey;
 69  
     }
 70  
 
 71  
     public void setMessageLocaleKey(String messageLocaleKey) {
 72  0
         this.messageLocaleKey = messageLocaleKey;
 73  0
     }
 74  
 
 75  
     public String getMessageGroupKey() {
 76  0
         return messageGroupKey;
 77  
     }
 78  
 
 79  
     public void setMessageGroupKey(String messageGroupKey) {
 80  0
         this.messageGroupKey = messageGroupKey;
 81  0
     }
 82  
 
 83  
     public void setDateParser(DateParser dateParser) {
 84  9
         this.dateParser = dateParser;
 85  9
     }
 86  
 
 87  
         /**
 88  
      * @return the serverSide
 89  
      */
 90  
     public boolean isServerSide() {
 91  0
         return serverSide;
 92  
     }
 93  
 
 94  
     /**
 95  
      * @param serverSide
 96  
      *            the serverSide to set
 97  
      */
 98  
     public void setServerSide(boolean serverSide) {
 99  0
         this.serverSide = serverSide;
 100  0
     }
 101  
 
 102  
     /**
 103  
      * @return the dateParser
 104  
      */
 105  
     public DateParser getDateParser() {
 106  0
         return dateParser;
 107  
     }
 108  
 
 109  
     /**
 110  
      * Validate Object and all its nested child objects for given type and state
 111  
      *
 112  
      * @param data
 113  
      * @param objStructure
 114  
      * @return
 115  
      */
 116  
     public List<ValidationResultInfo> validateObject(Object data, ObjectStructureDefinition objStructure) {
 117  
 
 118  12
         List<ValidationResultInfo> results = new ArrayList<ValidationResultInfo>();
 119  12
             Stack<String> elementStack = new Stack<String>();
 120  
 
 121  12
        validateObject(results, data, objStructure, elementStack, data, objStructure, true);
 122  
        
 123  12
        return results;
 124  
     }
 125  
 
 126  
     private void validateObject(List<ValidationResultInfo> results, Object data, ObjectStructureDefinition objStructure, Stack<String> elementStack,  Object rootData, ObjectStructureDefinition rootObjStructure, boolean isRoot) {
 127  
 
 128  17
         ConstraintDataProvider dataProvider = new BeanConstraintDataProvider();
 129  17
         dataProvider.initialize(data);
 130  
 
 131  
         // Push object structure to the top of the stack
 132  17
         StringBuilder objXPathElement = new StringBuilder(dataProvider.getPath());
 133  
 
 134  17
         if(!isRoot && !objXPathElement.toString().isEmpty()){
 135  0
                 elementStack.push(objXPathElement.toString());
 136  
         }
 137  
 
 138  
         /*
 139  
          * Do nothing if the object to be validated is not type/state or if the objectstructure with constraints is not
 140  
          * provided
 141  
          */
 142  17
         if (null == objStructure) {
 143  0
             return;
 144  
         }
 145  
 
 146  17
         for (FieldDefinition f : objStructure.getAttributes()) {
 147  83
             validateField(results, f, objStructure, dataProvider, elementStack, rootData, rootObjStructure);
 148  
 
 149  
             // Use Custom Validators
 150  83
             if (f.getCustomValidatorClass() != null || f.isServerSide() && serverSide) {
 151  0
                     Validator customValidator = validatorFactory.getValidator(f.getCustomValidatorClass());
 152  0
                     if(customValidator==null){
 153  0
                             throw new RuntimeException("Custom Validator "+f.getCustomValidatorClass()+" was not configured in this context");
 154  
                     }
 155  0
                     List<ValidationResultInfo> l = customValidator.validateObject(f,data, objStructure,elementStack);
 156  0
                     results.addAll(l);
 157  83
             }
 158  
         }
 159  17
         if(!isRoot && !objXPathElement.toString().isEmpty()){
 160  0
                 elementStack.pop();
 161  
         }
 162  
 
 163  
         /* All Field validations are returned right now */
 164  
         // List<ValidationResultInfo> resultsBuffer = new
 165  
         // ArrayList<ValidationResultInfo>();
 166  
         // for (ValidationResultContainer vc : results) {
 167  
         // if (skipFields.contains(vc.getElement()) == false) {
 168  
         // resultsBuffer.add(vc);
 169  
         // }
 170  
         // }
 171  
         // results = resultsBuffer;
 172  17
     }
 173  
 
 174  
     public void validateField(List<ValidationResultInfo> results, FieldDefinition field, ObjectStructureDefinition objStruct, ConstraintDataProvider dataProvider, Stack<String> elementStack,  Object rootData, ObjectStructureDefinition rootObjectStructure) {
 175  
 
 176  83
         Object value = dataProvider.getValue(field.getName());
 177  
 
 178  
         // Handle null values in field
 179  83
         if (value == null || "".equals(value.toString().trim())) {
 180  29
             processConstraint(results, field, objStruct, value, dataProvider, elementStack, rootData, rootObjectStructure);
 181  29
             return; //no need to do further processing
 182  
         }
 183  
 
 184  
         /*
 185  
          * For complex object structures only the following constraints apply 1. TypeStateCase 2. MinOccurs 3. MaxOccurs
 186  
          */
 187  54
         if (DataType.COMPLEX.equals(field.getDataType())) {
 188  5
             ObjectStructureDefinition nestedObjStruct = null;
 189  
 
 190  5
             if (null != field.getDataObjectStructure()) {
 191  5
                 nestedObjStruct = field.getDataObjectStructure();
 192  
             }
 193  
 
 194  5
             elementStack.push(field.getName());
 195  
 //                   beanPathStack.push(field.isDynamic()?"attributes("+field.getName()+")":field.getName());
 196  
 
 197  5
             if (value instanceof Collection) {
 198  
 
 199  5
                 String xPathForCollection = getElementXpath(elementStack) + "/*";
 200  
 
 201  5
                 int i=0;
 202  5
                 for (Object o : (Collection<?>) value) {
 203  5
                         elementStack.push(Integer.toString(i));
 204  
 //                        beanPathStack.push(!beanPathStack.isEmpty()?beanPathStack.pop():""+"["+i+"]");
 205  5
                     processNestedObjectStructure(results, o, nestedObjStruct, field, elementStack, rootData, rootObjectStructure);
 206  
 //                    beanPathStack.pop();
 207  
 //                    beanPathStack.push(field.isDynamic()?"attributes("+field.getName()+")":field.getName());
 208  5
                     elementStack.pop();
 209  5
                     i++;
 210  
                 }
 211  5
                 if (field.getMinOccurs() != null && field.getMinOccurs() > ((Collection<?>) value).size()) {
 212  0
                     ValidationResultInfo valRes = new ValidationResultInfo(xPathForCollection, value);
 213  0
                     valRes.setError(MessageUtils.interpolate(getMessage("validation.minOccurs"), toMap(field)));
 214  0
                     results.add(valRes);
 215  
                 }
 216  
 
 217  5
                 Integer maxOccurs = tryParse(field.getMaxOccurs());
 218  5
                 if (maxOccurs != null && maxOccurs < ((Collection<?>) value).size()) {
 219  0
                     ValidationResultInfo valRes = new ValidationResultInfo(xPathForCollection, value);
 220  0
                     valRes.setError(MessageUtils.interpolate(getMessage("validation.maxOccurs"), toMap(field)));
 221  0
                     results.add(valRes);
 222  
                 }
 223  5
             } else {
 224  0
                 if (null != value) {
 225  0
                     processNestedObjectStructure(results, value, nestedObjStruct, field, elementStack, rootData, rootObjectStructure);
 226  
                 } else {
 227  0
                     if (field.getMinOccurs() != null && field.getMinOccurs() > 0) {
 228  0
                         ValidationResultInfo val = new ValidationResultInfo(getElementXpath(elementStack), value);
 229  0
                         if(field.getLabelKey()!=null){
 230  0
                                 val.setError(getMessage(field.getLabelKey()));
 231  
                         } else {
 232  0
                                 val.setError(getMessage("validation.required"));
 233  
                         }
 234  0
                         results.add(val);
 235  
                     }
 236  
                 }
 237  
             }
 238  
             
 239  
 //            beanPathStack.pop();
 240  5
             elementStack.pop();
 241  
 
 242  5
         } else { // If non complex data type
 243  
 
 244  49
             if (value instanceof Collection) {
 245  
 
 246  0
                 if(((Collection<?>)value).isEmpty()){
 247  0
                     processConstraint(results, field, objStruct, "", dataProvider, elementStack, rootData, rootObjectStructure);
 248  
                 }
 249  
 
 250  0
                     int i = 0;
 251  0
                 for (Object o : (Collection<?>) value) {
 252  
                         //This is tricky, try to temporarily swap the field name and the index in the elementStack(this is for lists of non complex types)
 253  0
                         String oldFieldName = field.getName();
 254  0
                         elementStack.push(oldFieldName);
 255  0
                         field.setName(Integer.toBinaryString(i));
 256  
 
 257  0
                         processConstraint(results, field, objStruct, o, dataProvider, elementStack, rootData, rootObjectStructure);
 258  
                     
 259  0
                         elementStack.pop();
 260  0
                     field.setName(oldFieldName);
 261  0
                     i++;
 262  0
                 }
 263  
 
 264  0
                 String xPath = getElementXpath(elementStack) + "/" + field.getName() + "/*";
 265  0
                 if (field.getMinOccurs() != null && field.getMinOccurs() > ((Collection<?>) value).size()) {
 266  0
                     ValidationResultInfo valRes = new ValidationResultInfo(xPath, value);
 267  0
                     valRes.setError(MessageUtils.interpolate(getMessage("validation.minOccurs"), toMap(field)));
 268  0
                     results.add(valRes);
 269  
                 }
 270  
 
 271  0
                 Integer maxOccurs = tryParse(field.getMaxOccurs());
 272  0
                 if (maxOccurs != null && maxOccurs < ((Collection<?>) value).size()) {
 273  0
                     ValidationResultInfo valRes = new ValidationResultInfo(xPath, value);
 274  0
                     valRes.setError(MessageUtils.interpolate(getMessage("validation.maxOccurs"), toMap(field)));
 275  0
                     results.add(valRes);
 276  
                 }
 277  0
             } else {
 278  49
                 processConstraint(results, field, objStruct, value, dataProvider, elementStack, rootData, rootObjectStructure);
 279  
             }
 280  
 
 281  
         }
 282  54
     }
 283  
 
 284  
     protected Integer tryParse(String s) {
 285  46
         Integer result = null;
 286  46
         if (s != null) {
 287  
             try {
 288  40
                 result = Integer.valueOf(s);
 289  0
             } catch (NumberFormatException e) {
 290  
                 // do nothing
 291  40
             }
 292  
         }
 293  46
         return result;
 294  
     }
 295  
 
 296  
     protected void processNestedObjectStructure(List<ValidationResultInfo> results, Object value, ObjectStructureDefinition nestedObjStruct, FieldDefinition field, Stack<String> elementStack,  Object rootData, ObjectStructureDefinition rootObjStructure) {
 297  5
         validateObject(results, value, nestedObjStruct, elementStack, rootData, rootObjStructure, false);
 298  5
     }
 299  
 
 300  
     protected void processConstraint(List<ValidationResultInfo> valResults, FieldDefinition field, ObjectStructureDefinition objStructure, Object value, ConstraintDataProvider dataProvider, Stack<String> elementStack,  Object rootData, ObjectStructureDefinition rootObjStructure) {
 301  
 
 302  
         // Process Case Constraint
 303  
         // Case Constraint are only evaluated on the field. Nested case constraints are currently ignored
 304  78
         Constraint caseConstraint = processCaseConstraint(valResults, field.getCaseConstraint(), objStructure, value, dataProvider, elementStack, rootData, rootObjStructure);
 305  
 
 306  78
         Constraint constraint = (null != caseConstraint) ? caseConstraint : field;
 307  
 
 308  78
         processBaseConstraints(valResults, constraint, field, value, elementStack);
 309  
         
 310  
         // Stop other checks if value is null
 311  78
         if (value == null || "".equals(value.toString().trim())) {
 312  29
             return;
 313  
         }
 314  
 
 315  49
         String elementPath = getElementXpath(elementStack) + "/" + field.getName();
 316  
 
 317  
         // Process Valid Chars
 318  49
         if (null != constraint.getValidChars()) {
 319  21
             ValidationResultInfo val = processValidCharConstraint(elementPath, constraint.getValidChars(), dataProvider, value);
 320  21
             if (null != val) {
 321  3
                 valResults.add(val);
 322  
             }
 323  
         }
 324  
 
 325  
         // Process Require Constraints (only if this field has value)
 326  49
         if (value != null && !"".equals(value.toString().trim())) {
 327  49
             if (null != constraint.getRequireConstraint() && constraint.getRequireConstraint().size() > 0) {
 328  5
                 for (RequiredConstraint rc : constraint.getRequireConstraint()) {
 329  5
                     ValidationResultInfo val = processRequireConstraint(elementPath, rc, field, objStructure, dataProvider);
 330  5
                     if (null != val) {
 331  1
                         valResults.add(val);
 332  
                         //FIXME: For clarity, might be better to handle this in the processRequireConstraint method instead.
 333  1
                         processCrossFieldWarning(valResults, rc, val.getErrorLevel(), field.getName());
 334  
                     }
 335  5
                 }
 336  
             }
 337  
         }
 338  
 
 339  
         // Process Occurs Constraint
 340  49
         if (null != constraint.getOccursConstraint() && constraint.getOccursConstraint().size() > 0) {
 341  0
             for (MustOccurConstraint oc : constraint.getOccursConstraint()) {
 342  0
                 ValidationResultInfo val = processOccursConstraint(elementPath, oc, field, objStructure, dataProvider);
 343  0
                 if (null != val) {
 344  0
                     valResults.add(val); 
 345  
                 }
 346  0
             }
 347  
         }
 348  
 
 349  
         // Process lookup Constraint
 350  49
         if (null != constraint.getLookupDefinition()) {
 351  4
             processLookupConstraint(valResults, constraint.getLookupDefinition(), field, elementStack, dataProvider,objStructure, rootData, rootObjStructure, value);
 352  
         }
 353  49
     }
 354  
 
 355  
     protected ValidationResultInfo processRequireConstraint(String element, RequiredConstraint constraint, FieldDefinition field, ObjectStructureDefinition objStructure, ConstraintDataProvider dataProvider) {
 356  
 
 357  5
         ValidationResultInfo val = null;
 358  
 
 359  5
         String fieldName = constraint.getFieldPath();// TODO parse fieldname from here
 360  5
         Object fieldValue = dataProvider.getValue(fieldName);
 361  
 
 362  5
         boolean result = true;
 363  
 
 364  5
         if (fieldValue instanceof java.lang.String) {
 365  5
             result = hasText((String) fieldValue);
 366  0
         } else if (fieldValue instanceof Collection) {
 367  0
             result = (((Collection<?>) fieldValue).size() > 0);
 368  
         } else {
 369  0
             result = (null != fieldValue) ? true : false;
 370  
         }
 371  
 
 372  5
         if (!result) {
 373  1
             Map<String, Object> rMap = new HashMap<String, Object>();
 374  1
             rMap.put("field1", field.getName());
 375  1
             rMap.put("field2", fieldName);
 376  1
             val = new ValidationResultInfo(element, fieldValue);
 377  1
             val.setMessage(MessageUtils.interpolate(getMessage("validation.requiresField"), rMap));
 378  1
             val.setLevel(constraint.getErrorLevel());                       
 379  
         }
 380  
 
 381  5
         return val;
 382  
     }
 383  
 
 384  
     /**
 385  
      * Process caseConstraint tag and sets any of the base constraint items if any of the when condition matches
 386  
      *
 387  
      * @param caseConstraint
 388  
      * @param caseConstraint
 389  
      * @param field
 390  
      */
 391  
     protected Constraint processCaseConstraint(List<ValidationResultInfo> valResults, CaseConstraint caseConstraint, ObjectStructureDefinition objStructure, Object value, ConstraintDataProvider dataProvider, Stack<String> elementStack,  Object rootData, ObjectStructureDefinition rootObjStructure) {
 392  
 
 393  80
         if (null == caseConstraint) {
 394  56
             return null;
 395  
         }
 396  
 
 397  24
         String operator = (hasText(caseConstraint.getOperator())) ? caseConstraint.getOperator() : "EQUALS";
 398  24
         FieldDefinition caseField = null;
 399  24
         boolean absolutePath = false;
 400  24
         if(hasText(caseConstraint.getFieldPath())){
 401  24
                 if(caseConstraint.getFieldPath().startsWith("/")){
 402  0
                         absolutePath = true;
 403  0
                         caseField = ValidatorUtils.getField(caseConstraint.getFieldPath().substring(1), rootObjStructure);
 404  
                 }else{
 405  24
                         caseField = ValidatorUtils.getField(caseConstraint.getFieldPath(), objStructure); 
 406  
                 }
 407  
         }
 408  
 
 409  
         // TODO: What happens when the field is not in the dataProvider?
 410  24
         Object fieldValue = value;
 411  24
         if(caseField!=null){
 412  7
                 if(absolutePath){
 413  
                         try {
 414  0
                                 if(caseField.isDynamic()){
 415  
                                         //Pull the value from the dynamic attribute map
 416  
                                         //TODO There needs to be some mapping from PropertyUtils to the KS path
 417  
                                         //Until then, this will only work for root level properties
 418  0
                                         Map<String,String> attributes = (Map<String,String>) PropertyUtils.getNestedProperty(rootData, "attributes");
 419  0
                                         if(attributes!=null){
 420  0
                                                 fieldValue = attributes.get(caseConstraint.getFieldPath().substring(1));
 421  
                                         }
 422  0
                                 }else{
 423  0
                                         fieldValue = PropertyUtils.getNestedProperty(rootData, caseConstraint.getFieldPath().substring(1));
 424  
                                 }
 425  0
                                 } catch (IllegalAccessException e) {
 426  0
                                 } catch (InvocationTargetException e) {
 427  0
                                 } catch (NoSuchMethodException e) {
 428  0
                                 }
 429  
                 }else{
 430  7
                     fieldValue = dataProvider.getValue(caseField.getName());
 431  
                 }
 432  
         }
 433  24
         DataType fieldDataType = (null != caseField ? caseField.getDataType():null);
 434  
 
 435  
         // If fieldValue is null then skip Case check
 436  24
         if(null == fieldValue) {
 437  11
             return null;
 438  
         }
 439  
 
 440  
         // Extract value for field Key
 441  13
         for (WhenConstraint wc : caseConstraint.getWhenConstraint()) {
 442  
 
 443  19
                 if(hasText(wc.getValuePath())){
 444  0
                         Object whenValue = null;
 445  0
                         if(wc.getValuePath().startsWith("/")){
 446  
                                 try {
 447  0
                                         whenValue = PropertyUtils.getNestedProperty(rootData, wc.getValuePath().substring(1));
 448  0
                                         } catch (IllegalAccessException e) {
 449  0
                                         } catch (InvocationTargetException e) {
 450  0
                                         } catch (NoSuchMethodException e) {
 451  0
                                         }
 452  
                         }else{
 453  0
                                 whenValue = dataProvider.getValue(wc.getValuePath());
 454  
                         }
 455  0
                         if (ValidatorUtils.compareValues(fieldValue, whenValue, fieldDataType, operator, caseConstraint.isCaseSensitive(), dateParser) && null != wc.getConstraint()) {                    
 456  0
                                 Constraint constraint = wc.getConstraint();
 457  0
                                 if (constraint.getCaseConstraint() != null){
 458  0
                                         return processCaseConstraint(valResults, constraint.getCaseConstraint(), objStructure, value, dataProvider, elementStack, rootData, rootObjStructure);
 459  
                                 } else {
 460  0
                                         processCrossFieldWarning(valResults, caseConstraint, constraint, value, constraint.getErrorLevel());
 461  0
                                         return constraint;
 462  
                                 }
 463  
                 }
 464  0
                 }else{
 465  19
                         List<Object> whenValueList = wc.getValues();
 466  
             
 467  19
                     for (Object whenValue : whenValueList) {
 468  21
                         if (ValidatorUtils.compareValues(fieldValue, whenValue, fieldDataType, operator, caseConstraint.isCaseSensitive(), dateParser) && null != wc.getConstraint()) {
 469  3
                                         Constraint constraint = wc.getConstraint();
 470  3
                                         if (constraint.getCaseConstraint() != null){
 471  2
                                                 return processCaseConstraint(valResults, constraint.getCaseConstraint(), objStructure, value, dataProvider, elementStack, rootData, rootObjStructure);
 472  
                                         } else {
 473  1
                                                 processCrossFieldWarning(valResults, caseConstraint, constraint, value, constraint.getErrorLevel());                                                
 474  1
                                                 return constraint;
 475  
                                         }
 476  
                         }
 477  
                     }
 478  16
                 }
 479  
         }
 480  
 
 481  10
         return null;
 482  
     }
 483  
 
 484  
     public ValidationResultInfo processValidCharConstraint(String element, ValidCharsConstraint vcConstraint, ConstraintDataProvider dataProvider, Object value) {
 485  
 
 486  21
         ValidationResultInfo val = null;
 487  
 
 488  21
         StringBuilder fieldValue = new StringBuilder();
 489  21
         String validChars = vcConstraint.getValue();
 490  
 
 491  21
         fieldValue.append(ValidatorUtils.getString(value));
 492  
 
 493  21
         int typIdx = validChars.indexOf(":");
 494  21
         String processorType = "regex";
 495  21
         if (-1 == typIdx) {
 496  0
             validChars = "[" + validChars + "]*";
 497  
         } else {
 498  21
             processorType = validChars.substring(0, typIdx);
 499  21
             validChars = validChars.substring(typIdx + 1);
 500  
         }
 501  
 
 502  21
         if ("regex".equalsIgnoreCase(processorType)) {
 503  21
             if (fieldValue == null || !fieldValue.toString().matches(validChars)) {
 504  3
                     val = new ValidationResultInfo(element, fieldValue);
 505  3
                 if(vcConstraint.getLabelKey()!=null){
 506  0
                         val.setError(getMessage(vcConstraint.getLabelKey()));
 507  
                 }else{
 508  3
                         val.setError(getMessage("validation.validCharsFailed"));
 509  
                 }
 510  
             }
 511  
         }
 512  
 
 513  21
         return val;
 514  
     }
 515  
 
 516  
     /**
 517  
      * Computes if all the filed required in the occurs clause are between the min and max
 518  
      *
 519  
      * @param valResults
 520  
      * @param constraint
 521  
      * @param field
 522  
      * @param type
 523  
      * @param state
 524  
      * @param objStructure
 525  
      * @param dataProvider
 526  
      * @return
 527  
      */
 528  
     protected ValidationResultInfo processOccursConstraint(String element, MustOccurConstraint constraint, FieldDefinition field, ObjectStructureDefinition objStructure, ConstraintDataProvider dataProvider) {
 529  
 
 530  0
         boolean result = false;
 531  0
         int trueCount = 0;
 532  
 
 533  0
         ValidationResultInfo val = null;
 534  
 
 535  0
         for (RequiredConstraint rc : constraint.getRequiredFields()) {
 536  0
             trueCount += (processRequireConstraint("", rc, field, objStructure, dataProvider) != null) ? 1 : 0;
 537  
         }
 538  
 
 539  0
         for (MustOccurConstraint oc : constraint.getOccurs()) {
 540  0
             trueCount += (processOccursConstraint("", oc, field, objStructure, dataProvider) != null) ? 1 : 0;
 541  
         }
 542  
 
 543  0
         result = (trueCount >= constraint.getMin() && trueCount <= constraint.getMax()) ? true : false;
 544  
 
 545  0
         if (!result) {
 546  
          // TODO: figure out what data should go here instead of null
 547  0
             val = new ValidationResultInfo(element, null);
 548  0
             val.setMessage(getMessage("validation.occurs"));
 549  0
             val.setLevel(constraint.getErrorLevel());
 550  
         }
 551  
 
 552  0
         return val;
 553  
     }
 554  
 
 555  
     // TODO: Implement lookup constraint
 556  
     protected void processLookupConstraint(List<ValidationResultInfo> valResults, LookupConstraint lookupConstraint, FieldDefinition field, Stack<String> elementStack, ConstraintDataProvider dataProvider, ObjectStructureDefinition objStructure, Object rootData, ObjectStructureDefinition rootObjStructure, Object value) {
 557  4
         if (lookupConstraint == null) {
 558  0
             return;
 559  
         }
 560  
 
 561  
         // Create search params based on the param mapping
 562  4
         List<SearchParam> params = new ArrayList<SearchParam>();
 563  
 
 564  4
         for (CommonLookupParam paramMapping : lookupConstraint.getParams()) {
 565  
                 //Skip params that are the search param id key
 566  8
             if(lookupConstraint.getSearchParamIdKey()!=null&&lookupConstraint.getSearchParamIdKey().equals(paramMapping.getKey())){
 567  0
                     continue;
 568  
             }
 569  
                 
 570  8
             SearchParam param = new SearchParam();
 571  
 
 572  8
             param.setKey(paramMapping.getKey());
 573  
 
 574  
             // If the value of the search param comes form another field then get it
 575  8
             if (paramMapping.getFieldPath() != null && !paramMapping.getFieldPath().isEmpty()) {
 576  4
                 FieldDefinition lookupField = null;
 577  4
                     boolean absolutePath = false;
 578  4
                 if(hasText(paramMapping.getFieldPath())){
 579  4
                         if(paramMapping.getFieldPath().startsWith("/")){
 580  0
                                 absolutePath = true;
 581  0
                                 lookupField = ValidatorUtils.getField(paramMapping.getFieldPath().substring(1), rootObjStructure);
 582  
                         }else{
 583  4
                                 lookupField = ValidatorUtils.getField(paramMapping.getFieldPath(), objStructure); 
 584  
                         }
 585  
                 }
 586  4
                 Object fieldValue = null;
 587  4
                 if(lookupField!=null){
 588  4
                         if(absolutePath){
 589  
                                 try {
 590  0
                                         if(lookupField.isDynamic()){
 591  
                                                 //Pull the value from the dynamic attribute map
 592  
                                                 //Until then, this will only work for root level properties
 593  0
                                                 Map<String,String> attributes = (Map<String,String>) PropertyUtils.getNestedProperty(rootData, "attributes");
 594  0
                                                 if(attributes!=null){
 595  0
                                                         fieldValue = attributes.get(paramMapping.getFieldPath().substring(1));
 596  
                                                 }
 597  0
                                         }else{
 598  0
                                                 fieldValue = PropertyUtils.getNestedProperty(rootData, paramMapping.getFieldPath().substring(1));
 599  
                                         }
 600  0
                                         } catch (IllegalAccessException e) {
 601  0
                                         } catch (InvocationTargetException e) {
 602  0
                                         } catch (NoSuchMethodException e) {
 603  0
                                         }
 604  
                         }else{
 605  4
                             fieldValue = dataProvider.getValue(lookupField.getName());
 606  
                         }
 607  
                 }else{
 608  0
                         fieldValue = dataProvider.getValue(paramMapping.getFieldPath());
 609  
                 }
 610  
                 
 611  4
                 if (fieldValue instanceof String) {
 612  4
                     param.setValue((String) fieldValue);
 613  0
                 } else if (fieldValue instanceof List<?>) {
 614  0
                     param.setValue((List<String>) fieldValue);
 615  
                 }
 616  4
             } else if (paramMapping.getDefaultValueString() != null) {
 617  4
                 param.setValue(paramMapping.getDefaultValueString());
 618  
             } else {
 619  0
                 param.setValue(paramMapping.getDefaultValueList());
 620  
             }
 621  8
             params.add(param);
 622  8
         }
 623  
 
 624  4
         if(lookupConstraint.getSearchParamIdKey()!=null){
 625  0
                 SearchParam param = new SearchParam();
 626  0
                 param.setKey(lookupConstraint.getSearchParamIdKey());
 627  0
             if (value instanceof String) {
 628  0
                 param.setValue((String) value);
 629  0
             } else if (value instanceof List<?>) {
 630  0
                 param.setValue((List<String>) value);
 631  
             }
 632  0
                 params.add(param);
 633  
         }
 634  
         
 635  4
         SearchRequest searchRequest = new SearchRequest();
 636  4
         searchRequest.setMaxResults(1);
 637  4
         searchRequest.setStartAt(0);
 638  4
         searchRequest.setNeededTotalResults(false);
 639  4
         searchRequest.setSearchKey(lookupConstraint.getSearchTypeId());
 640  4
         searchRequest.setParams(params);
 641  
 
 642  4
         SearchResult searchResult = null;
 643  
         try {
 644  4
             searchResult = searchDispatcher.dispatchSearch(searchRequest);
 645  2
         } catch (Exception e) {
 646  2
             LOG.info("Error calling Search", e);
 647  2
         }
 648  
         //If there are no search results then make a validation result
 649  4
         if (searchResult == null || searchResult.getRows() == null || searchResult.getRows().isEmpty()) {
 650  4
             ValidationResultInfo val = new ValidationResultInfo(getElementXpath(elementStack) + "/" + field.getName(), value);
 651  4
             val.setLevel(lookupConstraint.getErrorLevel());
 652  4
             val.setMessage(getMessage("validation.lookup"));
 653  4
             valResults.add(val);
 654  4
                 processCrossFieldWarning(valResults, lookupConstraint, lookupConstraint.getErrorLevel());
 655  
         }
 656  4
     }
 657  
 
 658  
     protected void processBaseConstraints(List<ValidationResultInfo> valResults, Constraint constraint, FieldDefinition field, Object value, Stack<String> elementStack) {
 659  78
             DataType dataType = field.getDataType();
 660  78
             String name = field.getName();
 661  
 
 662  78
             if (value == null || "".equals(value.toString().trim())) {
 663  29
             if (constraint.getMinOccurs() != null && constraint.getMinOccurs() > 0) {
 664  7
                 ValidationResultInfo val = new ValidationResultInfo(getElementXpath(elementStack) + "/" + name, value);
 665  7
                 if(constraint.getLabelKey()!=null){
 666  0
                         val.setError(getMessage(constraint.getLabelKey()));
 667  
                 } else {
 668  7
                         val.setMessage(getMessage("validation.required"));
 669  
                 }
 670  7
                 val.setLevel(constraint.getErrorLevel());
 671  7
                 valResults.add(val);
 672  
             }
 673  29
             return;
 674  
         }
 675  
 
 676  49
         String elementPath = getElementXpath(elementStack) + "/" + name;
 677  
 
 678  49
         if (DataType.STRING.equals(dataType)) {
 679  41
             validateString(value, constraint, elementPath, valResults);
 680  8
         } else if (DataType.INTEGER.equals(dataType)) {
 681  0
             validateInteger(value, constraint, elementPath, valResults);
 682  8
         } else if (DataType.LONG.equals(dataType)) {
 683  0
             validateLong(value, constraint, elementPath, valResults);
 684  8
         } else if (DataType.DOUBLE.equals(dataType)) {
 685  1
             validateDouble(value, constraint, elementPath, valResults);
 686  7
         } else if (DataType.FLOAT.equals(dataType)) {
 687  0
             validateFloat(value, constraint, elementPath, valResults);
 688  7
         } else if (DataType.BOOLEAN.equals(dataType)) {
 689  0
             validateBoolean(value, constraint, elementPath, valResults);
 690  7
         } else if (DataType.DATE.equals(dataType)) {
 691  7
             validateDate(value, constraint, elementPath, valResults, dateParser);
 692  
         }
 693  49
     }
 694  
     
 695  
     /**
 696  
      * This adds a warning on the related field when a processed case-constraint results in a warning
 697  
      * 
 698  
      * @param valResults
 699  
      * @param crossConstraint
 700  
      * @param field
 701  
      */
 702  
     protected void processCrossFieldWarning(List<ValidationResultInfo> valResults, CaseConstraint crossConstraint, Constraint constraint, Object value, ErrorLevel errorLevel){
 703  1
             if ((ErrorLevel.WARN == errorLevel || ErrorLevel.ERROR == errorLevel) && (value == null || "".equals(value.toString().trim()))) {
 704  1
             if (constraint.getMinOccurs() != null && constraint.getMinOccurs() > 0) {
 705  
 
 706  1
                     String crossFieldPath = crossConstraint.getFieldPath();
 707  1
                     String crossFieldMessageId = crossConstraint.getFieldPathMessageId() == null ? 
 708  
                                     "validation.required":crossConstraint.getFieldPathMessageId();            
 709  1
                     addCrossFieldWarning(valResults, crossFieldPath, getMessage(crossFieldMessageId), errorLevel);
 710  
             }
 711  
             }
 712  1
     }
 713  
 
 714  
     /**
 715  
      * This adds a warning on the related field when a processed case-constraint results in a warning
 716  
      * 
 717  
      * @param valResults
 718  
      * @param requiredConstraint
 719  
      * @param field
 720  
      */
 721  
     protected void processCrossFieldWarning(List<ValidationResultInfo> valResults, RequiredConstraint requiredConstraint, ErrorLevel errorLevel, String field){
 722  1
             if ((ErrorLevel.WARN == errorLevel || ErrorLevel.ERROR == errorLevel) && requiredConstraint != null){
 723  1
             String crossFieldPath = requiredConstraint.getFieldPath();
 724  1
             String crossFieldMessageId = requiredConstraint.getFieldPathMessageId() == null ? 
 725  
                             "validation.required":requiredConstraint.getFieldPathMessageId();
 726  1
             addCrossFieldWarning(valResults, crossFieldPath, getMessage(crossFieldMessageId), errorLevel);
 727  
             }
 728  1
     }
 729  
 
 730  
     /**
 731  
      * This adds a warning on the related field when a processed lookup-constraint results in a warning
 732  
      * 
 733  
      * @param valResults
 734  
      * @param lookupConstraint
 735  
      */
 736  
     protected void processCrossFieldWarning(List<ValidationResultInfo> valResults, LookupConstraint lookupConstraint, ErrorLevel errorLevel){
 737  4
             if ((ErrorLevel.WARN == errorLevel || ErrorLevel.ERROR == errorLevel) && lookupConstraint != null){
 738  4
                     for(CommonLookupParam param:lookupConstraint.getParams()){
 739  8
                             if(param.getFieldPath()!=null && !param.getFieldPath().isEmpty()){
 740  4
                             String crossFieldPath = param.getFieldPath();
 741  4
                             String crossFieldMessageId = param.getFieldPathMessageId() == null ? 
 742  
                                             "validation.lookup.cause":param.getFieldPathMessageId();
 743  4
                             addCrossFieldWarning(valResults, crossFieldPath, getMessage(crossFieldMessageId), errorLevel);
 744  8
                             }
 745  
                     }
 746  
             }
 747  4
     }
 748  
     
 749  
     protected void addCrossFieldWarning(List<ValidationResultInfo> valResults, String crossFieldPath, String message, ErrorLevel errorLevel){
 750  
             //Check to see if the exact same validation message already exists on referenced field
 751  6
             boolean warnAlreadyExists = false;
 752  6
             for (ValidationResultInfo vr:valResults){
 753  9
                     if (vr.getElement().equals(crossFieldPath) && vr.getMessage().equals(message)){
 754  0
                             warnAlreadyExists = true;
 755  
                     }
 756  
             }
 757  
             
 758  
             //Only add this warning, if it hasn't been already added
 759  6
             if (!warnAlreadyExists){
 760  6
                     ValidationResultInfo val = new ValidationResultInfo(crossFieldPath, null);
 761  6
                         val.setMessage(message);
 762  6
                 val.setLevel(errorLevel);
 763  6
                 valResults.add(val);
 764  
             }
 765  6
     }
 766  
     
 767  
     protected void validateBoolean(Object value, Constraint constraint, String element, List<ValidationResultInfo> results) {
 768  0
         if (!(value instanceof Boolean)) {
 769  
             try {
 770  0
                 Boolean.valueOf(value.toString());
 771  0
             } catch (Exception e) {
 772  0
                 ValidationResultInfo val = new ValidationResultInfo(element, value);
 773  0
                 val.setError(getMessage("validation.mustBeBoolean"));
 774  0
                 results.add(val);
 775  0
             }
 776  
         }
 777  0
     }
 778  
 
 779  
     protected void validateDouble(Object value, Constraint constraint, String element, List<ValidationResultInfo> results) {
 780  1
         Double v = null;
 781  
 
 782  1
         ValidationResultInfo val = new ValidationResultInfo(element, value);
 783  
 
 784  1
         if (value instanceof Number) {
 785  1
             v = ((Number) value).doubleValue();
 786  
         } else {
 787  
             try {
 788  0
                 v = Double.valueOf(value.toString());
 789  0
             } catch (Exception e) {
 790  0
                 val.setError(getMessage("validation.mustBeDouble"));
 791  0
             }
 792  
         }
 793  
 
 794  1
         if (val.isOk()) {
 795  1
             Double maxValue = ValidatorUtils.getDouble(constraint.getInclusiveMax());
 796  1
             Double minValue = ValidatorUtils.getDouble(constraint.getExclusiveMin());
 797  
 
 798  1
             if (maxValue != null && minValue != null) {
 799  
                 // validate range
 800  1
                 if (v > maxValue || v < minValue) {
 801  1
                     val.setError(MessageUtils.interpolate(getMessage("validation.outOfRange"), toMap(constraint)));
 802  
                 }
 803  0
             } else if (maxValue != null) {
 804  0
                 if (v > maxValue) {
 805  0
                     val.setError(MessageUtils.interpolate(getMessage("validation.maxValueFailed"), toMap(constraint)));
 806  
                 }
 807  0
             } else if (minValue != null) {
 808  0
                 if (v < minValue) {
 809  0
                     val.setError(MessageUtils.interpolate(getMessage("validation.minValueFailed"), toMap(constraint)));
 810  
                 }
 811  
             }
 812  
         }
 813  
 
 814  1
         if (!val.isOk()) {
 815  1
             results.add(val);
 816  
         }
 817  1
     }
 818  
 
 819  
     protected void validateFloat(Object value, Constraint constraint, String element, List<ValidationResultInfo> results) {
 820  0
         Float v = null;
 821  
 
 822  0
         ValidationResultInfo val = new ValidationResultInfo(element, value);
 823  0
         if (value instanceof Number) {
 824  0
             v = ((Number) value).floatValue();
 825  
         } else {
 826  
             try {
 827  0
                 v = Float.valueOf(value.toString());
 828  0
             } catch (Exception e) {
 829  0
                 val.setError(getMessage("validation.mustBeFloat"));
 830  0
             }
 831  
         }
 832  
 
 833  0
         if (val.isOk()) {
 834  0
             Float maxValue = ValidatorUtils.getFloat(constraint.getInclusiveMax());
 835  0
             Float minValue = ValidatorUtils.getFloat(constraint.getExclusiveMin());
 836  
 
 837  0
             if (maxValue != null && minValue != null) {
 838  
                 // validate range
 839  0
                 if (v > maxValue || v < minValue) {
 840  0
                     val.setError(MessageUtils.interpolate(getMessage("validation.outOfRange"), toMap(constraint)));
 841  
                 }
 842  0
             } else if (maxValue != null) {
 843  0
                 if (v > maxValue) {
 844  0
                     val.setError(MessageUtils.interpolate(getMessage("validation.maxValueFailed"), toMap(constraint)));
 845  
                 }
 846  0
             } else if (minValue != null) {
 847  0
                 if (v < minValue) {
 848  0
                     val.setError(MessageUtils.interpolate(getMessage("validation.minValueFailed"), toMap(constraint)));
 849  
                 }
 850  
             }
 851  
         }
 852  
 
 853  0
         if (!val.isOk()) {
 854  0
             results.add(val);
 855  
         }
 856  0
     }
 857  
 
 858  
     protected void validateLong(Object value, Constraint constraint, String element, List<ValidationResultInfo> results) {
 859  0
         Long v = null;
 860  
 
 861  0
         ValidationResultInfo val = new ValidationResultInfo(element, value);
 862  0
         if (value instanceof Number) {
 863  0
             v = ((Number) value).longValue();
 864  
         } else {
 865  
             try {
 866  0
                 v = Long.valueOf(value.toString());
 867  0
             } catch (Exception e) {
 868  0
                 val.setError(getMessage("validation.mustBeLong"));
 869  0
             }
 870  
         }
 871  
 
 872  0
         if (val.isOk()) {
 873  0
             Long maxValue = ValidatorUtils.getLong(constraint.getInclusiveMax());
 874  0
             Long minValue = ValidatorUtils.getLong(constraint.getExclusiveMin());
 875  
 
 876  0
             if (maxValue != null && minValue != null) {
 877  
                 // validate range
 878  0
                 if (v > maxValue || v < minValue) {
 879  0
                     val.setError(MessageUtils.interpolate(getMessage("validation.outOfRange"), toMap(constraint)));
 880  
                 }
 881  0
             } else if (maxValue != null) {
 882  0
                 if (v > maxValue) {
 883  0
                     val.setError(MessageUtils.interpolate(getMessage("validation.maxValueFailed"), toMap(constraint)));
 884  
                 }
 885  0
             } else if (minValue != null) {
 886  0
                 if (v < minValue) {
 887  0
                     val.setError(MessageUtils.interpolate(getMessage("validation.minValueFailed"), toMap(constraint)));
 888  
                 }
 889  
             }
 890  
         }
 891  
 
 892  0
         if (!val.isOk()) {
 893  0
             results.add(val);
 894  
         }
 895  
 
 896  0
     }
 897  
 
 898  
     protected void validateInteger(Object value, Constraint constraint, String element, List<ValidationResultInfo> results) {
 899  0
         Integer v = null;
 900  
 
 901  0
         ValidationResultInfo val = new ValidationResultInfo(element, value);
 902  
 
 903  0
         if (value instanceof Number) {
 904  0
             v = ((Number) value).intValue();
 905  
         } else {
 906  
             try {
 907  0
                 v = Integer.valueOf(value.toString());
 908  0
             } catch (Exception e) {
 909  0
                 val.setError(getMessage("validation.mustBeInteger"));
 910  0
             }
 911  
         }
 912  
 
 913  0
         if (val.isOk()) {
 914  0
             Integer maxValue = ValidatorUtils.getInteger(constraint.getInclusiveMax());
 915  0
             Integer minValue = ValidatorUtils.getInteger(constraint.getExclusiveMin());
 916  
 
 917  0
             if (maxValue != null && minValue != null) {
 918  
                 // validate range
 919  0
                 if (v > maxValue || v < minValue) {
 920  0
                     val.setError(MessageUtils.interpolate(getMessage("validation.outOfRange"), toMap(constraint)));
 921  
                 }
 922  0
             } else if (maxValue != null) {
 923  0
                 if (v > maxValue) {
 924  0
                     val.setError(MessageUtils.interpolate(getMessage("validation.maxValueFailed"), toMap(constraint)));
 925  
                 }
 926  0
             } else if (minValue != null) {
 927  0
                 if (v < minValue) {
 928  0
                     val.setError(MessageUtils.interpolate(getMessage("validation.minValueFailed"), toMap(constraint)));
 929  
                 }
 930  
             }
 931  
         }
 932  
 
 933  0
         if (!val.isOk()) {
 934  0
             results.add(val);
 935  
         }
 936  0
     }
 937  
 
 938  
     protected void validateDate(Object value, Constraint constraint, String element, List<ValidationResultInfo> results, DateParser dateParser) {
 939  7
         ValidationResultInfo val = new ValidationResultInfo(element, value);
 940  
 
 941  7
         Date v = null;
 942  
 
 943  7
         if (value instanceof Date) {
 944  7
             v = (Date) value;
 945  
         } else {
 946  
             try {
 947  0
                 v = dateParser.parseDate(value.toString());
 948  0
             } catch (Exception e) {
 949  0
                 val.setError(getMessage("validation.mustBeDate"));
 950  0
             }
 951  
         }
 952  
 
 953  7
         if (val.isOk()) {
 954  7
             Date maxValue = ValidatorUtils.getDate(constraint.getInclusiveMax(), dateParser);
 955  7
             Date minValue = ValidatorUtils.getDate(constraint.getExclusiveMin(), dateParser);
 956  
 
 957  7
             if (maxValue != null && minValue != null) {
 958  
                 // validate range
 959  0
                 if (v.getTime() > maxValue.getTime() || v.getTime() < minValue.getTime()) {
 960  0
                     val.setError(MessageUtils.interpolate(getMessage("validation.outOfRange"), toMap(constraint)));
 961  
                 }
 962  7
             } else if (maxValue != null) {
 963  0
                 if (v.getTime() > maxValue.getTime()) {
 964  0
                     val.setError(MessageUtils.interpolate(getMessage("validation.maxValueFailed"), toMap(constraint)));
 965  
                 }
 966  7
             } else if (minValue != null) {
 967  7
                 if (v.getTime() < minValue.getTime()) {
 968  1
                     val.setError(MessageUtils.interpolate(getMessage("validation.minValueFailed"), toMap(constraint)));
 969  
                 }
 970  
             }
 971  
         }
 972  
 
 973  7
         if (!val.isOk()) {
 974  1
             results.add(val);
 975  
         }
 976  7
     }
 977  
 
 978  
     protected void validateString(Object value, Constraint constraint, String element, List<ValidationResultInfo> results) {
 979  
 
 980  41
         if (value == null) {
 981  0
             value = "";
 982  
         }
 983  41
         String s = value.toString().trim();
 984  
 
 985  41
         ValidationResultInfo val = new ValidationResultInfo(element, value);
 986  
 
 987  41
         Integer maxLength = tryParse(constraint.getMaxLength());
 988  41
         if (maxLength != null && constraint.getMinLength() != null && constraint.getMinLength() > 0) {
 989  39
             if (s.length() > maxLength || s.length() < constraint.getMinLength()) {
 990  1
                 val.setError(MessageUtils.interpolate(getMessage("validation.lengthOutOfRange"), toMap(constraint)));
 991  
             }
 992  2
         } else if (maxLength != null) {
 993  1
             if (s.length() > Integer.parseInt(constraint.getMaxLength())) {
 994  1
                 val.setError(MessageUtils.interpolate(getMessage("validation.maxLengthFailed"), toMap(constraint)));
 995  
             }
 996  1
         } else if (constraint.getMinLength() != null && constraint.getMinLength() > 0) {
 997  1
             if (s.length() < constraint.getMinLength()) {
 998  1
                 val.setError(MessageUtils.interpolate(getMessage("validation.minLengthFailed"), toMap(constraint)));
 999  
             }
 1000  
         }
 1001  
 
 1002  41
         if (!val.isOk()) {
 1003  3
             results.add(val);
 1004  
         }
 1005  41
     }
 1006  
 
 1007  
     protected String getMessage(String messageId) {
 1008  26
         if (null == messageService) {
 1009  26
             return messageId;
 1010  
         }
 1011  
 
 1012  0
         Message msg = messageService.getMessage(messageLocaleKey, messageGroupKey, messageId);
 1013  
 
 1014  0
         return msg.getValue();
 1015  
     }
 1016  
 
 1017  
     protected String getElementXpath(Stack<String> elementStack) {
 1018  114
         StringBuilder xPath = new StringBuilder();
 1019  114
         Iterator<String> itr = elementStack.iterator();
 1020  247
         while (itr.hasNext()) {
 1021  133
             xPath.append(itr.next());
 1022  133
             if(itr.hasNext()){
 1023  64
                     xPath.append("/");
 1024  
             }
 1025  
         }
 1026  
 
 1027  114
         return xPath.toString();
 1028  
     }
 1029  
 
 1030  
     /*
 1031  
      * Homemade has text so we dont need outside libs.
 1032  
      */
 1033  
     protected boolean hasText(String string) {
 1034  
 
 1035  76
         if (string == null || string.length() < 1) {
 1036  20
             return false;
 1037  
         }
 1038  56
         int stringLength = string.length();
 1039  
 
 1040  56
         for (int i = 0; i < stringLength; i++) {
 1041  56
             char currentChar = string.charAt(i);
 1042  56
             if (' ' != currentChar || '\t' != currentChar || '\n' != currentChar) {
 1043  56
                 return true;
 1044  
             }
 1045  
         }
 1046  
 
 1047  0
         return false;
 1048  
     }
 1049  
 
 1050  
     protected Map<String, Object> toMap(Constraint c) {
 1051  5
         Map<String, Object> result = new HashMap<String, Object>();
 1052  5
         result.put("minOccurs", c.getMinOccurs());
 1053  5
         result.put("maxOccurs", c.getMaxOccurs());
 1054  5
         result.put("minLength", c.getMinLength());
 1055  5
         result.put("maxLength", c.getMaxLength());
 1056  5
         result.put("minValue", c.getExclusiveMin());
 1057  5
         result.put("maxValue", c.getInclusiveMax());
 1058  
         // result.put("dataType", c.getDataType());
 1059  
 
 1060  5
         return result;
 1061  
     }
 1062  
 
 1063  
     public SearchDispatcher getSearchDispatcher() {
 1064  0
         return searchDispatcher;
 1065  
     }
 1066  
 
 1067  
     public void setSearchDispatcher(SearchDispatcher searchDispatcher) {
 1068  1
         this.searchDispatcher = searchDispatcher;
 1069  1
     }
 1070  
 
 1071  
         @Override
 1072  
         public List<ValidationResultInfo> validateObject(FieldDefinition field,
 1073  
                         Object o, ObjectStructureDefinition objStructure,Stack<String> elementStack) {
 1074  0
                 return null;
 1075  
         }
 1076  
 }