| 1 |  |   | 
  | 2 |  |   | 
  | 3 |  |   | 
  | 4 |  |   | 
  | 5 |  |   | 
  | 6 |  |   | 
  | 7 |  |   | 
  | 8 |  |   | 
  | 9 |  |   | 
  | 10 |  |   | 
  | 11 |  |   | 
  | 12 |  |   | 
  | 13 |  |   | 
  | 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 |  |   | 
  | 49 |  |   | 
  | 50 |  |      public DateParser getDateParser() { | 
  | 51 | 0 |          return dateParser; | 
  | 52 |  |      } | 
  | 53 |  |   | 
  | 54 |  |       | 
  | 55 |  |   | 
  | 56 |  |   | 
  | 57 |  |      public void setDateParser(DateParser dateParser) { | 
  | 58 | 0 |          this.dateParser = dateParser; | 
  | 59 | 0 |      } | 
  | 60 |  |   | 
  | 61 |  |   | 
  | 62 |  |       | 
  | 63 |  |   | 
  | 64 |  |   | 
  | 65 |  |   | 
  | 66 |  |   | 
  | 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 |  |   | 
  | 78 |  |   | 
  | 79 |  |   | 
  | 80 |  |   | 
  | 81 |  |   | 
  | 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 |  |   | 
  | 94 |  |   | 
  | 95 |  |   | 
  | 96 |  |   | 
  | 97 |  |   | 
  | 98 |  |   | 
  | 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 |  |   | 
  | 109 |  |   | 
  | 110 |  |   | 
  | 111 |  |   | 
  | 112 |  |   | 
  | 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 |  |                   | 
  | 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 |  |                       | 
  | 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 |  |                       | 
  | 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 |  |                   | 
  | 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 |  |                           | 
  | 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 |  |           | 
  | 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 |  |                       | 
  | 591 | 3 |                      validateOccurs(e.getKey(), values, meta, results); | 
  | 592 |  |                  } | 
  | 593 | 3 |              } | 
  | 594 |  |          } | 
  | 595 |  |   | 
  | 596 |  |           | 
  | 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 |  |                                       | 
  | 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 |  |           | 
  | 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 |  |       | 
  | 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 |  |       | 
  | 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 |  |  } |