View Javadoc

1   /**
2    * Copyright 2010 The Kuali Foundation Licensed under the Educational Community License, Version 2.0 (the "License"); you may
3    * not use this file except in compliance with the License. You may obtain a copy of the License at
4    * http://www.osedu.org/licenses/ECL-2.0 Unless required by applicable law or agreed to in writing, software distributed
5    * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
6    * implied. See the License for the specific language governing permissions and limitations under the License.
7    */
8   
9   package org.kuali.student.common.validator;
10  
11  import java.util.ArrayList;
12  import java.util.Collection;
13  import java.util.Date;
14  import java.util.HashMap;
15  import java.util.Iterator;
16  import java.util.List;
17  import java.util.Map;
18  import java.util.Stack;
19  
20  import org.apache.log4j.Logger;
21  import org.kuali.student.common.util.MessageUtils;
22  import org.kuali.student.core.dictionary.dto.CaseConstraint;
23  import org.kuali.student.core.dictionary.dto.CommonLookupParam;
24  import org.kuali.student.core.dictionary.dto.Constraint;
25  import org.kuali.student.core.dictionary.dto.DataType;
26  import org.kuali.student.core.dictionary.dto.FieldDefinition;
27  import org.kuali.student.core.dictionary.dto.LookupConstraint;
28  import org.kuali.student.core.dictionary.dto.MustOccurConstraint;
29  import org.kuali.student.core.dictionary.dto.ObjectStructureDefinition;
30  import org.kuali.student.core.dictionary.dto.RequiredConstraint;
31  import org.kuali.student.core.dictionary.dto.ValidCharsConstraint;
32  import org.kuali.student.core.dictionary.dto.WhenConstraint;
33  import org.kuali.student.core.messages.dto.Message;
34  import org.kuali.student.core.messages.service.MessageService;
35  import org.kuali.student.core.search.dto.SearchParam;
36  import org.kuali.student.core.search.dto.SearchRequest;
37  import org.kuali.student.core.search.dto.SearchResult;
38  import org.kuali.student.core.search.service.SearchDispatcher;
39  import org.kuali.student.core.validation.dto.ValidationResultInfo;
40  
41  public class DefaultValidatorImpl extends BaseAbstractValidator {
42      final static Logger LOG = Logger.getLogger(DefaultValidatorImpl.class);
43  
44      private MessageService messageService = null;
45  
46      private SearchDispatcher searchDispatcher;
47  
48      private String messageLocaleKey = "en";
49  
50      private String messageGroupKey = "validation";
51  
52      private DateParser dateParser = new ServerDateParser();
53  
54      private boolean serverSide = true;
55  
56      public MessageService getMessageService() {
57          return messageService;
58      }
59  
60      public void setMessageService(MessageService messageService) {
61          this.messageService = messageService;
62      }
63  
64      public String getMessageLocaleKey() {
65          return messageLocaleKey;
66      }
67  
68      public void setMessageLocaleKey(String messageLocaleKey) {
69          this.messageLocaleKey = messageLocaleKey;
70      }
71  
72      public String getMessageGroupKey() {
73          return messageGroupKey;
74      }
75  
76      public void setMessageGroupKey(String messageGroupKey) {
77          this.messageGroupKey = messageGroupKey;
78      }
79  
80      public void setDateParser(DateParser dateParser) {
81          this.dateParser = dateParser;
82      }
83  
84  	/**
85       * @return the serverSide
86       */
87      public boolean isServerSide() {
88          return serverSide;
89      }
90  
91      /**
92       * @param serverSide
93       *            the serverSide to set
94       */
95      public void setServerSide(boolean serverSide) {
96          this.serverSide = serverSide;
97      }
98  
99      /**
100      * @return the dateParser
101      */
102     public DateParser getDateParser() {
103         return dateParser;
104     }
105 
106     /**
107      * Validate Object and all its nested child objects for given type and state
108      *
109      * @param data
110      * @param objStructure
111      * @return
112      */
113     public List<ValidationResultInfo> validateObject(Object data, ObjectStructureDefinition objStructure) {
114 
115         Stack<String> elementStack = new Stack<String>();
116         return validateObject(data, objStructure, elementStack, true);
117     }
118 
119     private List<ValidationResultInfo> validateObject(Object data, ObjectStructureDefinition objStructure, Stack<String> elementStack, boolean isRoot) {
120 
121        List<ValidationResultInfo> results = new ArrayList<ValidationResultInfo>();
122 
123         ConstraintDataProvider dataProvider = new BeanConstraintDataProvider();
124         dataProvider.initialize(data);
125 
126         // Push object structure to the top of the stack
127         StringBuilder objXPathElement = new StringBuilder(dataProvider.getPath());
128 
129         if(!isRoot && !objXPathElement.toString().isEmpty()){
130         	elementStack.push(objXPathElement.toString());
131         }
132 
133         /*
134          * Do nothing if the object to be validated is not type/state or if the objectstructure with constraints is not
135          * provided
136          */
137         if (null == objStructure) {
138             return results;
139         }
140 
141         for (FieldDefinition f : objStructure.getAttributes()) {
142             List<ValidationResultInfo> l = validateField(f, objStructure, dataProvider, elementStack);
143 
144             results.addAll(l);
145 
146             // Use Custom Validators
147             if (f.getCustomValidatorClass() != null || f.isServerSide() && serverSide) {
148             	Validator customValidator = validatorFactory.getValidator(f.getCustomValidatorClass());
149             	if(customValidator==null){
150             		throw new RuntimeException("Custom Validator "+f.getCustomValidatorClass()+" was not configured in this context");
151             	}
152             	l = customValidator.validateObject(f,data, objStructure,elementStack);
153             	results.addAll(l);
154             }
155         }
156         if(!isRoot && !objXPathElement.toString().isEmpty()){
157         	elementStack.pop();
158         }
159 
160         /* All Field validations are returned right now */
161         // List<ValidationResultInfo> resultsBuffer = new
162         // ArrayList<ValidationResultInfo>();
163         // for (ValidationResultContainer vc : results) {
164         // if (skipFields.contains(vc.getElement()) == false) {
165         // resultsBuffer.add(vc);
166         // }
167         // }
168         // results = resultsBuffer;
169         return results;
170     }
171 
172     public List<ValidationResultInfo> validateField(FieldDefinition field, ObjectStructureDefinition objStruct, ConstraintDataProvider dataProvider, Stack<String> elementStack) {
173 
174         Object value = dataProvider.getValue(field.getName());
175         List<ValidationResultInfo> results = new ArrayList<ValidationResultInfo>();
176 
177         // Handle null values in field
178         if (value == null || "".equals(value.toString().trim())) {
179             processConstraint(results, field, objStruct, value, dataProvider, elementStack);
180             return results;
181         }
182 
183         /*
184          * For complex object structures only the following constraints apply 1. TypeStateCase 2. MinOccurs 3. MaxOccurs
185          */
186         if (DataType.COMPLEX.equals(field.getDataType())) {
187             ObjectStructureDefinition nestedObjStruct = null;
188 
189             if (null != field.getDataObjectStructure()) {
190                 nestedObjStruct = field.getDataObjectStructure();
191             }
192 
193             elementStack.push(field.getName());
194 
195             if (value instanceof Collection) {
196 
197                 String xPathForCollection = getElementXpath(elementStack) + "/*";
198 
199                 int i=0;
200                 for (Object o : (Collection<?>) value) {
201                 	elementStack.push(Integer.toString(i));
202                     processNestedObjectStructure(results, o, nestedObjStruct, field, elementStack);
203                     elementStack.pop();
204                     i++;
205                 }
206                 if (field.getMinOccurs() != null && field.getMinOccurs() > ((Collection<?>) value).size()) {
207                     ValidationResultInfo valRes = new ValidationResultInfo(xPathForCollection, value);
208                     valRes.setError(MessageUtils.interpolate(getMessage("validation.minOccurs"), toMap(field)));
209                     results.add(valRes);
210                 }
211 
212                 Integer maxOccurs = tryParse(field.getMaxOccurs());
213                 if (maxOccurs != null && maxOccurs < ((Collection<?>) value).size()) {
214                     ValidationResultInfo valRes = new ValidationResultInfo(xPathForCollection, value);
215                     valRes.setError(MessageUtils.interpolate(getMessage("validation.maxOccurs"), toMap(field)));
216                     results.add(valRes);
217                 }
218             } else {
219                 if (null != value) {
220                     processNestedObjectStructure(results, value, nestedObjStruct, field, elementStack);
221                 } else {
222                     if (field.getMinOccurs() != null && field.getMinOccurs() > 0) {
223                         ValidationResultInfo val = new ValidationResultInfo(getElementXpath(elementStack), value);
224                         val.setError(getMessage("validation.required"));
225                         results.add(val);
226                     }
227                 }
228             }
229 
230             elementStack.pop();
231 
232         } else { // If non complex data type
233 
234             if (value instanceof Collection) {
235 
236                 if(((Collection<?>)value).isEmpty()){
237                     processConstraint(results, field, objStruct, "", dataProvider, elementStack);
238                 }
239 
240             	int i = 0;
241                 for (Object o : (Collection<?>) value) {
242                 	elementStack.push(Integer.toBinaryString(i));
243                     processConstraint(results, field, objStruct, o, dataProvider, elementStack);
244                     elementStack.pop();
245                     i++;
246                 }
247 
248                 String xPath = getElementXpath(elementStack) + "/" + field.getName() + "/*";
249                 if (field.getMinOccurs() != null && field.getMinOccurs() > ((Collection<?>) value).size()) {
250                     ValidationResultInfo valRes = new ValidationResultInfo(xPath, value);
251                     valRes.setError(MessageUtils.interpolate(getMessage("validation.minOccurs"), toMap(field)));
252                     results.add(valRes);
253                 }
254 
255                 Integer maxOccurs = tryParse(field.getMaxOccurs());
256                 if (maxOccurs != null && maxOccurs < ((Collection<?>) value).size()) {
257                     ValidationResultInfo valRes = new ValidationResultInfo(xPath, value);
258                     valRes.setError(MessageUtils.interpolate(getMessage("validation.maxOccurs"), toMap(field)));
259                     results.add(valRes);
260                 }
261             } else {
262                 processConstraint(results, field, objStruct, value, dataProvider, elementStack);
263             }
264 
265         }
266         return results;
267     }
268 
269     protected Integer tryParse(String s) {
270         Integer result = null;
271         if (s != null) {
272             try {
273                 result = Integer.valueOf(s);
274             } catch (NumberFormatException e) {
275                 // do nothing
276             }
277         }
278         return result;
279     }
280 
281     protected void processNestedObjectStructure(List<ValidationResultInfo> results, Object value, ObjectStructureDefinition nestedObjStruct, FieldDefinition field, Stack<String> elementStack) {
282 
283         results.addAll(validateObject(value, nestedObjStruct, elementStack, false));
284 
285     }
286 
287     protected void processConstraint(List<ValidationResultInfo> valResults, FieldDefinition field, ObjectStructureDefinition objStructure, Object value, ConstraintDataProvider dataProvider, Stack<String> elementStack) {
288 
289         // Process Case Constraint
290         // Case Constraint are only evaluated on the field. Nested case constraints are currently ignored
291         Constraint caseConstraint = processCaseConstraint(valResults, field, objStructure, value, dataProvider, elementStack);
292 
293         Constraint constraint = (null != caseConstraint) ? caseConstraint : field;
294 
295         processBaseConstraints(valResults, constraint, field.getDataType(), field.getName(), value, elementStack);
296 
297         // Stop other checks if value is null
298         if (value == null || "".equals(value.toString().trim())) {
299             return;
300         }
301 
302         String elementPath = getElementXpath(elementStack) + "/" + field.getName();
303 
304         // Process Valid Chars
305         if (null != constraint.getValidChars()) {
306             ValidationResultInfo val = processValidCharConstraint(elementPath, constraint.getValidChars(), dataProvider, value);
307             if (null != val) {
308                 valResults.add(val);
309             }
310         }
311 
312         // Process Require Constraints (only if this field has value)
313         if (value != null && !"".equals(value.toString().trim())) {
314             if (null != constraint.getRequireConstraint() && constraint.getRequireConstraint().size() > 0) {
315                 for (RequiredConstraint rc : constraint.getRequireConstraint()) {
316                     ValidationResultInfo val = processRequireConstraint(elementPath, rc, field, objStructure, dataProvider);
317                     if (null != val) {
318                         valResults.add(val);
319                     }
320                 }
321             }
322         }
323 
324         // Process Occurs Constraint
325         if (null != constraint.getOccursConstraint() && constraint.getOccursConstraint().size() > 0) {
326             for (MustOccurConstraint oc : constraint.getOccursConstraint()) {
327                 ValidationResultInfo val = processOccursConstraint(elementPath, oc, field, objStructure, dataProvider);
328                 if (null != val) {
329                     valResults.add(val);
330                 }
331             }
332         }
333 
334         // Process lookup Constraint
335         if (null != constraint.getLookupDefinition()) {
336             processLookupConstraint(valResults, constraint.getLookupDefinition(), field, elementStack, dataProvider);
337         }
338     }
339 
340     protected ValidationResultInfo processRequireConstraint(String element, RequiredConstraint constraint, FieldDefinition field, ObjectStructureDefinition objStructure, ConstraintDataProvider dataProvider) {
341 
342         ValidationResultInfo val = null;
343 
344         String fieldName = constraint.getFieldPath();// TODO parse fieldname from here
345         Object fieldValue = dataProvider.getValue(fieldName);
346 
347         boolean result = true;
348 
349         if (fieldValue instanceof java.lang.String) {
350             result = hasText((String) fieldValue);
351         } else if (fieldValue instanceof Collection) {
352             result = (((Collection<?>) fieldValue).size() > 0);
353         } else {
354             result = (null != fieldValue) ? true : false;
355         }
356 
357         if (!result) {
358             Map<String, Object> rMap = new HashMap<String, Object>();
359             rMap.put("field1", field.getName());
360             rMap.put("field2", fieldName);
361             val = new ValidationResultInfo(element, fieldValue);
362             val.setError(MessageUtils.interpolate(getMessage("validation.requiresField"), rMap));
363         }
364 
365         return val;
366     }
367 
368     /**
369      * Process caseConstraint tag and sets any of the base constraint items if any of the when condition matches
370      *
371      * @param constraint
372      * @param caseConstraint
373      * @param field
374      */
375     protected Constraint processCaseConstraint(List<ValidationResultInfo> valResults, FieldDefinition field, ObjectStructureDefinition objStructure, Object value, ConstraintDataProvider dataProvider, Stack<String> elementStack) {
376 
377         CaseConstraint constraint = field.getCaseConstraint();
378 
379         if (null == constraint) {
380             return null;
381         }
382 
383         String operator = (hasText(constraint.getOperator())) ? constraint.getOperator() : "EQUALS";
384         FieldDefinition caseField = (hasText(constraint.getFieldPath())) ? ValidatorUtils.getField(constraint.getFieldPath(), objStructure) : null;
385 
386         // TODO: What happens when the field is not in the dataProvider?
387         Object fieldValue = (null != caseField) ? dataProvider.getValue(caseField.getName()) : value;
388         DataType fieldDataType = (null != caseField ? caseField.getDataType():null);
389 
390         // If fieldValue is null then skip Case check
391         if(null == fieldValue) {
392             return null;
393         }
394 
395         // Extract value for field Key
396         for (WhenConstraint wc : constraint.getWhenConstraint()) {
397 
398             List<Object> whenValueList = wc.getValues();
399 
400             for (Object whenValue : whenValueList) {
401                 if (ValidatorUtils.compareValues(fieldValue, whenValue, fieldDataType, operator, constraint.isCaseSensitive(), dateParser) && null != wc.getConstraint()) {
402                     return wc.getConstraint();
403                 }
404             }
405         }
406 
407         return null;
408     }
409 
410     protected ValidationResultInfo processValidCharConstraint(String element, ValidCharsConstraint vcConstraint, ConstraintDataProvider dataProvider, Object value) {
411 
412         ValidationResultInfo val = null;
413 
414         StringBuilder fieldValue = new StringBuilder();
415         String validChars = vcConstraint.getValue();
416 
417         fieldValue.append(ValidatorUtils.getString(value));
418 
419         int typIdx = validChars.indexOf(":");
420         String processorType = "regex";
421         if (-1 == typIdx) {
422             validChars = "[" + validChars + "]*";
423         } else {
424             processorType = validChars.substring(0, typIdx);
425             validChars = validChars.substring(typIdx + 1);
426         }
427 
428         if ("regex".equalsIgnoreCase(processorType)) {
429             if (fieldValue == null || !fieldValue.toString().matches(validChars)) {
430             	val = new ValidationResultInfo(element, fieldValue);
431                 if(vcConstraint.getLabelKey()!=null){
432                 	val.setError(getMessage(vcConstraint.getLabelKey()));
433                 }else{
434                 	val.setError(getMessage("validation.validCharsFailed"));
435                 }
436             }
437         }
438 
439         return val;
440     }
441 
442     /**
443      * Computes if all the filed required in the occurs clause are between the min and max
444      *
445      * @param valResults
446      * @param constraint
447      * @param field
448      * @param type
449      * @param state
450      * @param objStructure
451      * @param dataProvider
452      * @return
453      */
454     protected ValidationResultInfo processOccursConstraint(String element, MustOccurConstraint constraint, FieldDefinition field, ObjectStructureDefinition objStructure, ConstraintDataProvider dataProvider) {
455 
456         boolean result = false;
457         int trueCount = 0;
458 
459         ValidationResultInfo val = null;
460 
461         for (RequiredConstraint rc : constraint.getRequiredFields()) {
462             trueCount += (processRequireConstraint("", rc, field, objStructure, dataProvider) != null) ? 1 : 0;
463         }
464 
465         for (MustOccurConstraint oc : constraint.getOccurs()) {
466             trueCount += (processOccursConstraint("", oc, field, objStructure, dataProvider) != null) ? 1 : 0;
467         }
468 
469         result = (trueCount >= constraint.getMin() && trueCount <= constraint.getMax()) ? true : false;
470 
471         if (!result) {
472          // TODO: figure out what data should go here instead of null
473             val = new ValidationResultInfo(element, null);
474             val.setError(getMessage("validation.occurs"));
475         }
476 
477         return val;
478     }
479 
480     // TODO: Implement lookup constraint
481     protected void processLookupConstraint(List<ValidationResultInfo> valResults, LookupConstraint lookupConstraint, FieldDefinition field, Stack<String> elementStack, ConstraintDataProvider dataProvider) {
482         if (lookupConstraint == null) {
483             return;
484         }
485 
486         // Create search params based on the param mapping
487         List<SearchParam> params = new ArrayList<SearchParam>();
488         Object fieldValue = null;
489         for (CommonLookupParam paramMapping : lookupConstraint.getParams()) {
490             SearchParam param = new SearchParam();
491 
492             param.setKey(paramMapping.getKey());
493 
494             // If the value of the search param comes form another field then get it
495             if (paramMapping.getFieldPath() != null && !paramMapping.getFieldPath().isEmpty()) {
496                 fieldValue = dataProvider.getValue(paramMapping.getFieldPath());
497                 if (fieldValue instanceof String) {
498                     param.setValue((String) fieldValue);
499                 } else if (fieldValue instanceof List<?>) {
500                     param.setValue((List<String>) fieldValue);
501                 }
502             } else if (paramMapping.getDefaultValueString() != null) {
503                 param.setValue(paramMapping.getDefaultValueString());
504             } else {
505                 param.setValue(paramMapping.getDefaultValueList());
506             }
507             params.add(param);
508         }
509 
510         SearchRequest searchRequest = new SearchRequest();
511         searchRequest.setMaxResults(1);
512         searchRequest.setStartAt(0);
513         searchRequest.setNeededTotalResults(false);
514         searchRequest.setSearchKey(lookupConstraint.getSearchTypeId());
515         searchRequest.setParams(params);
516 
517         SearchResult searchResult = null;
518         try {
519             searchResult = searchDispatcher.dispatchSearch(searchRequest);
520         } catch (Exception e) {
521             LOG.info("Error calling Search", e);
522         }
523         if (searchResult == null || searchResult.getRows() == null || searchResult.getRows().isEmpty()) {
524             ValidationResultInfo val = new ValidationResultInfo(getElementXpath(elementStack) + "/" + field.getName(), fieldValue);
525             val.setError(getMessage("validation.lookup"));
526             valResults.add(val);
527         }
528     }
529 
530     protected void processBaseConstraints(List<ValidationResultInfo> valResults, Constraint constraint, DataType dataType, String name, Object value, Stack<String> elementStack) {
531 
532         if (value == null || "".equals(value.toString().trim())) {
533             if (constraint.getMinOccurs() != null && constraint.getMinOccurs() > 0) {
534                 ValidationResultInfo val = new ValidationResultInfo(getElementXpath(elementStack) + "/" + name, value);
535                 val.setError(getMessage("validation.required"));
536                 valResults.add(val);
537             }
538             return;
539         }
540 
541         String elementPath = getElementXpath(elementStack) + "/" + name;
542 
543         if (DataType.STRING.equals(dataType)) {
544             validateString(value, constraint, elementPath, valResults);
545         } else if (DataType.INTEGER.equals(dataType)) {
546             validateInteger(value, constraint, elementPath, valResults);
547         } else if (DataType.LONG.equals(dataType)) {
548             validateLong(value, constraint, elementPath, valResults);
549         } else if (DataType.DOUBLE.equals(dataType)) {
550             validateDouble(value, constraint, elementPath, valResults);
551         } else if (DataType.FLOAT.equals(dataType)) {
552             validateFloat(value, constraint, elementPath, valResults);
553         } else if (DataType.BOOLEAN.equals(dataType)) {
554             validateBoolean(value, constraint, elementPath, valResults);
555         } else if (DataType.DATE.equals(dataType)) {
556             validateDate(value, constraint, elementPath, valResults, dateParser);
557         }
558     }
559 
560     protected void validateBoolean(Object value, Constraint constraint, String element, List<ValidationResultInfo> results) {
561         if (!(value instanceof Boolean)) {
562             try {
563                 Boolean.valueOf(value.toString());
564             } catch (Exception e) {
565                 ValidationResultInfo val = new ValidationResultInfo(element, value);
566                 val.setError(getMessage("validation.mustBeBoolean"));
567                 results.add(val);
568             }
569         }
570     }
571 
572     protected void validateDouble(Object value, Constraint constraint, String element, List<ValidationResultInfo> results) {
573         Double v = null;
574 
575         ValidationResultInfo val = new ValidationResultInfo(element, value);
576 
577         if (value instanceof Number) {
578             v = ((Number) value).doubleValue();
579         } else {
580             try {
581                 v = Double.valueOf(value.toString());
582             } catch (Exception e) {
583                 val.setError(getMessage("validation.mustBeDouble"));
584             }
585         }
586 
587         if (val.isOk()) {
588             Double maxValue = ValidatorUtils.getDouble(constraint.getInclusiveMax());
589             Double minValue = ValidatorUtils.getDouble(constraint.getExclusiveMin());
590 
591             if (maxValue != null && minValue != null) {
592                 // validate range
593                 if (v > maxValue || v < minValue) {
594                     val.setError(MessageUtils.interpolate(getMessage("validation.outOfRange"), toMap(constraint)));
595                 }
596             } else if (maxValue != null) {
597                 if (v > maxValue) {
598                     val.setError(MessageUtils.interpolate(getMessage("validation.maxValueFailed"), toMap(constraint)));
599                 }
600             } else if (minValue != null) {
601                 if (v < minValue) {
602                     val.setError(MessageUtils.interpolate(getMessage("validation.minValueFailed"), toMap(constraint)));
603                 }
604             }
605         }
606 
607         if (!val.isOk()) {
608             results.add(val);
609         }
610     }
611 
612     protected void validateFloat(Object value, Constraint constraint, String element, List<ValidationResultInfo> results) {
613         Float v = null;
614 
615         ValidationResultInfo val = new ValidationResultInfo(element, value);
616         if (value instanceof Number) {
617             v = ((Number) value).floatValue();
618         } else {
619             try {
620                 v = Float.valueOf(value.toString());
621             } catch (Exception e) {
622                 val.setError(getMessage("validation.mustBeFloat"));
623             }
624         }
625 
626         if (val.isOk()) {
627             Float maxValue = ValidatorUtils.getFloat(constraint.getInclusiveMax());
628             Float minValue = ValidatorUtils.getFloat(constraint.getExclusiveMin());
629 
630             if (maxValue != null && minValue != null) {
631                 // validate range
632                 if (v > maxValue || v < minValue) {
633                     val.setError(MessageUtils.interpolate(getMessage("validation.outOfRange"), toMap(constraint)));
634                 }
635             } else if (maxValue != null) {
636                 if (v > maxValue) {
637                     val.setError(MessageUtils.interpolate(getMessage("validation.maxValueFailed"), toMap(constraint)));
638                 }
639             } else if (minValue != null) {
640                 if (v < minValue) {
641                     val.setError(MessageUtils.interpolate(getMessage("validation.minValueFailed"), toMap(constraint)));
642                 }
643             }
644         }
645 
646         if (!val.isOk()) {
647             results.add(val);
648         }
649     }
650 
651     protected void validateLong(Object value, Constraint constraint, String element, List<ValidationResultInfo> results) {
652         Long v = null;
653 
654         ValidationResultInfo val = new ValidationResultInfo(element, value);
655         if (value instanceof Number) {
656             v = ((Number) value).longValue();
657         } else {
658             try {
659                 v = Long.valueOf(value.toString());
660             } catch (Exception e) {
661                 val.setError(getMessage("validation.mustBeLong"));
662             }
663         }
664 
665         if (val.isOk()) {
666             Long maxValue = ValidatorUtils.getLong(constraint.getInclusiveMax());
667             Long minValue = ValidatorUtils.getLong(constraint.getExclusiveMin());
668 
669             if (maxValue != null && minValue != null) {
670                 // validate range
671                 if (v > maxValue || v < minValue) {
672                     val.setError(MessageUtils.interpolate(getMessage("validation.outOfRange"), toMap(constraint)));
673                 }
674             } else if (maxValue != null) {
675                 if (v > maxValue) {
676                     val.setError(MessageUtils.interpolate(getMessage("validation.maxValueFailed"), toMap(constraint)));
677                 }
678             } else if (minValue != null) {
679                 if (v < minValue) {
680                     val.setError(MessageUtils.interpolate(getMessage("validation.minValueFailed"), toMap(constraint)));
681                 }
682             }
683         }
684 
685         if (!val.isOk()) {
686             results.add(val);
687         }
688 
689     }
690 
691     protected void validateInteger(Object value, Constraint constraint, String element, List<ValidationResultInfo> results) {
692         Integer v = null;
693 
694         ValidationResultInfo val = new ValidationResultInfo(element, value);
695 
696         if (value instanceof Number) {
697             v = ((Number) value).intValue();
698         } else {
699             try {
700                 v = Integer.valueOf(value.toString());
701             } catch (Exception e) {
702                 val.setError(getMessage("validation.mustBeInteger"));
703             }
704         }
705 
706         if (val.isOk()) {
707             Integer maxValue = ValidatorUtils.getInteger(constraint.getInclusiveMax());
708             Integer minValue = ValidatorUtils.getInteger(constraint.getExclusiveMin());
709 
710             if (maxValue != null && minValue != null) {
711                 // validate range
712                 if (v > maxValue || v < minValue) {
713                     val.setError(MessageUtils.interpolate(getMessage("validation.outOfRange"), toMap(constraint)));
714                 }
715             } else if (maxValue != null) {
716                 if (v > maxValue) {
717                     val.setError(MessageUtils.interpolate(getMessage("validation.maxValueFailed"), toMap(constraint)));
718                 }
719             } else if (minValue != null) {
720                 if (v < minValue) {
721                     val.setError(MessageUtils.interpolate(getMessage("validation.minValueFailed"), toMap(constraint)));
722                 }
723             }
724         }
725 
726         if (!val.isOk()) {
727             results.add(val);
728         }
729     }
730 
731     protected void validateDate(Object value, Constraint constraint, String element, List<ValidationResultInfo> results, DateParser dateParser) {
732         ValidationResultInfo val = new ValidationResultInfo(element, value);
733 
734         Date v = null;
735 
736         if (value instanceof Date) {
737             v = (Date) value;
738         } else {
739             try {
740                 v = dateParser.parseDate(value.toString());
741             } catch (Exception e) {
742                 val.setError(getMessage("validation.mustBeDate"));
743             }
744         }
745 
746         if (val.isOk()) {
747             Date maxValue = ValidatorUtils.getDate(constraint.getInclusiveMax(), dateParser);
748             Date minValue = ValidatorUtils.getDate(constraint.getExclusiveMin(), dateParser);
749 
750             if (maxValue != null && minValue != null) {
751                 // validate range
752                 if (v.getTime() > maxValue.getTime() || v.getTime() < minValue.getTime()) {
753                     val.setError(MessageUtils.interpolate(getMessage("validation.outOfRange"), toMap(constraint)));
754                 }
755             } else if (maxValue != null) {
756                 if (v.getTime() > maxValue.getTime()) {
757                     val.setError(MessageUtils.interpolate(getMessage("validation.maxValueFailed"), toMap(constraint)));
758                 }
759             } else if (minValue != null) {
760                 if (v.getTime() < minValue.getTime()) {
761                     val.setError(MessageUtils.interpolate(getMessage("validation.minValueFailed"), toMap(constraint)));
762                 }
763             }
764         }
765 
766         if (!val.isOk()) {
767             results.add(val);
768         }
769     }
770 
771     protected void validateString(Object value, Constraint constraint, String element, List<ValidationResultInfo> results) {
772 
773         if (value == null) {
774             value = "";
775         }
776         String s = value.toString().trim();
777 
778         ValidationResultInfo val = new ValidationResultInfo(element, value);
779 
780         Integer maxLength = tryParse(constraint.getMaxLength());
781         if (maxLength != null && constraint.getMinLength() != null && constraint.getMinLength() > 0) {
782             if (s.length() > maxLength || s.length() < constraint.getMinLength()) {
783                 val.setError(MessageUtils.interpolate(getMessage("validation.lengthOutOfRange"), toMap(constraint)));
784             }
785         } else if (maxLength != null) {
786             if (s.length() > Integer.parseInt(constraint.getMaxLength())) {
787                 val.setError(MessageUtils.interpolate(getMessage("validation.maxLengthFailed"), toMap(constraint)));
788             }
789         } else if (constraint.getMinLength() != null && constraint.getMinLength() > 0) {
790             if (s.length() < constraint.getMinLength()) {
791                 val.setError(MessageUtils.interpolate(getMessage("validation.minLengthFailed"), toMap(constraint)));
792             }
793         }
794 
795         if (!val.isOk()) {
796             results.add(val);
797         }
798     }
799 
800     protected String getMessage(String messageId) {
801         if (null == messageService) {
802             return messageId;
803         }
804 
805         Message msg = messageService.getMessage(messageLocaleKey, messageGroupKey, messageId);
806 
807         return msg.getValue();
808     }
809 
810     protected String getElementXpath(Stack<String> elementStack) {
811         StringBuilder xPath = new StringBuilder();
812         Iterator<String> itr = elementStack.iterator();
813         while (itr.hasNext()) {
814             xPath.append(itr.next());
815             if(itr.hasNext()){
816             	xPath.append("/");
817             }
818         }
819 
820         return xPath.toString();
821     }
822 
823     /*
824      * Homemade has text so we dont need outside libs.
825      */
826     protected boolean hasText(String string) {
827 
828         if (string == null || string.length() < 1) {
829             return false;
830         }
831         int stringLength = string.length();
832 
833         for (int i = 0; i < stringLength; i++) {
834             char currentChar = string.charAt(i);
835             if (' ' != currentChar || '\t' != currentChar || '\n' != currentChar) {
836                 return true;
837             }
838         }
839 
840         return false;
841     }
842 
843     protected Map<String, Object> toMap(Constraint c) {
844         Map<String, Object> result = new HashMap<String, Object>();
845         result.put("minOccurs", c.getMinOccurs());
846         result.put("maxOccurs", c.getMaxOccurs());
847         result.put("minLength", c.getMinLength());
848         result.put("maxLength", c.getMaxLength());
849         result.put("minValue", c.getExclusiveMin());
850         result.put("maxValue", c.getInclusiveMax());
851         // result.put("dataType", c.getDataType());
852 
853         return result;
854     }
855 
856     public SearchDispatcher getSearchDispatcher() {
857         return searchDispatcher;
858     }
859 
860     public void setSearchDispatcher(SearchDispatcher searchDispatcher) {
861         this.searchDispatcher = searchDispatcher;
862     }
863 
864 	@Override
865 	public List<ValidationResultInfo> validateObject(FieldDefinition field,
866 			Object o, ObjectStructureDefinition objStructure,Stack<String> elementStack) {
867 		return null;
868 	}
869 }