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