Coverage Report - org.kuali.student.common.ui.client.validator.DataModelValidator
 
Classes in this File Line Coverage Branch Coverage Complexity
DataModelValidator
30%
103/342
18%
59/314
6.821
DataModelValidator$1
100%
1/1
N/A
6.821
 
 1  
 /**
 2  
  * Copyright 2010 The Kuali Foundation Licensed under the
 3  
  * Educational Community License, Version 2.0 (the "License"); you may
 4  
  * not use this file except in compliance with the License. You may
 5  
  * obtain a copy of the License at
 6  
  *
 7  
  * http://www.osedu.org/licenses/ECL-2.0
 8  
  *
 9  
  * Unless required by applicable law or agreed to in writing,
 10  
  * software distributed under the License is distributed on an "AS IS"
 11  
  * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 12  
  * or implied. See the License for the specific language governing
 13  
  * permissions and limitations under the License.
 14  
  */
 15  
 
 16  
 package org.kuali.student.common.ui.client.validator;
 17  
 
 18  
 import static org.kuali.student.common.ui.client.validator.ValidationMessageKeys.BOOLEAN;
 19  
 import static org.kuali.student.common.ui.client.validator.ValidationMessageKeys.DATE;
 20  
 import static org.kuali.student.common.ui.client.validator.ValidationMessageKeys.DOUBLE;
 21  
 import static org.kuali.student.common.ui.client.validator.ValidationMessageKeys.FLOAT;
 22  
 import static org.kuali.student.common.ui.client.validator.ValidationMessageKeys.INTEGER;
 23  
 import static org.kuali.student.common.ui.client.validator.ValidationMessageKeys.LENGTH_OUT_OF_RANGE;
 24  
 import static org.kuali.student.common.ui.client.validator.ValidationMessageKeys.LONG;
 25  
 import static org.kuali.student.common.ui.client.validator.ValidationMessageKeys.MAX_LENGTH;
 26  
 import static org.kuali.student.common.ui.client.validator.ValidationMessageKeys.MAX_OCCURS;
 27  
 import static org.kuali.student.common.ui.client.validator.ValidationMessageKeys.MAX_VALUE;
 28  
 import static org.kuali.student.common.ui.client.validator.ValidationMessageKeys.MIN_LENGTH;
 29  
 import static org.kuali.student.common.ui.client.validator.ValidationMessageKeys.MIN_OCCURS;
 30  
 import static org.kuali.student.common.ui.client.validator.ValidationMessageKeys.MIN_VALUE;
 31  
 import static org.kuali.student.common.ui.client.validator.ValidationMessageKeys.OCCURS;
 32  
 import static org.kuali.student.common.ui.client.validator.ValidationMessageKeys.OUT_OF_RANGE;
 33  
 import static org.kuali.student.common.ui.client.validator.ValidationMessageKeys.REQUIRED;
 34  
 import static org.kuali.student.common.ui.client.validator.ValidationMessageKeys.VALID_CHARS;
 35  
 import static org.kuali.student.core.assembly.data.MetadataInterrogator.getLargestMinLength;
 36  
 import static org.kuali.student.core.assembly.data.MetadataInterrogator.getLargestMinOccurs;
 37  
 import static org.kuali.student.core.assembly.data.MetadataInterrogator.getLargestMinValue;
 38  
 import static org.kuali.student.core.assembly.data.MetadataInterrogator.getLargestMinValueDate;
 39  
 import static org.kuali.student.core.assembly.data.MetadataInterrogator.getLargestMinValueDouble;
 40  
 import static org.kuali.student.core.assembly.data.MetadataInterrogator.getSmallestMaxLength;
 41  
 import static org.kuali.student.core.assembly.data.MetadataInterrogator.getSmallestMaxOccurs;
 42  
 import static org.kuali.student.core.assembly.data.MetadataInterrogator.getSmallestMaxValue;
 43  
 import static org.kuali.student.core.assembly.data.MetadataInterrogator.getSmallestMaxValueDate;
 44  
 import static org.kuali.student.core.assembly.data.MetadataInterrogator.getSmallestMaxValueDouble;
 45  
 import static org.kuali.student.core.assembly.data.MetadataInterrogator.isRequired;
 46  
 import static org.kuali.student.core.assembly.data.MetadataInterrogator.isRequiredForNextState;
 47  
 
 48  
 import java.util.ArrayList;
 49  
 import java.util.Date;
 50  
 import java.util.HashMap;
 51  
 import java.util.List;
 52  
 import java.util.Map;
 53  
 
 54  
 import org.kuali.student.common.ui.client.application.Application;
 55  
 import org.kuali.student.common.ui.client.configurable.mvc.FieldDescriptor;
 56  
 import org.kuali.student.common.ui.client.mvc.DataModel;
 57  
 import org.kuali.student.common.ui.client.mvc.DataModelDefinition;
 58  
 import org.kuali.student.common.util.MessageUtils;
 59  
 import org.kuali.student.common.validator.DateParser;
 60  
 import org.kuali.student.core.assembly.data.ConstraintMetadata;
 61  
 import org.kuali.student.core.assembly.data.Data;
 62  
 import org.kuali.student.core.assembly.data.Metadata;
 63  
 import org.kuali.student.core.assembly.data.QueryPath;
 64  
 import org.kuali.student.core.assembly.data.Data.DataType;
 65  
 import org.kuali.student.core.assembly.data.Data.StringKey;
 66  
 import org.kuali.student.core.validation.dto.ValidationResultInfo;
 67  
 
 68  
 import com.google.gwt.i18n.client.DateTimeFormat;
 69  
 
 70  3
 public class DataModelValidator {
 71  
         
 72  
         private static final String RUNTIME_DELETED_KEY = "_runtimeData/deleted";
 73  
         
 74  3
         private DateParser dateParser = null;
 75  3
         private boolean validateNextState = false;
 76  
 
 77  
         /**
 78  
          * @return the dateParser
 79  
          */
 80  
         public DateParser getDateParser() {
 81  0
                 return dateParser;
 82  
         }
 83  
 
 84  
         /**
 85  
          * @param dateParser
 86  
          *            the dateParser to set
 87  
          */
 88  
         public void setDateParser(DateParser dateParser) {
 89  0
                 this.dateParser = dateParser;
 90  0
         }
 91  
 
 92  
         
 93  
         /**
 94  
          * Use to validate the entire DataModel structure against constraints defined in the metadata. 
 95  
          * @param model
 96  
          * @return
 97  
          */
 98  
         public List<ValidationResultInfo> validate(final DataModel model) {
 99  4
                 validateNextState = false;
 100  4
                 List<ValidationResultInfo> results = new ArrayList<ValidationResultInfo>();
 101  4
                 DataModelDefinition def = (DataModelDefinition) model.getDefinition();
 102  4
                 doValidate(model, def.getMetadata(), new QueryPath(), results);
 103  4
                 return results;
 104  
         }
 105  
         
 106  
         /**
 107  
          * Use to validate the entire DataModel structure against constraints defined in the metadata
 108  
          * for the current state and the "next" state. 
 109  
          * @param model
 110  
          * @return
 111  
          */
 112  
         public List<ValidationResultInfo> validateNextState(final DataModel model) {
 113  0
                 validateNextState = true;
 114  0
                 List<ValidationResultInfo> results = new ArrayList<ValidationResultInfo>();
 115  0
                 DataModelDefinition def = (DataModelDefinition) model.getDefinition();
 116  0
                 doValidate(model, def.getMetadata(), new QueryPath(), results);
 117  
 
 118  0
                 return results;
 119  
         }
 120  
         
 121  
         /**
 122  
          * Use to validated a single field within the data model against constraints defined in the metadata
 123  
          * @param fd
 124  
          * @param model
 125  
          * @return
 126  
          */
 127  
         public List<ValidationResultInfo> validate(FieldDescriptor fd,
 128  
                         DataModel model) {
 129  0
                 validateNextState = false;
 130  0
                 List<ValidationResultInfo> results = new ArrayList<ValidationResultInfo>();
 131  0
                 if(fd != null && fd.getMetadata() != null && fd.getFieldKey() != null){
 132  0
                         doValidate(model, fd.getMetadata(), QueryPath.parse(fd.getFieldKey()), results);        
 133  
                 }
 134  
                                 
 135  0
                 return results;
 136  
         }
 137  
         
 138  
     private void doValidate(final DataModel model, final Metadata meta, final QueryPath path, List<ValidationResultInfo> results) {
 139  15
             switch (meta.getDataType()) {
 140  
                 case DATA:
 141  
                         // intentional fallthrough case
 142  
                 case LIST:
 143  8
                         doValidateComplex(model, meta, path, results);
 144  
                         break;
 145  
             }
 146  
             
 147  15
             if (meta.getConstraints() != null){
 148  15
                     switch (meta.getDataType()){
 149  
                     
 150  
                         case BOOLEAN:
 151  0
                                 doValidateBoolean(model, meta, path, results);
 152  0
                                 break;
 153  
                         
 154  
                         case DATE:
 155  
                                 // intentional fallthrough case
 156  
                         case TRUNCATED_DATE:
 157  0
                                 doValidateDate(model, meta, path, results);
 158  0
                                 break;
 159  
                         
 160  
                         case DOUBLE:
 161  0
                                 doValidateDouble(model, meta, path, results);
 162  0
                                 break;
 163  
                         
 164  
                         case FLOAT:
 165  0
                                 doValidateFloat(model, meta, path, results);
 166  0
                                 break;
 167  
         
 168  
                         case INTEGER:
 169  0
                                 doValidateInteger(model, meta, path, results);
 170  0
                                 break;
 171  
                         
 172  
                         case LONG:
 173  0
                                 doValidateLong(model, meta, path, results);
 174  0
                                 break;
 175  
                         
 176  
                         case STRING:
 177  7
                                 doValidateString(model, meta, path, results);
 178  7
                                 break;
 179  
                         
 180  
                         default:
 181  
                                 // do nothing
 182  
                         }
 183  
             }
 184  15
         }
 185  
         
 186  
     private void addError(List<ValidationResultInfo> list, QueryPath element, ValidationMessageKeys msgKey, Map<String, Object> constraintInfo) {
 187  3
         ValidationResultInfo v = new ValidationResultInfo();            
 188  3
         String rawMsg = getValidationMessage(msgKey.getKey());
 189  3
         v.setElement(element.toString());
 190  3
         v.setError(MessageUtils.interpolate(rawMsg, constraintInfo));       
 191  3
         list.add(v);
 192  3
     }
 193  
     
 194  
     private void addError(List<ValidationResultInfo> list, QueryPath element, ValidationMessageKeys msgKey, Object value ){
 195  0
         ValidationResultInfo v = new ValidationResultInfo();
 196  0
         String rawMsg = getValidationMessage(msgKey.getKey());
 197  0
         v.setElement(element.toString());
 198  0
         v.setError(MessageUtils.interpolate(rawMsg, msgKey.getProperty(), value));       
 199  0
         list.add(v);
 200  0
     }
 201  
     
 202  
     protected String getValidationMessage(String msgKey){
 203  0
             return Application.getApplicationContext().getMessage(msgKey);
 204  
     }
 205  
     
 206  
     private void addError(List<ValidationResultInfo> list, QueryPath element, ValidationMessageKeys msgKey){
 207  0
             addError(list, element, msgKey.getKey());
 208  0
     }
 209  
     
 210  
     private void addError(List<ValidationResultInfo> list, QueryPath element, String msgKey){
 211  0
         ValidationResultInfo v = new ValidationResultInfo();
 212  0
         v.setElement(element.toString());
 213  0
         v.setError(getValidationMessage(msgKey));       
 214  0
         list.add(v);
 215  0
     }
 216  
     
 217  
     private void addRangeError(List<ValidationResultInfo> list, QueryPath element, ValidationMessageKeys msgKey, Object minValue, Object maxValue){
 218  3
             Map<String, Object> constraintInfo = new HashMap<String, Object>();
 219  
             
 220  3
             put(constraintInfo, MIN_VALUE.getProperty(), minValue);
 221  3
             put(constraintInfo, MAX_VALUE.getProperty(), maxValue);
 222  
             
 223  3
             addError(list, element, msgKey, constraintInfo);
 224  3
     }
 225  
     
 226  
     private boolean isRequiredCheck(Metadata meta){
 227  8
             if(validateNextState){
 228  0
                     return (isRequired(meta) || isRequiredForNextState(meta));
 229  
             }
 230  
             else{
 231  8
                     return isRequired(meta);
 232  
             }
 233  
     }
 234  
         
 235  
         private void doValidateString(DataModel model, Metadata meta,
 236  
                         QueryPath path, List<ValidationResultInfo> results) {
 237  
                 
 238  7
             Map<QueryPath, Object> values = model.query(path);
 239  
                         
 240  7
                 if (values.isEmpty() && isRequiredCheck(meta)) {
 241  0
                         addError(results, path, REQUIRED);
 242  
                 } else {
 243  7
                     Object[] keys = values.keySet().toArray();
 244  14
                         for (int keyIndex = 0; keyIndex < keys.length; keyIndex++) {
 245  7
                                 QueryPath element = (QueryPath)keys[keyIndex];
 246  
         
 247  7
                                 String s = (values.get(element) == null) ? "" : values.get(element).toString();
 248  7
                                 doValidateString(s,element,meta,results);
 249  
                                                                         
 250  
                         }
 251  
                 }
 252  7
         }
 253  
         
 254  
         
 255  
         public void doValidateString(String s, QueryPath element, Metadata meta,
 256  
                         List<ValidationResultInfo> results) {
 257  7
                 if (s.isEmpty() && isRequiredCheck(meta)) {
 258  0
                         addError(results, element, REQUIRED);
 259  7
                 } else if(!s.isEmpty()) {
 260  5
                         int len = s.length();
 261  5
                         Integer minLength = getLargestMinLength(meta);
 262  5
                         Integer maxLength = getSmallestMaxLength(meta);
 263  
                         
 264  5
                         if (minLength != null && maxLength != null) {
 265  5
                                 if (len < minLength || len > maxLength) {
 266  3
                                         addRangeError(results, element, LENGTH_OUT_OF_RANGE, minLength, maxLength);
 267  
                                 }
 268  0
                         } else if (minLength != null && len < minLength) {
 269  0
                                 addError(results, element, MIN_LENGTH, minLength);
 270  0
                         } else if (maxLength != null && len > maxLength) {
 271  0
                                 addError(results, element, MAX_LENGTH, maxLength);
 272  
                         }
 273  
                         
 274  
                         // process valid chars constraint
 275  5
                         if (meta.getConstraints() != null) {
 276  5
                                 boolean failed = false;
 277  5
                                 List<ConstraintMetadata> constraints = meta.getConstraints();
 278  
                                 
 279  10
                                 for (int consIdx=0; consIdx <constraints.size(); consIdx++) {
 280  5
                                         ConstraintMetadata cons = constraints.get(consIdx);
 281  5
                                         if (failed) {
 282  0
                                                 break;
 283  
                                         }
 284  5
                                         String validChars = cons.getValidChars();
 285  5
                                         validChars = (validChars == null) ? "" : validChars.trim();
 286  5
                                         if (!validChars.isEmpty()) {
 287  0
                                                 if (validChars.startsWith("regex:")) {
 288  0
                                                         validChars = validChars.substring(6);
 289  0
                                                         if (!s.matches(validChars)) {
 290  0
                                                                 if(cons.getValidCharsMessageId() != null ){
 291  0
                                                                         addError(results, element, cons.getValidCharsMessageId());
 292  
                                                                 }else{
 293  0
                                                                         addError(results, element, VALID_CHARS);        
 294  
                                                                 }
 295  0
                                                                 failed = true;
 296  0
                                                                 break;
 297  
                                                         }
 298  
                                                 } else {
 299  0
                                                         for (char c : s.toCharArray()) {
 300  0
                                                                 if (!validChars.contains(String.valueOf(c))) {
 301  0
                                                                         if(cons.getValidCharsMessageId() != null ){
 302  0
                                                                                 addError(results, element, cons.getValidCharsMessageId());
 303  
                                                                         }else{
 304  0
                                                                                 addError(results, element, VALID_CHARS);        
 305  
                                                                         }
 306  0
                                                                         failed = true;
 307  0
                                                                         break;
 308  
                                                                 }
 309  
                                                         }
 310  
                                                 }
 311  
                                         }
 312  
                                 }
 313  
                         }
 314  
                 }
 315  7
         }
 316  
 
 317  
         private void doValidateInteger(DataModel model, Metadata meta,
 318  
                         QueryPath path, List<ValidationResultInfo> results) {
 319  
                 
 320  0
             Map<QueryPath, Object> values = model.query(path);
 321  
             
 322  0
                 if (values.isEmpty() && isRequiredCheck(meta)) {
 323  0
                         addError(results, path, REQUIRED);
 324  
                 } else {
 325  0
                         Object[] keys = values.keySet().toArray();
 326  0
                         for (int keyIndex = 0; keyIndex < keys.length; keyIndex++) {
 327  0
                                 QueryPath element = (QueryPath)keys[keyIndex];
 328  
         
 329  0
                                 Object o = values.get(element);
 330  
                                 
 331  0
                                 if (o == null) {
 332  0
                                         if (isRequiredCheck(meta)) {
 333  0
                                                 addError(results, element, REQUIRED);
 334  
                                         }
 335  
                                 } else {
 336  0
                                         Integer i = null;
 337  
                                         try {
 338  0
                                                 i = (o instanceof Integer) ? (Integer) o : Integer.valueOf(o.toString());
 339  0
                                         } catch (Exception ex) {
 340  0
                                                 addError(results, element, INTEGER);
 341  0
                                         }
 342  
                                         
 343  0
                                         if (i != null) {
 344  0
                                             Long min = getLargestMinValue(meta);
 345  0
                                             Long max = getSmallestMaxValue(meta);
 346  
                                             
 347  0
                                             if (min != null && max != null) {
 348  0
                                                     if (i < min || i > max) {
 349  0
                                                             addRangeError(results, element, OUT_OF_RANGE, min, max);
 350  
                                                     }
 351  0
                                             } else if (min != null && i < min) {
 352  0
                                                     addError(results, element, MIN_VALUE, min);
 353  0
                                             } else if (max != null && i > max) {
 354  0
                                                     addError(results, element, MAX_VALUE, max);
 355  
                                             }
 356  
                                         }
 357  
                                 }
 358  
                         }
 359  
                 }
 360  0
         }
 361  
 
 362  
         private void doValidateLong(DataModel model, Metadata meta,
 363  
                         QueryPath path, List<ValidationResultInfo> results) {
 364  
                 
 365  0
             Map<QueryPath, Object> values = model.query(path);
 366  
 
 367  0
                 if (values.isEmpty() && isRequiredCheck(meta)) {
 368  0
                         addError(results, path, REQUIRED);
 369  
                 } else {
 370  0
                     Object[] keys = values.keySet().toArray();
 371  0
                         for (int keyIndex = 0; keyIndex < keys.length; keyIndex++) {
 372  0
                                 QueryPath element = (QueryPath)keys[keyIndex];
 373  
         
 374  0
                                 Object o = values.get(element);
 375  
                                 
 376  0
                                 if (o == null) {
 377  0
                                         if (isRequiredCheck(meta)) {
 378  0
                                                 addError(results, element, REQUIRED);
 379  
                                         }
 380  
                                 } else {
 381  0
                                         Long i = null;
 382  
                                         try {
 383  0
                                                 i = (o instanceof Long) ? (Long) o : Long.valueOf(o.toString());
 384  0
                                         } catch (Exception ex) {
 385  0
                                                 addError(results, element, LONG);
 386  0
                                         }
 387  
                                         
 388  
                                         
 389  0
                                         if (i != null) {
 390  0
                                             Long min = getLargestMinValue(meta);
 391  0
                                             Long max = getSmallestMaxValue(meta);
 392  
                                             
 393  0
                                             if (min != null && max != null) {
 394  0
                                                     if (i < min || i > max) {
 395  0
                                                             addRangeError(results, element, OUT_OF_RANGE, min, max);
 396  
                                                     }
 397  0
                                             } else if (min != null && i < min) {
 398  0
                                                     addError(results, element, MIN_VALUE, min);
 399  0
                                             } else if (max != null && i > max) {
 400  0
                                                     addError(results, element, MAX_VALUE, max);
 401  
                                             }
 402  
                                         }
 403  
                                 }
 404  
                         }
 405  
                 }
 406  0
         }
 407  
         
 408  
         private void doValidateDouble(DataModel model, Metadata meta,
 409  
                         QueryPath path, List<ValidationResultInfo> results) {
 410  
                 
 411  0
             Map<QueryPath, Object> values = model.query(path);
 412  
 
 413  0
             if (values.isEmpty() && isRequiredCheck(meta)) {
 414  0
                         addError(results, path, REQUIRED);
 415  
             } else {
 416  0
                         Object[] keys = values.keySet().toArray();
 417  0
                         for (int keyIndex = 0; keyIndex < keys.length; keyIndex++) {
 418  0
                                 QueryPath element = (QueryPath)keys[keyIndex];
 419  
         
 420  0
                                 Object o = values.get(element);
 421  
                                 
 422  0
                                 if (o == null) {
 423  0
                                         if (isRequiredCheck(meta)) {
 424  0
                                                 addError(results, element, REQUIRED);
 425  
                                         }
 426  
                                 } else {
 427  0
                                         Double d = null;
 428  
                                         try {
 429  0
                                                 d = (o instanceof Double) ? (Double) o : Double.valueOf(o.toString());
 430  0
                                         } catch (Exception ex) {
 431  0
                                                 addError(results, element, DOUBLE);
 432  0
                                         }
 433  
                                         
 434  
                                         
 435  0
                                         if (d != null) {
 436  0
                                             Double min = getLargestMinValueDouble(meta);
 437  0
                                             Double max = getSmallestMaxValueDouble(meta);
 438  
                                                                                 
 439  0
                                             if (min != null && max != null) {
 440  0
                                                     if (d < min || d > max) {
 441  0
                                                             addRangeError(results, element, OUT_OF_RANGE,  min, max);
 442  
                                                     }
 443  0
                                             } else if (min != null && d < min) {
 444  0
                                                     addError(results, element, MIN_VALUE, min);
 445  0
                                             } else if (max != null && d > max) {
 446  0
                                                     addError(results, element, MAX_VALUE, max);
 447  
                                             }
 448  
                                         }
 449  
                                 }
 450  
                         }
 451  
             }
 452  0
         }
 453  
         
 454  
         private void doValidateFloat(DataModel model, Metadata meta,
 455  
                         QueryPath path, List<ValidationResultInfo> results) {
 456  
                 
 457  0
             Map<QueryPath, Object> values = model.query(path);
 458  
 
 459  0
                 if (values.isEmpty() && isRequiredCheck(meta)) {
 460  0
                         addError(results, path, REQUIRED);
 461  
                 } else {            
 462  0
                         Object[] keys = values.keySet().toArray();                
 463  0
                         for (int keyIndex = 0; keyIndex < keys.length; keyIndex++) {
 464  0
                                 QueryPath element = (QueryPath)keys[keyIndex];
 465  
         
 466  0
                                 Object o = values.get(element);
 467  
                                 
 468  0
                                 if (o == null) {
 469  0
                                         if (isRequiredCheck(meta)) {
 470  0
                                                 addError(results, element, REQUIRED);
 471  
                                         }
 472  
                                 } else {
 473  0
                                         Float d = null;
 474  
                                         try {
 475  0
                                                 d = (o instanceof Float) ? (Float) o : Float.valueOf(o.toString());
 476  0
                                         } catch (Exception ex) {
 477  0
                                                 addError(results, element, FLOAT);
 478  0
                                         }
 479  
                                         
 480  
         
 481  0
                                         if (d != null) {
 482  0
                                             Double min = getLargestMinValueDouble(meta);
 483  0
                                             Double max = getSmallestMaxValueDouble(meta);
 484  
                                             
 485  0
                                             if (min != null && max != null) {
 486  0
                                                     if (d < min || d > max) {
 487  0
                                                             addRangeError(results, element, OUT_OF_RANGE,  min, max);
 488  
                                                     }
 489  0
                                             } else if (min != null && d < min) {
 490  0
                                                     addError(results, element, MIN_VALUE, min);
 491  0
                                             } else if (max != null && d > max) {
 492  0
                                                     addError(results, element, MAX_VALUE, max);
 493  
                                             }
 494  
                                         }
 495  
                                 }
 496  
                         }
 497  
                 }
 498  0
         }
 499  
         
 500  
         private void doValidateDate(DataModel model, Metadata meta,
 501  
                         QueryPath path, List<ValidationResultInfo> results) {
 502  
                 
 503  0
             Map<QueryPath, Object> values = model.query(path);
 504  
 
 505  0
                 if (values.isEmpty() && isRequiredCheck(meta)) {
 506  0
                         addError(results, path, REQUIRED);
 507  
                 } else {
 508  0
                     Object[] keys = values.keySet().toArray();
 509  0
                         for (int keyIndex = 0; keyIndex < keys.length; keyIndex++) {
 510  0
                                 QueryPath element = (QueryPath)keys[keyIndex];
 511  0
                                 Object o = values.get(element);
 512  
                                 
 513  0
                                 if (o == null) {
 514  0
                                         if (isRequiredCheck(meta)) {
 515  0
                                                 addError(results, element, REQUIRED);
 516  
                                         }
 517  
                                 } else {
 518  0
                                         Date d = null;
 519  
                                         try {
 520  0
                                                 d = (o instanceof Date) ? (Date) o : dateParser.parseDate(o.toString());
 521  0
                                         } catch (Exception ex) {
 522  0
                                                 addError(results, element, DATE);
 523  0
                                         }
 524  
                                 
 525  
                                         
 526  0
                                         if (d != null) {
 527  
                                                 //Get defined min/max value constraint
 528  0
                                             Date min = getLargestMinValueDate(meta, dateParser, getCrossFieldMinValue(model, element, meta));
 529  0
                                             Date max = getSmallestMaxValueDate(meta, dateParser, getCrossFieldMaxValue(model, element, meta));
 530  
                                                                                 
 531  0
                                             if (min != null && max != null) {
 532  0
                                                     if (d.getTime() < min.getTime() || d.getTime() > max.getTime()) {
 533  0
                                                             addRangeError(results, element, OUT_OF_RANGE,  asDateString(min), asDateString(max));
 534  
                                                     }
 535  0
                                             } else if (min != null && d.getTime() < min.getTime()) {
 536  0
                                                     addError(results, element, MIN_VALUE, asDateString(min));
 537  0
                                             } else if (max != null && d.getTime() > max.getTime()) {
 538  0
                                                     addError(results, element, MAX_VALUE, asDateString(max));
 539  
                                             }
 540  
                                         }
 541  
                                 }
 542  
                         }
 543  
                 }
 544  0
         }
 545  
         
 546  
         
 547  
         private void doValidateBoolean(DataModel model, Metadata meta,
 548  
                         QueryPath path, List<ValidationResultInfo> results) {
 549  
                 
 550  0
             Map<QueryPath, Object> values = model.query(path);
 551  
 
 552  0
                 if (values.isEmpty() && isRequiredCheck(meta)) {
 553  0
                         addError(results, path, REQUIRED);
 554  
                 } else {
 555  
         
 556  0
                     Object[] keys = values.keySet().toArray();
 557  0
                         for (int keyIndex = 0; keyIndex < keys.length; keyIndex++) {
 558  0
                                 QueryPath element = (QueryPath)keys[keyIndex];
 559  
         
 560  0
                                 Object o = values.get(element);
 561  
                                 
 562  0
                                 if (o == null) {
 563  0
                                         if (isRequiredCheck(meta)) {
 564  0
                                                 addError(results, element, REQUIRED);
 565  
                                         }
 566  
                                 } else {
 567  0
                                         if (o instanceof Boolean == false) {
 568  0
                                                 addError(results, element, BOOLEAN);
 569  
                                         }
 570  
                                 }
 571  
                         }
 572  
                 }
 573  0
         }
 574  
 
 575  
         private void doValidateComplex(final DataModel model, final Metadata meta, final QueryPath path, List<ValidationResultInfo> results) {
 576  8
                 Map<QueryPath, Object> values = model.query(path);
 577  8
                 boolean hasChildElements = true;
 578  
                 
 579  
                 //Check if complex object is required and/or meets min/max occurs requirements
 580  8
                 if (values.isEmpty() && isRequiredCheck(meta)) {
 581  0
                         addError(results, path, REQUIRED);
 582  0
                         hasChildElements = false;
 583  8
                 } else if (meta.getDataType().equals(DataType.LIST)){
 584  3
                         hasChildElements = false;
 585  3
                         for (Map.Entry<QueryPath, Object> e:values.entrySet()){
 586  3
                                 QueryPath listPath = QueryPath.parse(e.getKey().toString());
 587  3
                                 listPath.add(Data.WILDCARD_KEY);
 588  3
                                 values = model.query(listPath);
 589  
                                 
 590  3
                                 if (!values.isEmpty()){
 591  1
                                         hasChildElements = true;
 592  
                                 }
 593  
                                 
 594  3
                                 if (values.isEmpty() && isRequiredCheck(meta)){
 595  0
                                         addError(results, e.getKey(), REQUIRED);
 596  
                                 } else {        
 597  
                                         // do min/max occurs checks
 598  3
                                         validateOccurs(e.getKey(), values, meta, results);
 599  
                                 }
 600  3
                         }
 601  
                 }
 602  
                         
 603  
                 // Validate child elements when child elements exist in data or when validating root path
 604  8
                 if (hasChildElements || path.toString().isEmpty()){
 605  6
                         String basePath = path.toString();
 606  6
                         if (meta.getProperties() != null) {
 607  6
                                 Object[] keys = meta.getProperties().keySet().toArray();
 608  17
                                 for (int keyIndex = 0; keyIndex < keys.length; keyIndex++) {
 609  11
                                         String element = (String)keys[keyIndex];
 610  11
                                         if (!element.contains("runtimeData")){
 611  11
                                                 QueryPath childPath = QueryPath.concat(basePath, element);
 612  
                                                 //System.out.println(childPath.toString());
 613  11
                                                 doValidate(model, meta.getProperties().get(element), childPath, results);
 614  
                                         }
 615  
                                 }
 616  
                         }
 617  
                 }        
 618  8
         }
 619  
         
 620  
         private boolean validateOccurs(QueryPath path, Map<QueryPath, Object> values, Metadata meta, List<ValidationResultInfo> results) {
 621  
             
 622  3
             int size = getListSize(values, meta);
 623  
                 
 624  3
             Integer min = getLargestMinOccurs(meta);
 625  3
             boolean minValid = min == null || min <= size;
 626  
          
 627  3
             Integer max = getSmallestMaxOccurs(meta);
 628  3
         boolean maxValid = (max == null || max == -1 || max >= size);
 629  
             
 630  
         
 631  3
                 if (!minValid || !maxValid) {
 632  0
             if (!minValid && !maxValid) {
 633  0
                     addRangeError(results, path, OCCURS, min, max);
 634  0
             } else if (!minValid) {
 635  0
                 addError(results, path, MIN_OCCURS, min);
 636  
             } else {
 637  0
                     addError(results, path, MAX_OCCURS, max);
 638  
             }
 639  
         }
 640  
         
 641  3
             return minValid && maxValid;
 642  
         }
 643  
         
 644  
         private int getListSize(Map<QueryPath, Object> values, Metadata meta){
 645  3
             int size = 0;
 646  
 
 647  
             //Check to see if a complex data element in list has been deleted
 648  3
             Map<String, Metadata> properties = meta.getProperties();                
 649  3
                 if (properties.containsKey(Data.WILDCARD_KEY.toString())){
 650  3
                         Metadata listMeta = properties.get(Data.WILDCARD_KEY.toString());
 651  3
                         if (listMeta != null && listMeta.getDataType().equals(DataType.DATA)){
 652  3
                                 Object[] valueList = values.values().toArray();
 653  4
                                 for (int i=0; i < valueList.length; i++){
 654  1
                                         Object value = valueList[i];
 655  1
                                         Data d = (Data)value;
 656  1
                                         Boolean deleted = d.query(RUNTIME_DELETED_KEY);
 657  1
                                         if (deleted == null || !deleted){
 658  1
                                                 size++;
 659  
                                         }
 660  
                                 }
 661  3
                         } else {
 662  0
                                 size = values.size();
 663  
                         }
 664  3
                 } else {
 665  0
                         size = values.size();
 666  
                 }
 667  
                 
 668  3
                 return size;
 669  
         }
 670  
                     
 671  
         //FIXME: This is a temp solution for getting cross field min value
 672  
     private Object getCrossFieldMinValue(DataModel model, QueryPath path, Metadata meta){
 673  0
             Object v = null;
 674  0
             List<ConstraintMetadata> constraints = meta.getConstraints();
 675  0
             for (int i=0; i < constraints.size(); i++) {
 676  0
                     ConstraintMetadata cons = constraints.get(i);
 677  0
                         if (cons.getMinValue() != null && cons.getMinValue().contains("../")){
 678  0
                                 QueryPath crossFieldPath = QueryPath.parse(path.toString());
 679  0
                                 String crossFieldKey = cons.getMinValue().substring(3);
 680  0
                                 crossFieldPath.remove(crossFieldPath.size()-1);
 681  0
                                 crossFieldPath.add(new StringKey(crossFieldKey));
 682  0
                                 v = model.get(crossFieldPath);
 683  
                         }
 684  
                 }
 685  
             
 686  0
             return v;
 687  
     }
 688  
     
 689  
         //FIXME: This is a temp solution for getting cross field max value
 690  
     private Object getCrossFieldMaxValue(DataModel model, QueryPath path, Metadata meta){
 691  0
             Object v = null;
 692  0
             List<ConstraintMetadata> constraints = meta.getConstraints();
 693  0
             for (int i=0; i < constraints.size(); i++) {
 694  0
                     ConstraintMetadata cons = constraints.get(i);
 695  0
                         if (cons.getMaxValue() != null && cons.getMaxValue().contains("../")){
 696  0
                                 QueryPath crossFieldPath = QueryPath.parse(path.toString());
 697  0
                                 String crossFieldKey = cons.getMinValue().substring(3);
 698  0
                                 crossFieldPath.remove(crossFieldPath.size()-1);
 699  0
                                 crossFieldPath.add(new StringKey(crossFieldKey));
 700  0
                                 v = model.get(crossFieldPath);
 701  
                         }
 702  
                 }
 703  
             
 704  0
             return v;
 705  
     }
 706  
 
 707  
     private void put(Map<String, Object> m, String key, Object value) {
 708  6
         if (value != null) {
 709  6
             m.put(key, value);
 710  
         }
 711  6
     }
 712  
     
 713  
     private String asDateString(Date date){
 714  0
             DateTimeFormat dateTimeFormat = DateTimeFormat.getFormat("MM/dd/yyyy");
 715  0
             return dateTimeFormat.format(date);
 716  
     }
 717  
 
 718  
 
 719  
         
 720  
 }