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