Coverage Report - org.kuali.student.common.validator.DefaultValidatorImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
DefaultValidatorImpl
54%
246/451
39%
141/356
6.667
 
 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.BeanUtils;
 22  
 import org.apache.commons.beanutils.PropertyUtils;
 23  
 import org.apache.log4j.Logger;
 24  
 import org.kuali.student.common.util.MessageUtils;
 25  
 import org.kuali.student.core.dictionary.dto.CaseConstraint;
 26  
 import org.kuali.student.core.dictionary.dto.CommonLookupParam;
 27  
 import org.kuali.student.core.dictionary.dto.Constraint;
 28  
 import org.kuali.student.core.dictionary.dto.DataType;
 29  
 import org.kuali.student.core.dictionary.dto.FieldDefinition;
 30  
 import org.kuali.student.core.dictionary.dto.LookupConstraint;
 31  
 import org.kuali.student.core.dictionary.dto.MustOccurConstraint;
 32  
 import org.kuali.student.core.dictionary.dto.ObjectStructureDefinition;
 33  
 import org.kuali.student.core.dictionary.dto.RequiredConstraint;
 34  
 import org.kuali.student.core.dictionary.dto.ValidCharsConstraint;
 35  
 import org.kuali.student.core.dictionary.dto.WhenConstraint;
 36  
 import org.kuali.student.core.messages.dto.Message;
 37  
 import org.kuali.student.core.messages.service.MessageService;
 38  
 import org.kuali.student.core.search.dto.SearchParam;
 39  
 import org.kuali.student.core.search.dto.SearchRequest;
 40  
 import org.kuali.student.core.search.dto.SearchResult;
 41  
 import org.kuali.student.core.search.service.SearchDispatcher;
 42  
 import org.kuali.student.core.validation.dto.ValidationResultInfo;
 43  
 
 44  8
 public class DefaultValidatorImpl extends BaseAbstractValidator {
 45  1
     final static Logger LOG = Logger.getLogger(DefaultValidatorImpl.class);
 46  
 
 47  8
     private MessageService messageService = null;
 48  
 
 49  
     private SearchDispatcher searchDispatcher;
 50  
 
 51  8
     private String messageLocaleKey = "en";
 52  
 
 53  8
     private String messageGroupKey = "validation";
 54  
 
 55  8
     private DateParser dateParser = new ServerDateParser();
 56  
 
 57  8
     private boolean serverSide = true;
 58  
 
 59  
     public MessageService getMessageService() {
 60  0
         return messageService;
 61  
     }
 62  
 
 63  
     public void setMessageService(MessageService messageService) {
 64  8
         this.messageService = messageService;
 65  8
     }
 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  8
         this.dateParser = dateParser;
 85  8
     }
 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  10
         Stack<String> elementStack = new Stack<String>();
 119  
 
 120  10
         return validateObject(data, objStructure, elementStack, data, objStructure, true);
 121  
     }
 122  
 
 123  
     private List<ValidationResultInfo> validateObject(Object data, ObjectStructureDefinition objStructure, Stack<String> elementStack,  Object rootData, ObjectStructureDefinition rootObjStructure, boolean isRoot) {
 124  
 
 125  13
        List<ValidationResultInfo> results = new ArrayList<ValidationResultInfo>();
 126  
 
 127  13
         ConstraintDataProvider dataProvider = new BeanConstraintDataProvider();
 128  13
         dataProvider.initialize(data);
 129  
 
 130  
         // Push object structure to the top of the stack
 131  13
         StringBuilder objXPathElement = new StringBuilder(dataProvider.getPath());
 132  
 
 133  13
         if(!isRoot && !objXPathElement.toString().isEmpty()){
 134  0
                 elementStack.push(objXPathElement.toString());
 135  
         }
 136  
 
 137  
         /*
 138  
          * Do nothing if the object to be validated is not type/state or if the objectstructure with constraints is not
 139  
          * provided
 140  
          */
 141  13
         if (null == objStructure) {
 142  0
             return results;
 143  
         }
 144  
 
 145  13
         for (FieldDefinition f : objStructure.getAttributes()) {
 146  58
             List<ValidationResultInfo> l = validateField(f, objStructure, dataProvider, elementStack, rootData, rootObjStructure);
 147  
 
 148  58
             results.addAll(l);
 149  
 
 150  
             // Use Custom Validators
 151  58
             if (f.getCustomValidatorClass() != null || f.isServerSide() && serverSide) {
 152  0
                     Validator customValidator = validatorFactory.getValidator(f.getCustomValidatorClass());
 153  0
                     if(customValidator==null){
 154  0
                             throw new RuntimeException("Custom Validator "+f.getCustomValidatorClass()+" was not configured in this context");
 155  
                     }
 156  0
                     l = customValidator.validateObject(f,data, objStructure,elementStack);
 157  0
                     results.addAll(l);
 158  
             }
 159  58
         }
 160  13
         if(!isRoot && !objXPathElement.toString().isEmpty()){
 161  0
                 elementStack.pop();
 162  
         }
 163  
 
 164  
         /* All Field validations are returned right now */
 165  
         // List<ValidationResultInfo> resultsBuffer = new
 166  
         // ArrayList<ValidationResultInfo>();
 167  
         // for (ValidationResultContainer vc : results) {
 168  
         // if (skipFields.contains(vc.getElement()) == false) {
 169  
         // resultsBuffer.add(vc);
 170  
         // }
 171  
         // }
 172  
         // results = resultsBuffer;
 173  13
         return results;
 174  
     }
 175  
 
 176  
     public List<ValidationResultInfo> validateField(FieldDefinition field, ObjectStructureDefinition objStruct, ConstraintDataProvider dataProvider, Stack<String> elementStack,  Object rootData, ObjectStructureDefinition rootObjectStructure) {
 177  
 
 178  58
         Object value = dataProvider.getValue(field.getName());
 179  58
         List<ValidationResultInfo> results = new ArrayList<ValidationResultInfo>();
 180  
 
 181  
         // Handle null values in field
 182  58
         if (value == null || "".equals(value.toString().trim())) {
 183  25
             processConstraint(results, field, objStruct, value, dataProvider, elementStack, rootData, rootObjectStructure);
 184  25
             return results;
 185  
         }
 186  
 
 187  
         /*
 188  
          * For complex object structures only the following constraints apply 1. TypeStateCase 2. MinOccurs 3. MaxOccurs
 189  
          */
 190  33
         if (DataType.COMPLEX.equals(field.getDataType())) {
 191  3
             ObjectStructureDefinition nestedObjStruct = null;
 192  
 
 193  3
             if (null != field.getDataObjectStructure()) {
 194  3
                 nestedObjStruct = field.getDataObjectStructure();
 195  
             }
 196  
 
 197  3
             elementStack.push(field.getName());
 198  
 //                   beanPathStack.push(field.isDynamic()?"attributes("+field.getName()+")":field.getName());
 199  
 
 200  3
             if (value instanceof Collection) {
 201  
 
 202  3
                 String xPathForCollection = getElementXpath(elementStack) + "/*";
 203  
 
 204  3
                 int i=0;
 205  3
                 for (Object o : (Collection<?>) value) {
 206  3
                         elementStack.push(Integer.toString(i));
 207  
 //                        beanPathStack.push(!beanPathStack.isEmpty()?beanPathStack.pop():""+"["+i+"]");
 208  3
                     processNestedObjectStructure(results, o, nestedObjStruct, field, elementStack, rootData, rootObjectStructure);
 209  
 //                    beanPathStack.pop();
 210  
 //                    beanPathStack.push(field.isDynamic()?"attributes("+field.getName()+")":field.getName());
 211  3
                     elementStack.pop();
 212  3
                     i++;
 213  
                 }
 214  3
                 if (field.getMinOccurs() != null && field.getMinOccurs() > ((Collection<?>) value).size()) {
 215  0
                     ValidationResultInfo valRes = new ValidationResultInfo(xPathForCollection, value);
 216  0
                     valRes.setError(MessageUtils.interpolate(getMessage("validation.minOccurs"), toMap(field)));
 217  0
                     results.add(valRes);
 218  
                 }
 219  
 
 220  3
                 Integer maxOccurs = tryParse(field.getMaxOccurs());
 221  3
                 if (maxOccurs != null && maxOccurs < ((Collection<?>) value).size()) {
 222  0
                     ValidationResultInfo valRes = new ValidationResultInfo(xPathForCollection, value);
 223  0
                     valRes.setError(MessageUtils.interpolate(getMessage("validation.maxOccurs"), toMap(field)));
 224  0
                     results.add(valRes);
 225  
                 }
 226  3
             } else {
 227  0
                 if (null != value) {
 228  0
                     processNestedObjectStructure(results, value, nestedObjStruct, field, elementStack, rootData, rootObjectStructure);
 229  
                 } else {
 230  0
                     if (field.getMinOccurs() != null && field.getMinOccurs() > 0) {
 231  0
                         ValidationResultInfo val = new ValidationResultInfo(getElementXpath(elementStack), value);
 232  0
                         val.setError(getMessage("validation.required"));
 233  0
                         results.add(val);
 234  
                     }
 235  
                 }
 236  
             }
 237  
             
 238  
 //            beanPathStack.pop();
 239  3
             elementStack.pop();
 240  
 
 241  3
         } else { // If non complex data type
 242  
 
 243  30
             if (value instanceof Collection) {
 244  
 
 245  0
                 if(((Collection<?>)value).isEmpty()){
 246  0
                     processConstraint(results, field, objStruct, "", dataProvider, elementStack, rootData, rootObjectStructure);
 247  
                 }
 248  
 
 249  0
                     int i = 0;
 250  0
                 for (Object o : (Collection<?>) value) {
 251  0
                         elementStack.push(Integer.toBinaryString(i));
 252  
 //                        beanPathStack.push(!beanPathStack.isEmpty()?beanPathStack.pop():""+"["+i+"]");
 253  0
                     processConstraint(results, field, objStruct, o, dataProvider, elementStack, rootData, rootObjectStructure);
 254  
 //                    beanPathStack.pop();
 255  
 //                    beanPathStack.push(field.isDynamic()?"attributes("+field.getName()+")":field.getName());
 256  0
                     elementStack.pop();
 257  0
                     i++;
 258  
                 }
 259  
 
 260  0
                 String xPath = getElementXpath(elementStack) + "/" + field.getName() + "/*";
 261  0
                 if (field.getMinOccurs() != null && field.getMinOccurs() > ((Collection<?>) value).size()) {
 262  0
                     ValidationResultInfo valRes = new ValidationResultInfo(xPath, value);
 263  0
                     valRes.setError(MessageUtils.interpolate(getMessage("validation.minOccurs"), toMap(field)));
 264  0
                     results.add(valRes);
 265  
                 }
 266  
 
 267  0
                 Integer maxOccurs = tryParse(field.getMaxOccurs());
 268  0
                 if (maxOccurs != null && maxOccurs < ((Collection<?>) value).size()) {
 269  0
                     ValidationResultInfo valRes = new ValidationResultInfo(xPath, value);
 270  0
                     valRes.setError(MessageUtils.interpolate(getMessage("validation.maxOccurs"), toMap(field)));
 271  0
                     results.add(valRes);
 272  
                 }
 273  0
             } else {
 274  30
                 processConstraint(results, field, objStruct, value, dataProvider, elementStack, rootData, rootObjectStructure);
 275  
             }
 276  
 
 277  
         }
 278  33
         return results;
 279  
     }
 280  
 
 281  
     protected Integer tryParse(String s) {
 282  27
         Integer result = null;
 283  27
         if (s != null) {
 284  
             try {
 285  23
                 result = Integer.valueOf(s);
 286  0
             } catch (NumberFormatException e) {
 287  
                 // do nothing
 288  23
             }
 289  
         }
 290  27
         return result;
 291  
     }
 292  
 
 293  
     protected void processNestedObjectStructure(List<ValidationResultInfo> results, Object value, ObjectStructureDefinition nestedObjStruct, FieldDefinition field, Stack<String> elementStack,  Object rootData, ObjectStructureDefinition rootObjStructure) {
 294  
 
 295  3
         results.addAll(validateObject(value, nestedObjStruct, elementStack, rootData, rootObjStructure, false));
 296  
 
 297  3
     }
 298  
 
 299  
     protected void processConstraint(List<ValidationResultInfo> valResults, FieldDefinition field, ObjectStructureDefinition objStructure, Object value, ConstraintDataProvider dataProvider, Stack<String> elementStack,  Object rootData, ObjectStructureDefinition rootObjStructure) {
 300  
 
 301  
         // Process Case Constraint
 302  
         // Case Constraint are only evaluated on the field. Nested case constraints are currently ignored
 303  55
         Constraint caseConstraint = processCaseConstraint(valResults, field, objStructure, value, dataProvider, elementStack, rootData, rootObjStructure);
 304  
 
 305  55
         Constraint constraint = (null != caseConstraint) ? caseConstraint : field;
 306  
 
 307  55
         processBaseConstraints(valResults, constraint, field.getDataType(), field.getName(), value, elementStack);
 308  
 
 309  
         // Stop other checks if value is null
 310  55
         if (value == null || "".equals(value.toString().trim())) {
 311  25
             return;
 312  
         }
 313  
 
 314  30
         String elementPath = getElementXpath(elementStack) + "/" + field.getName();
 315  
 
 316  
         // Process Valid Chars
 317  30
         if (null != constraint.getValidChars()) {
 318  15
             ValidationResultInfo val = processValidCharConstraint(elementPath, constraint.getValidChars(), dataProvider, value);
 319  15
             if (null != val) {
 320  3
                 valResults.add(val);
 321  
             }
 322  
         }
 323  
 
 324  
         // Process Require Constraints (only if this field has value)
 325  30
         if (value != null && !"".equals(value.toString().trim())) {
 326  30
             if (null != constraint.getRequireConstraint() && constraint.getRequireConstraint().size() > 0) {
 327  3
                 for (RequiredConstraint rc : constraint.getRequireConstraint()) {
 328  3
                     ValidationResultInfo val = processRequireConstraint(elementPath, rc, field, objStructure, dataProvider);
 329  3
                     if (null != val) {
 330  1
                         valResults.add(val);
 331  
                     }
 332  3
                 }
 333  
             }
 334  
         }
 335  
 
 336  
         // Process Occurs Constraint
 337  30
         if (null != constraint.getOccursConstraint() && constraint.getOccursConstraint().size() > 0) {
 338  0
             for (MustOccurConstraint oc : constraint.getOccursConstraint()) {
 339  0
                 ValidationResultInfo val = processOccursConstraint(elementPath, oc, field, objStructure, dataProvider);
 340  0
                 if (null != val) {
 341  0
                     valResults.add(val);
 342  
                 }
 343  0
             }
 344  
         }
 345  
 
 346  
         // Process lookup Constraint
 347  30
         if (null != constraint.getLookupDefinition()) {
 348  2
             processLookupConstraint(valResults, constraint.getLookupDefinition(), field, elementStack, dataProvider);
 349  
         }
 350  30
     }
 351  
 
 352  
     protected ValidationResultInfo processRequireConstraint(String element, RequiredConstraint constraint, FieldDefinition field, ObjectStructureDefinition objStructure, ConstraintDataProvider dataProvider) {
 353  
 
 354  3
         ValidationResultInfo val = null;
 355  
 
 356  3
         String fieldName = constraint.getFieldPath();// TODO parse fieldname from here
 357  3
         Object fieldValue = dataProvider.getValue(fieldName);
 358  
 
 359  3
         boolean result = true;
 360  
 
 361  3
         if (fieldValue instanceof java.lang.String) {
 362  3
             result = hasText((String) fieldValue);
 363  0
         } else if (fieldValue instanceof Collection) {
 364  0
             result = (((Collection<?>) fieldValue).size() > 0);
 365  
         } else {
 366  0
             result = (null != fieldValue) ? true : false;
 367  
         }
 368  
 
 369  3
         if (!result) {
 370  1
             Map<String, Object> rMap = new HashMap<String, Object>();
 371  1
             rMap.put("field1", field.getName());
 372  1
             rMap.put("field2", fieldName);
 373  1
             val = new ValidationResultInfo(element, fieldValue);
 374  1
             val.setError(MessageUtils.interpolate(getMessage("validation.requiresField"), rMap));
 375  
         }
 376  
 
 377  3
         return val;
 378  
     }
 379  
 
 380  
     /**
 381  
      * Process caseConstraint tag and sets any of the base constraint items if any of the when condition matches
 382  
      *
 383  
      * @param constraint
 384  
      * @param caseConstraint
 385  
      * @param field
 386  
      */
 387  
     protected Constraint processCaseConstraint(List<ValidationResultInfo> valResults, FieldDefinition field, ObjectStructureDefinition objStructure, Object value, ConstraintDataProvider dataProvider, Stack<String> elementStack,  Object rootData, ObjectStructureDefinition rootObjStructure) {
 388  
 
 389  55
         CaseConstraint constraint = field.getCaseConstraint();
 390  
 
 391  55
         if (null == constraint) {
 392  42
             return null;
 393  
         }
 394  
 
 395  13
         String operator = (hasText(constraint.getOperator())) ? constraint.getOperator() : "EQUALS";
 396  13
         FieldDefinition caseField = null;
 397  13
         boolean absolutePath = false;
 398  13
         if(hasText(constraint.getFieldPath())){
 399  13
                 if(constraint.getFieldPath().startsWith("/")){
 400  0
                         absolutePath = true;
 401  0
                         caseField = ValidatorUtils.getField(constraint.getFieldPath().substring(1), rootObjStructure);
 402  
                 }else{
 403  13
                         caseField = ValidatorUtils.getField(constraint.getFieldPath(), objStructure); 
 404  
                 }
 405  
         }
 406  
 
 407  
         // TODO: What happens when the field is not in the dataProvider?
 408  13
         Object fieldValue = value;
 409  13
         if(caseField!=null){
 410  0
                 if(absolutePath){
 411  
                         try {
 412  0
                                         fieldValue = PropertyUtils.getNestedProperty(rootData, constraint.getFieldPath().substring(1));
 413  0
                                 } catch (IllegalAccessException e) {
 414  0
                                 } catch (InvocationTargetException e) {
 415  0
                                 } catch (NoSuchMethodException e) {
 416  0
                                 }
 417  
                 }else{
 418  0
                     fieldValue = dataProvider.getValue(caseField.getName());
 419  
                 }
 420  
         }
 421  13
         DataType fieldDataType = (null != caseField ? caseField.getDataType():null);
 422  
 
 423  
         // If fieldValue is null then skip Case check
 424  13
         if(null == fieldValue) {
 425  9
             return null;
 426  
         }
 427  
 
 428  
         // Extract value for field Key
 429  4
         for (WhenConstraint wc : constraint.getWhenConstraint()) {
 430  
 
 431  7
                 if(hasText(wc.getValuePath())){
 432  0
                         Object whenValue = null;
 433  0
                         if(wc.getValuePath().startsWith("/")){
 434  
                                 try {
 435  0
                                         whenValue = PropertyUtils.getNestedProperty(rootData, wc.getValuePath().substring(1));
 436  0
                                         } catch (IllegalAccessException e) {
 437  0
                                         } catch (InvocationTargetException e) {
 438  0
                                         } catch (NoSuchMethodException e) {
 439  0
                                         }
 440  
                         }else{
 441  0
                                 whenValue = dataProvider.getValue(wc.getValuePath());
 442  
                         }
 443  0
                         if (ValidatorUtils.compareValues(fieldValue, whenValue, fieldDataType, operator, constraint.isCaseSensitive(), dateParser) && null != wc.getConstraint()) {
 444  0
                     return wc.getConstraint();
 445  
                 }
 446  0
                 }else{
 447  7
                         List<Object> whenValueList = wc.getValues();
 448  
             
 449  7
                     for (Object whenValue : whenValueList) {
 450  9
                         if (ValidatorUtils.compareValues(fieldValue, whenValue, fieldDataType, operator, constraint.isCaseSensitive(), dateParser) && null != wc.getConstraint()) {
 451  0
                             return wc.getConstraint();
 452  
                         }
 453  
                     }
 454  7
                 }
 455  
         }
 456  
 
 457  4
         return null;
 458  
     }
 459  
 
 460  
     public ValidationResultInfo processValidCharConstraint(String element, ValidCharsConstraint vcConstraint, ConstraintDataProvider dataProvider, Object value) {
 461  
 
 462  15
         ValidationResultInfo val = null;
 463  
 
 464  15
         StringBuilder fieldValue = new StringBuilder();
 465  15
         String validChars = vcConstraint.getValue();
 466  
 
 467  15
         fieldValue.append(ValidatorUtils.getString(value));
 468  
 
 469  15
         int typIdx = validChars.indexOf(":");
 470  15
         String processorType = "regex";
 471  15
         if (-1 == typIdx) {
 472  0
             validChars = "[" + validChars + "]*";
 473  
         } else {
 474  15
             processorType = validChars.substring(0, typIdx);
 475  15
             validChars = validChars.substring(typIdx + 1);
 476  
         }
 477  
 
 478  15
         if ("regex".equalsIgnoreCase(processorType)) {
 479  15
             if (fieldValue == null || !fieldValue.toString().matches(validChars)) {
 480  3
                     val = new ValidationResultInfo(element, fieldValue);
 481  3
                 if(vcConstraint.getLabelKey()!=null){
 482  0
                         val.setError(getMessage(vcConstraint.getLabelKey()));
 483  
                 }else{
 484  3
                         val.setError(getMessage("validation.validCharsFailed"));
 485  
                 }
 486  
             }
 487  
         }
 488  
 
 489  15
         return val;
 490  
     }
 491  
 
 492  
     /**
 493  
      * Computes if all the filed required in the occurs clause are between the min and max
 494  
      *
 495  
      * @param valResults
 496  
      * @param constraint
 497  
      * @param field
 498  
      * @param type
 499  
      * @param state
 500  
      * @param objStructure
 501  
      * @param dataProvider
 502  
      * @return
 503  
      */
 504  
     protected ValidationResultInfo processOccursConstraint(String element, MustOccurConstraint constraint, FieldDefinition field, ObjectStructureDefinition objStructure, ConstraintDataProvider dataProvider) {
 505  
 
 506  0
         boolean result = false;
 507  0
         int trueCount = 0;
 508  
 
 509  0
         ValidationResultInfo val = null;
 510  
 
 511  0
         for (RequiredConstraint rc : constraint.getRequiredFields()) {
 512  0
             trueCount += (processRequireConstraint("", rc, field, objStructure, dataProvider) != null) ? 1 : 0;
 513  
         }
 514  
 
 515  0
         for (MustOccurConstraint oc : constraint.getOccurs()) {
 516  0
             trueCount += (processOccursConstraint("", oc, field, objStructure, dataProvider) != null) ? 1 : 0;
 517  
         }
 518  
 
 519  0
         result = (trueCount >= constraint.getMin() && trueCount <= constraint.getMax()) ? true : false;
 520  
 
 521  0
         if (!result) {
 522  
          // TODO: figure out what data should go here instead of null
 523  0
             val = new ValidationResultInfo(element, null);
 524  0
             val.setError(getMessage("validation.occurs"));
 525  
         }
 526  
 
 527  0
         return val;
 528  
     }
 529  
 
 530  
     // TODO: Implement lookup constraint
 531  
     protected void processLookupConstraint(List<ValidationResultInfo> valResults, LookupConstraint lookupConstraint, FieldDefinition field, Stack<String> elementStack, ConstraintDataProvider dataProvider) {
 532  2
         if (lookupConstraint == null) {
 533  0
             return;
 534  
         }
 535  
 
 536  
         // Create search params based on the param mapping
 537  2
         List<SearchParam> params = new ArrayList<SearchParam>();
 538  2
         Object fieldValue = null;
 539  2
         for (CommonLookupParam paramMapping : lookupConstraint.getParams()) {
 540  4
             SearchParam param = new SearchParam();
 541  
 
 542  4
             param.setKey(paramMapping.getKey());
 543  
 
 544  
             // If the value of the search param comes form another field then get it
 545  4
             if (paramMapping.getFieldPath() != null && !paramMapping.getFieldPath().isEmpty()) {
 546  2
                 fieldValue = dataProvider.getValue(paramMapping.getFieldPath());
 547  2
                 if (fieldValue instanceof String) {
 548  2
                     param.setValue((String) fieldValue);
 549  0
                 } else if (fieldValue instanceof List<?>) {
 550  0
                     param.setValue((List<String>) fieldValue);
 551  
                 }
 552  2
             } else if (paramMapping.getDefaultValueString() != null) {
 553  2
                 param.setValue(paramMapping.getDefaultValueString());
 554  
             } else {
 555  0
                 param.setValue(paramMapping.getDefaultValueList());
 556  
             }
 557  4
             params.add(param);
 558  4
         }
 559  
 
 560  2
         SearchRequest searchRequest = new SearchRequest();
 561  2
         searchRequest.setMaxResults(1);
 562  2
         searchRequest.setStartAt(0);
 563  2
         searchRequest.setNeededTotalResults(false);
 564  2
         searchRequest.setSearchKey(lookupConstraint.getSearchTypeId());
 565  2
         searchRequest.setParams(params);
 566  
 
 567  2
         SearchResult searchResult = null;
 568  
         try {
 569  2
             searchResult = searchDispatcher.dispatchSearch(searchRequest);
 570  0
         } catch (Exception e) {
 571  0
             LOG.info("Error calling Search", e);
 572  2
         }
 573  2
         if (searchResult == null || searchResult.getRows() == null || searchResult.getRows().isEmpty()) {
 574  2
             ValidationResultInfo val = new ValidationResultInfo(getElementXpath(elementStack) + "/" + field.getName(), fieldValue);
 575  2
             val.setError(getMessage("validation.lookup"));
 576  2
             valResults.add(val);
 577  
         }
 578  2
     }
 579  
 
 580  
     protected void processBaseConstraints(List<ValidationResultInfo> valResults, Constraint constraint, DataType dataType, String name, Object value, Stack<String> elementStack) {
 581  
 
 582  55
         if (value == null || "".equals(value.toString().trim())) {
 583  25
             if (constraint.getMinOccurs() != null && constraint.getMinOccurs() > 0) {
 584  9
                 ValidationResultInfo val = new ValidationResultInfo(getElementXpath(elementStack) + "/" + name, value);
 585  9
                 val.setError(getMessage("validation.required"));
 586  9
                 valResults.add(val);
 587  
             }
 588  25
             return;
 589  
         }
 590  
 
 591  30
         String elementPath = getElementXpath(elementStack) + "/" + name;
 592  
 
 593  30
         if (DataType.STRING.equals(dataType)) {
 594  24
             validateString(value, constraint, elementPath, valResults);
 595  6
         } else if (DataType.INTEGER.equals(dataType)) {
 596  0
             validateInteger(value, constraint, elementPath, valResults);
 597  6
         } else if (DataType.LONG.equals(dataType)) {
 598  0
             validateLong(value, constraint, elementPath, valResults);
 599  6
         } else if (DataType.DOUBLE.equals(dataType)) {
 600  1
             validateDouble(value, constraint, elementPath, valResults);
 601  5
         } else if (DataType.FLOAT.equals(dataType)) {
 602  0
             validateFloat(value, constraint, elementPath, valResults);
 603  5
         } else if (DataType.BOOLEAN.equals(dataType)) {
 604  0
             validateBoolean(value, constraint, elementPath, valResults);
 605  5
         } else if (DataType.DATE.equals(dataType)) {
 606  5
             validateDate(value, constraint, elementPath, valResults, dateParser);
 607  
         }
 608  30
     }
 609  
 
 610  
     protected void validateBoolean(Object value, Constraint constraint, String element, List<ValidationResultInfo> results) {
 611  0
         if (!(value instanceof Boolean)) {
 612  
             try {
 613  0
                 Boolean.valueOf(value.toString());
 614  0
             } catch (Exception e) {
 615  0
                 ValidationResultInfo val = new ValidationResultInfo(element, value);
 616  0
                 val.setError(getMessage("validation.mustBeBoolean"));
 617  0
                 results.add(val);
 618  0
             }
 619  
         }
 620  0
     }
 621  
 
 622  
     protected void validateDouble(Object value, Constraint constraint, String element, List<ValidationResultInfo> results) {
 623  1
         Double v = null;
 624  
 
 625  1
         ValidationResultInfo val = new ValidationResultInfo(element, value);
 626  
 
 627  1
         if (value instanceof Number) {
 628  1
             v = ((Number) value).doubleValue();
 629  
         } else {
 630  
             try {
 631  0
                 v = Double.valueOf(value.toString());
 632  0
             } catch (Exception e) {
 633  0
                 val.setError(getMessage("validation.mustBeDouble"));
 634  0
             }
 635  
         }
 636  
 
 637  1
         if (val.isOk()) {
 638  1
             Double maxValue = ValidatorUtils.getDouble(constraint.getInclusiveMax());
 639  1
             Double minValue = ValidatorUtils.getDouble(constraint.getExclusiveMin());
 640  
 
 641  1
             if (maxValue != null && minValue != null) {
 642  
                 // validate range
 643  1
                 if (v > maxValue || v < minValue) {
 644  1
                     val.setError(MessageUtils.interpolate(getMessage("validation.outOfRange"), toMap(constraint)));
 645  
                 }
 646  0
             } else if (maxValue != null) {
 647  0
                 if (v > maxValue) {
 648  0
                     val.setError(MessageUtils.interpolate(getMessage("validation.maxValueFailed"), toMap(constraint)));
 649  
                 }
 650  0
             } else if (minValue != null) {
 651  0
                 if (v < minValue) {
 652  0
                     val.setError(MessageUtils.interpolate(getMessage("validation.minValueFailed"), toMap(constraint)));
 653  
                 }
 654  
             }
 655  
         }
 656  
 
 657  1
         if (!val.isOk()) {
 658  1
             results.add(val);
 659  
         }
 660  1
     }
 661  
 
 662  
     protected void validateFloat(Object value, Constraint constraint, String element, List<ValidationResultInfo> results) {
 663  0
         Float v = null;
 664  
 
 665  0
         ValidationResultInfo val = new ValidationResultInfo(element, value);
 666  0
         if (value instanceof Number) {
 667  0
             v = ((Number) value).floatValue();
 668  
         } else {
 669  
             try {
 670  0
                 v = Float.valueOf(value.toString());
 671  0
             } catch (Exception e) {
 672  0
                 val.setError(getMessage("validation.mustBeFloat"));
 673  0
             }
 674  
         }
 675  
 
 676  0
         if (val.isOk()) {
 677  0
             Float maxValue = ValidatorUtils.getFloat(constraint.getInclusiveMax());
 678  0
             Float minValue = ValidatorUtils.getFloat(constraint.getExclusiveMin());
 679  
 
 680  0
             if (maxValue != null && minValue != null) {
 681  
                 // validate range
 682  0
                 if (v > maxValue || v < minValue) {
 683  0
                     val.setError(MessageUtils.interpolate(getMessage("validation.outOfRange"), toMap(constraint)));
 684  
                 }
 685  0
             } else if (maxValue != null) {
 686  0
                 if (v > maxValue) {
 687  0
                     val.setError(MessageUtils.interpolate(getMessage("validation.maxValueFailed"), toMap(constraint)));
 688  
                 }
 689  0
             } else if (minValue != null) {
 690  0
                 if (v < minValue) {
 691  0
                     val.setError(MessageUtils.interpolate(getMessage("validation.minValueFailed"), toMap(constraint)));
 692  
                 }
 693  
             }
 694  
         }
 695  
 
 696  0
         if (!val.isOk()) {
 697  0
             results.add(val);
 698  
         }
 699  0
     }
 700  
 
 701  
     protected void validateLong(Object value, Constraint constraint, String element, List<ValidationResultInfo> results) {
 702  0
         Long v = null;
 703  
 
 704  0
         ValidationResultInfo val = new ValidationResultInfo(element, value);
 705  0
         if (value instanceof Number) {
 706  0
             v = ((Number) value).longValue();
 707  
         } else {
 708  
             try {
 709  0
                 v = Long.valueOf(value.toString());
 710  0
             } catch (Exception e) {
 711  0
                 val.setError(getMessage("validation.mustBeLong"));
 712  0
             }
 713  
         }
 714  
 
 715  0
         if (val.isOk()) {
 716  0
             Long maxValue = ValidatorUtils.getLong(constraint.getInclusiveMax());
 717  0
             Long minValue = ValidatorUtils.getLong(constraint.getExclusiveMin());
 718  
 
 719  0
             if (maxValue != null && minValue != null) {
 720  
                 // validate range
 721  0
                 if (v > maxValue || v < minValue) {
 722  0
                     val.setError(MessageUtils.interpolate(getMessage("validation.outOfRange"), toMap(constraint)));
 723  
                 }
 724  0
             } else if (maxValue != null) {
 725  0
                 if (v > maxValue) {
 726  0
                     val.setError(MessageUtils.interpolate(getMessage("validation.maxValueFailed"), toMap(constraint)));
 727  
                 }
 728  0
             } else if (minValue != null) {
 729  0
                 if (v < minValue) {
 730  0
                     val.setError(MessageUtils.interpolate(getMessage("validation.minValueFailed"), toMap(constraint)));
 731  
                 }
 732  
             }
 733  
         }
 734  
 
 735  0
         if (!val.isOk()) {
 736  0
             results.add(val);
 737  
         }
 738  
 
 739  0
     }
 740  
 
 741  
     protected void validateInteger(Object value, Constraint constraint, String element, List<ValidationResultInfo> results) {
 742  0
         Integer v = null;
 743  
 
 744  0
         ValidationResultInfo val = new ValidationResultInfo(element, value);
 745  
 
 746  0
         if (value instanceof Number) {
 747  0
             v = ((Number) value).intValue();
 748  
         } else {
 749  
             try {
 750  0
                 v = Integer.valueOf(value.toString());
 751  0
             } catch (Exception e) {
 752  0
                 val.setError(getMessage("validation.mustBeInteger"));
 753  0
             }
 754  
         }
 755  
 
 756  0
         if (val.isOk()) {
 757  0
             Integer maxValue = ValidatorUtils.getInteger(constraint.getInclusiveMax());
 758  0
             Integer minValue = ValidatorUtils.getInteger(constraint.getExclusiveMin());
 759  
 
 760  0
             if (maxValue != null && minValue != null) {
 761  
                 // validate range
 762  0
                 if (v > maxValue || v < minValue) {
 763  0
                     val.setError(MessageUtils.interpolate(getMessage("validation.outOfRange"), toMap(constraint)));
 764  
                 }
 765  0
             } else if (maxValue != null) {
 766  0
                 if (v > maxValue) {
 767  0
                     val.setError(MessageUtils.interpolate(getMessage("validation.maxValueFailed"), toMap(constraint)));
 768  
                 }
 769  0
             } else if (minValue != null) {
 770  0
                 if (v < minValue) {
 771  0
                     val.setError(MessageUtils.interpolate(getMessage("validation.minValueFailed"), toMap(constraint)));
 772  
                 }
 773  
             }
 774  
         }
 775  
 
 776  0
         if (!val.isOk()) {
 777  0
             results.add(val);
 778  
         }
 779  0
     }
 780  
 
 781  
     protected void validateDate(Object value, Constraint constraint, String element, List<ValidationResultInfo> results, DateParser dateParser) {
 782  5
         ValidationResultInfo val = new ValidationResultInfo(element, value);
 783  
 
 784  5
         Date v = null;
 785  
 
 786  5
         if (value instanceof Date) {
 787  5
             v = (Date) value;
 788  
         } else {
 789  
             try {
 790  0
                 v = dateParser.parseDate(value.toString());
 791  0
             } catch (Exception e) {
 792  0
                 val.setError(getMessage("validation.mustBeDate"));
 793  0
             }
 794  
         }
 795  
 
 796  5
         if (val.isOk()) {
 797  5
             Date maxValue = ValidatorUtils.getDate(constraint.getInclusiveMax(), dateParser);
 798  5
             Date minValue = ValidatorUtils.getDate(constraint.getExclusiveMin(), dateParser);
 799  
 
 800  5
             if (maxValue != null && minValue != null) {
 801  
                 // validate range
 802  0
                 if (v.getTime() > maxValue.getTime() || v.getTime() < minValue.getTime()) {
 803  0
                     val.setError(MessageUtils.interpolate(getMessage("validation.outOfRange"), toMap(constraint)));
 804  
                 }
 805  5
             } else if (maxValue != null) {
 806  0
                 if (v.getTime() > maxValue.getTime()) {
 807  0
                     val.setError(MessageUtils.interpolate(getMessage("validation.maxValueFailed"), toMap(constraint)));
 808  
                 }
 809  5
             } else if (minValue != null) {
 810  5
                 if (v.getTime() < minValue.getTime()) {
 811  1
                     val.setError(MessageUtils.interpolate(getMessage("validation.minValueFailed"), toMap(constraint)));
 812  
                 }
 813  
             }
 814  
         }
 815  
 
 816  5
         if (!val.isOk()) {
 817  1
             results.add(val);
 818  
         }
 819  5
     }
 820  
 
 821  
     protected void validateString(Object value, Constraint constraint, String element, List<ValidationResultInfo> results) {
 822  
 
 823  24
         if (value == null) {
 824  0
             value = "";
 825  
         }
 826  24
         String s = value.toString().trim();
 827  
 
 828  24
         ValidationResultInfo val = new ValidationResultInfo(element, value);
 829  
 
 830  24
         Integer maxLength = tryParse(constraint.getMaxLength());
 831  24
         if (maxLength != null && constraint.getMinLength() != null && constraint.getMinLength() > 0) {
 832  22
             if (s.length() > maxLength || s.length() < constraint.getMinLength()) {
 833  10
                 val.setError(MessageUtils.interpolate(getMessage("validation.lengthOutOfRange"), toMap(constraint)));
 834  
             }
 835  2
         } else if (maxLength != null) {
 836  1
             if (s.length() > Integer.parseInt(constraint.getMaxLength())) {
 837  1
                 val.setError(MessageUtils.interpolate(getMessage("validation.maxLengthFailed"), toMap(constraint)));
 838  
             }
 839  1
         } else if (constraint.getMinLength() != null && constraint.getMinLength() > 0) {
 840  1
             if (s.length() < constraint.getMinLength()) {
 841  1
                 val.setError(MessageUtils.interpolate(getMessage("validation.minLengthFailed"), toMap(constraint)));
 842  
             }
 843  
         }
 844  
 
 845  24
         if (!val.isOk()) {
 846  12
             results.add(val);
 847  
         }
 848  24
     }
 849  
 
 850  
     protected String getMessage(String messageId) {
 851  29
         if (null == messageService) {
 852  29
             return messageId;
 853  
         }
 854  
 
 855  0
         Message msg = messageService.getMessage(messageLocaleKey, messageGroupKey, messageId);
 856  
 
 857  0
         return msg.getValue();
 858  
     }
 859  
 
 860  
     protected String getElementXpath(Stack<String> elementStack) {
 861  74
         StringBuilder xPath = new StringBuilder();
 862  74
         Iterator<String> itr = elementStack.iterator();
 863  145
         while (itr.hasNext()) {
 864  71
             xPath.append(itr.next());
 865  71
             if(itr.hasNext()){
 866  34
                     xPath.append("/");
 867  
             }
 868  
         }
 869  
 
 870  74
         return xPath.toString();
 871  
     }
 872  
 
 873  
     /*
 874  
      * Homemade has text so we dont need outside libs.
 875  
      */
 876  
     protected boolean hasText(String string) {
 877  
 
 878  36
         if (string == null || string.length() < 1) {
 879  8
             return false;
 880  
         }
 881  28
         int stringLength = string.length();
 882  
 
 883  28
         for (int i = 0; i < stringLength; i++) {
 884  28
             char currentChar = string.charAt(i);
 885  28
             if (' ' != currentChar || '\t' != currentChar || '\n' != currentChar) {
 886  28
                 return true;
 887  
             }
 888  
         }
 889  
 
 890  0
         return false;
 891  
     }
 892  
 
 893  
     protected Map<String, Object> toMap(Constraint c) {
 894  14
         Map<String, Object> result = new HashMap<String, Object>();
 895  14
         result.put("minOccurs", c.getMinOccurs());
 896  14
         result.put("maxOccurs", c.getMaxOccurs());
 897  14
         result.put("minLength", c.getMinLength());
 898  14
         result.put("maxLength", c.getMaxLength());
 899  14
         result.put("minValue", c.getExclusiveMin());
 900  14
         result.put("maxValue", c.getInclusiveMax());
 901  
         // result.put("dataType", c.getDataType());
 902  
 
 903  14
         return result;
 904  
     }
 905  
 
 906  
     public SearchDispatcher getSearchDispatcher() {
 907  0
         return searchDispatcher;
 908  
     }
 909  
 
 910  
     public void setSearchDispatcher(SearchDispatcher searchDispatcher) {
 911  1
         this.searchDispatcher = searchDispatcher;
 912  1
     }
 913  
 
 914  
         @Override
 915  
         public List<ValidationResultInfo> validateObject(FieldDefinition field,
 916  
                         Object o, ObjectStructureDefinition objStructure,Stack<String> elementStack) {
 917  0
                 return null;
 918  
         }
 919  
 }