View Javadoc

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