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