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