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