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