View Javadoc

1   /**
2    * Copyright 2010 The Kuali Foundation Licensed under the
3    * Educational Community License, Version 2.0 (the "License"); you may
4    * not use this file except in compliance with the License. You may
5    * obtain a copy of the License at
6    *
7    * http://www.osedu.org/licenses/ECL-2.0
8    *
9    * Unless required by applicable law or agreed to in writing,
10   * software distributed under the License is distributed on an "AS IS"
11   * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12   * or implied. See the License for the specific language governing
13   * permissions and limitations under the License.
14   */
15  
16  package org.kuali.student.r1.common.validator.old;
17  
18  import org.kuali.student.common.util.MessageUtils;
19  import org.kuali.student.r1.common.dictionary.old.dto.*;
20  import org.kuali.student.r2.common.dto.LocaleInfo;
21  import org.kuali.student.r2.common.dto.ValidationResultInfo;
22  import org.kuali.student.r2.common.exceptions.*;
23  import org.kuali.student.r2.common.messages.dto.MessageInfo;
24  import org.kuali.student.r2.common.messages.service.MessageService;
25  import org.kuali.student.r2.common.util.ContextUtils;
26  
27  import java.util.*;
28  
29  @Deprecated
30  public class Validator {
31  
32  	//TODO: Change this to 'default' when the change is made in xml
33  	private static final String DEFAULT_STATE = "*";
34  
35  	private static final String UNBOUNDED_CHECK = null;
36  
37  	private MessageService messageService = null;
38  
39  	private String messageLocaleKey = "en";
40  
41  	private String messageGroupKey = "validation";
42  
43  	private DateParser dateParser = new ServerDateParser();
44  
45  	private boolean serverSide = true;
46  
47  	public MessageService getMessageService() {
48  		return messageService;
49  	}
50  
51  	public void setMessageService(MessageService messageService) {
52  		this.messageService = messageService;
53  	}
54  
55  	public String getMessageLocaleKey() {
56  		return messageLocaleKey;
57  	}
58  
59  	public void setMessageLocaleKey(String messageLocaleKey) {
60  		this.messageLocaleKey = messageLocaleKey;
61  	}
62  
63  	public String getMessageGroupKey() {
64  		return messageGroupKey;
65  	}
66  
67  	public void setMessageGroupKey(String messageGroupKey) {
68  		this.messageGroupKey = messageGroupKey;
69  	}
70  
71  	public void setDateParser(DateParser dateParser) {
72  		this.dateParser = dateParser;
73  	}
74  
75  	/**
76  	 * @return the serverSide
77  	 */
78  	public boolean isServerSide() {
79  		return serverSide;
80  	}
81  
82  	/**
83  	 * @param serverSide
84  	 *            the serverSide to set
85  	 */
86  	public void setServerSide(boolean serverSide) {
87  		this.serverSide = serverSide;
88  	}
89  
90  	/**
91  	 * @return the dateParser
92  	 */
93  	public DateParser getDateParser() {
94  		return dateParser;
95  	}
96  
97  
98  
99  
100 	/**
101 	 * Validate Object and all its nested child objects for given type and state
102 	 *
103 	 * @param data
104 	 * @param objStructure
105 	 * @return
106 	 */
107 	public List<ValidationResultInfo> validateTypeStateObject(Object data,
108 			ObjectStructure objStructure) {
109 
110 		Stack<String> elementStack = new Stack<String>();
111 		return validateTypeStateObject(data, objStructure, elementStack);
112 	}
113 
114 	private List<ValidationResultInfo> validateTypeStateObject(Object data,
115 			ObjectStructure objStructure, Stack<String> elementStack) {
116 
117 		List<ValidationResultInfo> results = new ArrayList<ValidationResultInfo>();
118 
119 		ConstraintDataProvider dataProvider = new BeanConstraintDataProvider();
120 		dataProvider.initialize(data);
121 
122 		boolean isTypeStateObject = (dataProvider.hasField("type") && dataProvider
123 				.hasField("state"));
124 
125 		// Push object structure to the top of the stack
126 		StringBuilder objXPathElement = new StringBuilder(dataProvider
127 				.getPath());
128 		if (null != dataProvider.getObjectId()) {
129 			objXPathElement.append("[id='" + dataProvider.getObjectId() + "']");
130 		} else {
131 			objXPathElement.append("[id='null']");
132 		}
133 		elementStack.push(objXPathElement.toString());
134 
135 		/*
136 		 * Do nothing if the object to be validated is not type/state or if the
137 		 * objectstructure with constraints is not provided
138 		 */
139 		if (!isTypeStateObject || null == objStructure) {
140 			return results;
141 		}
142 
143 		// Validate with the matching Type/State
144 		List<Type> types = objStructure.getType();
145 		for (Type t : types) {
146 			if (t.getKey().equalsIgnoreCase(
147 					(String) dataProvider.getValue("type"))) {
148 				for (State s : t.getState()) {
149 					if (s.getKey().equalsIgnoreCase(
150 							(String) dataProvider.getValue("state"))
151 							|| s.getKey().equalsIgnoreCase(DEFAULT_STATE)) {
152 						for (Field f : s.getField()) {
153 							List<ValidationResultInfo> l = validateField(f, t,
154 									s, objStructure, dataProvider, elementStack);
155 							results.addAll(l);
156 						}
157 						break;
158 					}
159 				}
160 				break;
161 			}
162 		}
163 		elementStack.pop();
164 
165 		/* All Field validations are returned right now */
166 		// List<ValidationResultInfo> resultsBuffer = new
167 		// ArrayList<ValidationResultInfo>();
168 		// for (ValidationResultContainer vc : results) {
169 		// if (skipFields.contains(vc.getElement()) == false) {
170 		// resultsBuffer.add(vc);
171 		// }
172 		// }
173 		// results = resultsBuffer;
174 		return results;
175 	}
176 
177 	public List<ValidationResultInfo> validateField(Field field, Type type,
178 			State state, ObjectStructure objStruct,
179 			ConstraintDataProvider dataProvider, Stack<String> elementStack) {
180 
181 		Object value = dataProvider.getValue(field.getKey());
182 		List<ValidationResultInfo> results = new ArrayList<ValidationResultInfo>();
183 
184 		ConstraintDescriptor cd = field.getConstraintDescriptor();
185 
186 		// Handle null values in field
187 		if (value == null || "".equals(value.toString().trim())) {
188 			if (isNullable(field) == false) {
189 				ValidationResultInfo valInfo = new ValidationResultInfo(
190 						getElementXpath(elementStack) + field.getKey());
191 				valInfo.setError(getMessage("validation.required"));
192 				results.add(valInfo);
193 			}
194 			return results;
195 		}
196 
197 		/*
198 		 * For complex object structures only the following constraints apply
199 		 * 1. TypeStateCase
200 		 * 2. MinOccurs
201 		 * 3. MaxOccurs
202 		 */
203 		if ("complex"
204 				.equalsIgnoreCase(field.getFieldDescriptor().getDataType())) {
205 			ObjectStructure nestedObjStruct = null;
206 
207 			if(null != field.getFieldDescriptor().getObjectStructure()) {
208 				nestedObjStruct = field.getFieldDescriptor()
209 				.getObjectStructure();
210 			}
211 			else if (hasText(field.getFieldDescriptor().getObjectStructureRef())) {
212                 throw new UnsupportedOperationException("Empty If Statement: No action defined for this statement ");
213 				//TODO: Setup a mechanism to retrive referenced object structures
214 //				nestedObjStruct = setupFactory.getObjectStructure(field
215 //						.getFieldDescriptor().getObjectStructureRef());
216 			}
217 
218 			BaseConstraintBean bcb = new BaseConstraintBean();
219 			if(null != cd) {
220 				for(ConstraintSelector constraint: cd.getConstraint()) {
221 					computeBaseConstraints(constraint, bcb, field);
222 				}
223 			}
224 
225 			elementStack.push(field.getKey());
226 
227 			if (value instanceof Collection<?>) {
228 
229 				String xPath = getElementXpath(elementStack) + "/";
230 
231 				for (Object o : (Collection<?>) value) {
232 					processNestedObjectStructure(results, o, nestedObjStruct,
233 							field, elementStack);
234 				}
235 				if (bcb.minOccurs > ((Collection<?>) value).size()) {
236 					ValidationResultInfo valRes = new ValidationResultInfo(
237 							xPath);
238 					valRes.setError(MessageUtils.interpolate(getMessage("validation.minOccurs"), bcb.toMap()));
239 					results.add(valRes);
240 				}
241 
242 				Integer maxOccurs = tryParse(bcb.maxOccurs);
243 				if (maxOccurs != null
244 						&& maxOccurs < ((Collection<?>) value).size()) {
245 					ValidationResultInfo valRes = new ValidationResultInfo(
246 							xPath);
247 					valRes.setError(MessageUtils.interpolate(getMessage("validation.maxOccurs"), bcb.toMap()));
248 					results.add(valRes);
249 				}
250 			} else {
251 				if(null != value) {
252 					processNestedObjectStructure(results, value, nestedObjStruct,
253 							field, elementStack);
254 				} else {
255 					if (bcb.minOccurs != null && bcb.minOccurs > 0) {
256 						ValidationResultInfo val = new ValidationResultInfo(
257 								getElementXpath(elementStack) + "[value='null']/");
258 						val.setError(getMessage("validation.required"));
259 						results.add(val);
260 					}
261 				}
262 			}
263 
264 			elementStack.pop();
265 
266 		} else { // If non complex data type
267 			if (null != cd) {
268 				List<ConstraintSelector> constraints = cd.getConstraint();
269 
270 				if (value instanceof Collection<?>) {
271 					BaseConstraintBean bcb = new BaseConstraintBean();
272 					for (Object o : (Collection<?>) value) {
273 						for (ConstraintSelector constraint : constraints) {
274 							processConstraint(results, constraint, field, type,
275 									state, objStruct, o, dataProvider, bcb, elementStack);
276 						}
277 						processBaseConstraints(results, bcb, field, o, elementStack);
278 						if(!bcb.initialized) {
279 							bcb.initialized = true;
280 						}
281 					}
282 
283 					String xPath = getElementXpath(elementStack) + field.getKey() + "/";
284 					if (bcb.minOccurs > ((Collection<?>) value).size()) {
285 						ValidationResultInfo valRes = new ValidationResultInfo(
286 								xPath);
287 						valRes.setError(MessageUtils.interpolate(getMessage("validation.minOccurs"), bcb.toMap()));
288 						results.add(valRes);
289 					}
290 
291 					Integer maxOccurs = tryParse(bcb.maxOccurs);
292 					if (maxOccurs != null
293 							&& maxOccurs < ((Collection<?>) value).size()) {
294 						ValidationResultInfo valRes = new ValidationResultInfo(
295 								xPath);
296 						valRes.setError(MessageUtils.interpolate(getMessage("validation.maxOccurs"), bcb.toMap()));
297 						results.add(valRes);
298 					}
299 				} else {
300 					BaseConstraintBean bcb = new BaseConstraintBean();
301 					for (ConstraintSelector constraint : constraints) {
302 						processConstraint(results, constraint, field, type,
303 								state, objStruct, value, dataProvider, bcb, elementStack);
304 					}
305 					processBaseConstraints(results, bcb, field, value, elementStack);
306 				}
307 			}
308 		}
309 		return results;
310 	}
311 
312 	private boolean isNullable(Field field) {
313 		ConstraintDescriptor cd = field.getConstraintDescriptor();
314 		if (null != cd) {
315 			List<ConstraintSelector> constraintList = cd.getConstraint();
316 			for (ConstraintSelector cs : constraintList) {
317 				if (cs.getMinOccurs() != null && cs.getMinOccurs() > 0) {
318 					return false;
319 				}
320 			}
321 		}
322 		return true;
323 	}
324 
325 	private Integer tryParse(String s) {
326 		Integer result = null;
327 		if (s != null) {
328 			try {
329 				result = Integer.valueOf(s);
330 			} catch (NumberFormatException e) {
331 				// do nothing
332 			}
333 		}
334 		return result;
335 	}
336 
337 	private void processNestedObjectStructure(
338 			List<ValidationResultInfo> results, Object value,
339 			ObjectStructure nestedObjStruct, Field field, Stack<String> elementStack) {
340 
341 		results.addAll(validateTypeStateObject(value, nestedObjStruct, elementStack));
342 
343 		ConstraintDescriptor cd = field.getConstraintDescriptor();
344 		if (null != cd) {
345 			ConstraintSelector cs = cd.getConstraint().get(0);
346 			TypeStateCaseConstraint tscs = cs.getTypeStateCaseConstraint();
347 			if (null != tscs) {
348                 throw new UnsupportedOperationException("Empty If Statement: No action defined for this statement ");
349 				// ValidationResultContainer vc = new
350 				// ValidationResultContainer();
351 				// processTypeStateCaseConstraint(vc);
352 				// results.add(vc);k
353 			}
354 		}
355 	}
356 
357 	private void computeBaseConstraints(ConstraintSelector constraint,
358 			BaseConstraintBean bcb, Field field) {
359 		if (null != constraint.getMinLength()) {
360 			bcb.minLength = (bcb.minLength > constraint.getMinLength()) ? bcb.minLength
361 					: constraint.getMinLength();
362 		}
363 
364 		if (null != constraint.getMinOccurs()) {
365 			bcb.minOccurs = (bcb.minOccurs > constraint.getMinOccurs()) ? bcb.minOccurs
366 					: constraint.getMinOccurs();
367 		}
368 
369 		if (null != constraint.getMinValue()) {
370 			bcb.minValue = (null == bcb.minValue || ValidatorUtils
371 					.compareValues(bcb.minValue, constraint.getMinValue(),
372 							field.getFieldDescriptor().getDataType(),
373 							"GREATER_THAN", dateParser)) ? constraint
374 					.getMinValue() : bcb.minValue;
375 		}
376 
377 		if (null != constraint.getMaxValue()) {
378 			bcb.maxValue = (null == bcb.maxValue || ValidatorUtils
379 					.compareValues(bcb.maxValue, constraint.getMaxValue(),
380 							field.getFieldDescriptor().getDataType(),
381 							"LESS_THAN", dateParser)) ? constraint
382 					.getMaxValue() : bcb.maxValue;
383 		}
384 
385 		if (hasText(constraint.getMaxLength())) {
386 			Integer maxLength = tryParse(bcb.maxLength);
387 			Integer constraintMaxLength = tryParse(constraint.getMaxLength());
388 			if (maxLength == null) {
389 				bcb.maxLength = constraint.getMaxLength();
390 			} else if (constraintMaxLength != null) {
391 				if (constraintMaxLength > maxLength) {
392 					bcb.maxLength = constraint.getMaxLength();
393 				}
394 			}
395 		}
396 
397 		if (hasText(constraint.getMaxOccurs())) {
398 			Integer maxOccurs = tryParse(bcb.maxOccurs);
399 			Integer constraintMaxOccurs = tryParse(constraint.getMaxOccurs());
400 			if (maxOccurs == null) {
401 				bcb.maxOccurs = constraint.getMaxOccurs();
402 			} else if (constraintMaxOccurs != null) {
403 				if (constraintMaxOccurs > maxOccurs) {
404 					bcb.maxOccurs = constraint.getMaxOccurs();
405 				}
406 			}
407 		}
408 	}
409 
410 	private void processConstraint(List<ValidationResultInfo> valResults,
411 			ConstraintSelector constraint, Field field, Type type, State state,
412 			ObjectStructure objStructure, Object value,
413 			ConstraintDataProvider dataProvider, BaseConstraintBean bcb,
414 			Stack<String> elementStack) {
415 
416 		// If constraint is only to be processed on server side
417 		if (hasText(constraint.getClassName()) || constraint.isServerSide()
418 				&& !serverSide) {
419 			return;
420 		}
421 
422 		String elementPath = getElementXpath(elementStack) + field.getKey()
423 				+ "[value='" + value.toString() + "']/";
424 
425 		if(!bcb.initialized) {
426 			computeBaseConstraints(constraint, bcb, field);
427 		}
428 
429 		// Process Valid Chars
430 		if (null != constraint.getValidChars()) {
431 			ValidationResultInfo val = processValidCharConstraint(elementPath,
432 					constraint.getValidChars(), dataProvider, value);
433 			if (null != val) {
434 				valResults.add(val);
435 			}
436 		}
437 
438 		// Process Require Constraints (only if this field has value)
439 		if (value != null && !"".equals(value.toString().trim())) {
440 			if (null != constraint.getRequireConstraint()
441 					&& constraint.getRequireConstraint().size() > 0) {
442 				for (RequireConstraint rc : constraint.getRequireConstraint()) {
443 					ValidationResultInfo val = processRequireConstraint(
444 							elementPath, rc, field, objStructure, dataProvider);
445 					if (null != val) {
446 						valResults.add(val);
447 					}
448 				}
449 			}
450 		}
451 
452 		// Process Occurs Constraint
453 		if (null != constraint.getOccursConstraint()
454 				&& constraint.getOccursConstraint().size() > 0) {
455 			for (OccursConstraint oc : constraint.getOccursConstraint()) {
456 				ValidationResultInfo val = processOccursConstraint(elementPath,
457 						oc, field, type, state, objStructure, dataProvider);
458 				if (null != val) {
459 					valResults.add(val);
460 				}
461 			}
462 		}
463 
464 		// Process lookup Constraint
465 		if (null != constraint.getLookupConstraint()
466 				&& constraint.getLookupConstraint().size() > 0) {
467 			for (LookupConstraint lc : constraint.getLookupConstraint()) {
468 				processLookupConstraint(valResults);
469 			}
470 		}
471 
472 		// Process Case Constraint
473 		if (null != constraint.getCaseConstraint()
474 				&& constraint.getCaseConstraint().size() > 0) {
475 			for (CaseConstraint cc : constraint.getCaseConstraint()) {
476 				processCaseConstraint(valResults, cc, field, type, state,
477 						objStructure, value, dataProvider, bcb, elementStack);
478 			}
479 		}
480 	}
481 
482 	private ValidationResultInfo processRequireConstraint(String element,
483 			RequireConstraint constraint, Field field,
484 			ObjectStructure objStructure, ConstraintDataProvider dataProvider) {
485 
486 		ValidationResultInfo val = null;
487 
488 		String fieldName = constraint.getField();
489 		Object fieldValue = dataProvider.getValue(fieldName);
490 
491 		boolean result = true;
492 
493 		if (fieldValue instanceof java.lang.String) {
494 			result = hasText((String) fieldValue);
495 		} else if (fieldValue instanceof Collection<?>) {
496 			result = (((Collection<?>) fieldValue).size() > 0);
497 		} else {
498 			result = (null != fieldValue) ? true : false;
499 		}
500 
501 		if (!result) {
502 			Map<String, Object> rMap = new HashMap<String, Object>();
503 			rMap.put("field1", field.getKey());
504 			rMap.put("field2", fieldName);
505 			val = new ValidationResultInfo(element);
506 			val.setError(MessageUtils.interpolate(
507 					getMessage("validation.requiresField"), rMap));
508 		}
509 
510 		return val;
511 	}
512 
513 	/**
514 	 * Process caseConstraint tag and sets any of the base constraint items if
515 	 * any of the when condition matches
516 	 *
517 	 * @param bcb
518 	 * @param constraint
519 	 * @param field
520 	 */
521 	private void processCaseConstraint(List<ValidationResultInfo> valResults,
522 			CaseConstraint constraint, Field field, Type type, State state,
523 			ObjectStructure objStructure, Object value,
524 			ConstraintDataProvider dataProvider, BaseConstraintBean bcb,
525 			Stack<String> elementStack) {
526 
527 		String operator = (hasText(constraint.getOperator())) ? constraint
528 				.getOperator() : "EQUALS";
529 		Field caseField = (hasText(constraint.getField())) ? ValidatorUtils
530 				.getField(constraint.getField(), objStructure, type.getKey(),
531 						state.getKey()) : null;
532 
533 		// TODO: What happens when the field is not in the dataProvider?
534 		Object fieldValue = (null != caseField) ? dataProvider
535 				.getValue(caseField.getKey()) : value;
536 
537 		// Extract value for field Key
538 		for (WhenConstraint wc : constraint.getWhenConstraint()) {
539 			String whenValue = wc.getValue();
540 
541 			if (ValidatorUtils.compareValues(fieldValue, whenValue, caseField
542 					.getFieldDescriptor().getDataType(), operator, dateParser)) {
543 				processConstraint(valResults, wc.getConstraint(), field, type,
544 						state, objStructure, value, dataProvider, bcb,
545 						elementStack);
546 			}
547 		}
548 	}
549 
550 	private ValidationResultInfo processValidCharConstraint(String element,
551 			ValidCharsConstraint vcConstraint,
552 			ConstraintDataProvider dataProvider, Object value) {
553 
554 		ValidationResultInfo val = null;
555 
556 		StringBuilder fieldValue = new StringBuilder();
557 		String validChars = vcConstraint.getValue();
558 		String fields = vcConstraint.getFields();
559 
560 		if (hasText(fields)) {
561 			String separator = vcConstraint.getSeparator();
562 			String[] fieldNameList = fields.split(",");
563 
564 			int sz = fieldNameList.length;
565 
566 			for (String fieldName : fieldNameList) {
567 				Object v = dataProvider.getValue(fieldName);
568 				fieldValue.append(ValidatorUtils.getString(v));
569 
570 				if (--sz > 0) {
571 					fieldValue.append(separator);
572 				}
573 			}
574 		} else {
575 			fieldValue.append(ValidatorUtils.getString(value));
576 		}
577 
578 		int typIdx = validChars.indexOf(":");
579 		String processorType = "regex";
580 		if (-1 == typIdx) {
581 			validChars = "[" + validChars + "]*";
582 		} else {
583 			processorType = validChars.substring(0, typIdx);
584 			validChars = validChars.substring(typIdx + 1);
585 		}
586 
587 		// TODO: Allow different processing based on the label
588 		if ("regex".equalsIgnoreCase(processorType)) {
589 			// if (!Pattern.matches(validChars, fieldValue.toString())) {
590 			if (fieldValue == null) {
591 				val = new ValidationResultInfo(element);
592 				val.setError(getMessage("validation.validCharsFailed"));
593 			} else if (fieldValue != null
594 					&& !fieldValue.toString().matches(validChars)) {
595 				val = new ValidationResultInfo(element);
596 				val.setError(getMessage("validation.validCharsFailed"));
597 			}
598 		}
599 
600 		return val;
601 	}
602 
603 	/**
604 	 * Computes if all the filed required in the occurs clause are between the
605 	 * min and max
606 	 *
607 	 * @param element
608 	 * @param constraint
609 	 * @param field
610 	 * @param type
611 	 * @param state
612 	 * @param objStructure
613 	 * @param dataProvider
614 	 * @return
615 	 */
616 	private ValidationResultInfo processOccursConstraint(String element,
617 			OccursConstraint constraint, Field field, Type type, State state,
618 			ObjectStructure objStructure, ConstraintDataProvider dataProvider) {
619 
620 		boolean result = false;
621 		int trueCount = 0;
622 
623 		ValidationResultInfo val = null;
624 
625 		for (RequireConstraint rc : constraint.getRequire()) {
626 			trueCount += (processRequireConstraint("", rc, field, objStructure,
627 					dataProvider) != null) ? 1 : 0;
628 		}
629 
630 		for (OccursConstraint oc : constraint.getOccurs()) {
631 			trueCount += (processOccursConstraint("", oc, field, type, state,
632 					objStructure, dataProvider) != null) ? 1 : 0;
633 		}
634 
635 		result = (trueCount >= constraint.getMin() && trueCount <= constraint
636 				.getMax()) ? true : false;
637 
638 		if (!result) {
639 			val = new ValidationResultInfo(element);
640 			val.setError(getMessage("validation.occurs"));
641 		}
642 
643 		return val;
644 	}
645 
646 	// TODO: Implement lookup constraint
647 	private void processLookupConstraint(List<ValidationResultInfo> valResults) {
648 	}
649 
650 	// TODO: Implement TypeStateCase constraint
651 	private void processTypeStateCaseConstraint(
652 			List<ValidationResultInfo> valResults) {
653 	}
654 
655 	private void processBaseConstraints(List<ValidationResultInfo> valResults,
656 			BaseConstraintBean bcb, Field field, Object value,
657 			Stack<String> elementStack) {
658 
659 		String dataType = field.getFieldDescriptor().getDataType();
660 
661 		if (value == null || "".equals(value.toString().trim())) {
662 			if (bcb.minOccurs != null && bcb.minOccurs > 0) {
663 				ValidationResultInfo val = new ValidationResultInfo(
664 						getElementXpath(elementStack) + field.getKey()
665 								+ "[value='null']/");
666 				val.setError(getMessage("validation.required"));
667 				valResults.add(val);
668 				return;
669 			}
670 		}
671 
672 		String elementPath = getElementXpath(elementStack) + field.getKey()
673 				+ "[value='" + value.toString() + "']/";
674 
675 		if ("string".equalsIgnoreCase(dataType)) {
676 			validateString(value, bcb, elementPath, valResults);
677 		} else if ("integer".equalsIgnoreCase(dataType)) {
678 			validateInteger(value, bcb, elementPath, valResults);
679 		} else if ("long".equalsIgnoreCase(dataType)) {
680 			validateLong(value, bcb, elementPath, valResults);
681 		} else if ("double".equalsIgnoreCase(dataType)) {
682 			validateDouble(value, bcb, elementPath, valResults);
683 		} else if ("float".equalsIgnoreCase(dataType)) {
684 			validateFloat(value, bcb, elementPath, valResults);
685 		} else if ("boolean".equalsIgnoreCase(dataType)) {
686 			validateBoolean(value, bcb, elementPath, valResults);
687 		} else if ("date".equalsIgnoreCase(dataType)) {
688 			validateDate(value, bcb, elementPath, valResults, dateParser);
689 		}
690 	}
691 
692 	private void validateBoolean(Object value, BaseConstraintBean bcb,
693 			String element, List<ValidationResultInfo> results) {
694 		if (!(value instanceof Boolean)) {
695 			try {
696 				Boolean.valueOf(value.toString());
697 			} catch (Exception e) {
698 				ValidationResultInfo val = new ValidationResultInfo(element);
699 				val.setError(getMessage("validation.mustBeBoolean"));
700 				results.add(val);
701 			}
702 		}
703 	}
704 
705 	private void validateDouble(Object value, BaseConstraintBean bcb,
706 			String element, List<ValidationResultInfo> results) {
707 		Double v = null;
708 
709 		ValidationResultInfo val = new ValidationResultInfo(element);
710 
711 		if (value instanceof Number) {
712 			v = ((Number) value).doubleValue();
713 		} else {
714 			try {
715 				v = Double.valueOf(value.toString());
716 			} catch (Exception e) {
717 				val.setError(getMessage("validation.mustBeDouble"));
718 			}
719 		}
720 
721 		if (val.isOk()) {
722 			Double maxValue = ValidatorUtils.getDouble(bcb.maxValue);
723 			Double minValue = ValidatorUtils.getDouble(bcb.minValue);
724 
725 			if (maxValue != null && minValue != null) {
726 				// validate range
727 				if (v > maxValue || v < minValue) {
728 					val.setError(MessageUtils.interpolate(
729 							getMessage("validation.outOfRange"), bcb.toMap()));
730 				}
731 			} else if (maxValue != null) {
732 				if (v > maxValue) {
733 					val.setError(MessageUtils.interpolate(
734 							getMessage("validation.maxValueFailed"), bcb
735 									.toMap()));
736 				}
737 			} else if (minValue != null) {
738 				if (v < minValue) {
739 					val.setError(MessageUtils.interpolate(
740 							getMessage("validation.minValueFailed"), bcb
741 									.toMap()));
742 				}
743 			}
744 		}
745 
746 		if (!val.isOk()) {
747 			results.add(val);
748 		}
749 	}
750 
751 	private void validateFloat(Object value, BaseConstraintBean bcb,
752 			String element, List<ValidationResultInfo> results) {
753 		Float v = null;
754 
755 		ValidationResultInfo val = new ValidationResultInfo(element);
756 		if (value instanceof Number) {
757 			v = ((Number) value).floatValue();
758 		} else {
759 			try {
760 				v = Float.valueOf(value.toString());
761 			} catch (Exception e) {
762 				val.setError(getMessage("validation.mustBeFloat"));
763 			}
764 		}
765 
766 		if (val.isOk()) {
767 			Float maxValue = ValidatorUtils.getFloat(bcb.maxValue);
768 			Float minValue = ValidatorUtils.getFloat(bcb.minValue);
769 
770 			if (maxValue != null && minValue != null) {
771 				// validate range
772 				if (v > maxValue || v < minValue) {
773 					val.setError(MessageUtils.interpolate(
774 							getMessage("validation.outOfRange"), bcb.toMap()));
775 				}
776 			} else if (maxValue != null) {
777 				if (v > maxValue) {
778 					val.setError(MessageUtils.interpolate(
779 							getMessage("validation.maxValueFailed"), bcb
780 									.toMap()));
781 				}
782 			} else if (minValue != null) {
783 				if (v < minValue) {
784 					val.setError(MessageUtils.interpolate(
785 							getMessage("validation.minValueFailed"), bcb
786 									.toMap()));
787 				}
788 			}
789 		}
790 
791 		if (!val.isOk()) {
792 			results.add(val);
793 		}
794 	}
795 
796 	private void validateLong(Object value, BaseConstraintBean bcb,
797 			String element, List<ValidationResultInfo> results) {
798 		Long v = null;
799 
800 		ValidationResultInfo val = new ValidationResultInfo(element);
801 		if (value instanceof Number) {
802 			v = ((Number) value).longValue();
803 		} else {
804 			try {
805 				v = Long.valueOf(value.toString());
806 			} catch (Exception e) {
807 				val.setError(getMessage("validation.mustBeLong"));
808 			}
809 		}
810 
811 		if (val.isOk()) {
812 			Long maxValue = ValidatorUtils.getLong(bcb.maxValue);
813 			Long minValue = ValidatorUtils.getLong(bcb.minValue);
814 
815 			if (maxValue != null && minValue != null) {
816 				// validate range
817 				if (v > maxValue || v < minValue) {
818 					val.setError(MessageUtils.interpolate(
819 							getMessage("validation.outOfRange"), bcb.toMap()));
820 				}
821 			} else if (maxValue != null) {
822 				if (v > maxValue) {
823 					val.setError(MessageUtils.interpolate(
824 							getMessage("validation.maxValueFailed"), bcb
825 									.toMap()));
826 				}
827 			} else if (minValue != null) {
828 				if (v < minValue) {
829 					val.setError(MessageUtils.interpolate(
830 							getMessage("validation.minValueFailed"), bcb
831 									.toMap()));
832 				}
833 			}
834 		}
835 
836 		if (!val.isOk()) {
837 			results.add(val);
838 		}
839 
840 	}
841 
842 	private void validateInteger(Object value, BaseConstraintBean bcb,
843 			String element, List<ValidationResultInfo> results) {
844 		Integer v = null;
845 
846 		ValidationResultInfo val = new ValidationResultInfo(element);
847 
848 		if (value instanceof Number) {
849 			v = ((Number) value).intValue();
850 		} else {
851 			try {
852 				v = Integer.valueOf(value.toString());
853 			} catch (Exception e) {
854 				val.setError(getMessage("validation.mustBeInteger"));
855 			}
856 		}
857 
858 		if (val.isOk()) {
859 			Integer maxValue = ValidatorUtils.getInteger(bcb.maxValue);
860 			Integer minValue = ValidatorUtils.getInteger(bcb.minValue);
861 
862 			if (maxValue != null && minValue != null) {
863 				// validate range
864 				if (v > maxValue || v < minValue) {
865 					val.setError(MessageUtils.interpolate(
866 							getMessage("validation.outOfRange"), bcb.toMap()));
867 				}
868 			} else if (maxValue != null) {
869 				if (v > maxValue) {
870 					val.setError(MessageUtils.interpolate(
871 							getMessage("validation.maxValueFailed"), bcb
872 									.toMap()));
873 				}
874 			} else if (minValue != null) {
875 				if (v < minValue) {
876 					val.setError(MessageUtils.interpolate(
877 							getMessage("validation.minValueFailed"), bcb
878 									.toMap()));
879 				}
880 			}
881 		}
882 
883 		if (!val.isOk()) {
884 			results.add(val);
885 		}
886 	}
887 
888 	private void validateDate(Object value, BaseConstraintBean bcb,
889 			String element, List<ValidationResultInfo> results,
890 			DateParser dateParser) {
891 		ValidationResultInfo val = new ValidationResultInfo(element);
892 
893 		Date v = null;
894 
895 		if (value instanceof Date) {
896 			v = (Date) value;
897 		} else {
898 			try {
899 				v = dateParser.parseDate(value.toString());
900 			} catch (Exception e) {
901 				val.setError(getMessage("validation.mustBeDate"));
902 			}
903 		}
904 
905 		if (val.isOk()) {
906 			Date maxValue = ValidatorUtils.getDate(bcb.maxValue, dateParser);
907 			Date minValue = ValidatorUtils.getDate(bcb.minValue, dateParser);
908 
909 			if (maxValue != null && minValue != null) {
910 				// validate range
911 				if (v.getTime() > maxValue.getTime()
912 						|| v.getTime() < minValue.getTime()) {
913 					val.setError(MessageUtils.interpolate(
914 							getMessage("validation.outOfRange"), bcb.toMap()));
915 				}
916 			} else if (maxValue != null) {
917 				if (v.getTime() > maxValue.getTime()) {
918 					val.setError(MessageUtils.interpolate(
919 							getMessage("validation.maxValueFailed"), bcb
920 									.toMap()));
921 				}
922 			} else if (minValue != null) {
923 				if (v.getTime() < minValue.getTime()) {
924 					val.setError(MessageUtils.interpolate(
925 							getMessage("validation.minValueFailed"), bcb
926 									.toMap()));
927 				}
928 			}
929 		}
930 
931 		if (!val.isOk()) {
932 			results.add(val);
933 		}
934 	}
935 
936 	private void validateString(Object value, BaseConstraintBean bcb,
937 			String element, List<ValidationResultInfo> results) {
938 
939 		if (value == null) {
940 			value = "";
941 		}
942 		String s = value.toString().trim();
943 
944 		ValidationResultInfo val = new ValidationResultInfo(element);
945 
946 		Integer maxLength = tryParse(bcb.maxLength);
947 		if (maxLength != null && bcb.minLength > 0) {
948 			if (s.length() > maxLength || s.length() < bcb.minLength) {
949 				val
950 						.setError(MessageUtils.interpolate(
951 								getMessage("validation.lengthOutOfRange"), bcb
952 										.toMap()));
953 			}
954 		} else if (maxLength != null) {
955 			if (s.length() > Integer.parseInt(bcb.maxLength)) {
956 				val.setError(MessageUtils.interpolate(
957 						getMessage("validation.maxLengthFailed"), bcb.toMap()));
958 			}
959 		} else if (bcb.minLength > 0) {
960 			if (s.length() < bcb.minLength) {
961 				val.setError(MessageUtils.interpolate(
962 						getMessage("validation.minLengthFailed"), bcb.toMap()));
963 			}
964 		}
965 
966 		if (!val.isOk()) {
967 			results.add(val);
968 		}
969 	}
970 
971 	private String getMessage(String messageId) {
972 		if (null == messageService) {
973 			return messageId;
974 		}
975 
976 		// TODO: this need to be properly implemented.
977         LocaleInfo locale = new LocaleInfo();
978         locale.setLocaleLanguage(messageLocaleKey);
979 		MessageInfo msg;
980         try {
981             msg = messageService.getMessage(locale,
982             		messageGroupKey, messageId, ContextUtils.getContextInfo());
983         } catch (DoesNotExistException e) {
984             return "";
985         } catch (InvalidParameterException e) {
986             return "";
987         } catch (MissingParameterException e) {
988             return "";
989         } catch (OperationFailedException e) {
990             return "";
991         } catch (PermissionDeniedException e) {
992             return "";
993         }
994 
995 		return msg.getValue();
996 	}
997 
998 	private String getElementXpath(Stack<String> elementStack) {
999 		StringBuilder xPath = new StringBuilder();
1000 		xPath.append("/");
1001 		Iterator<String> itr = elementStack.iterator();
1002 		while (itr.hasNext()) {
1003 			xPath.append(itr.next() + "/");
1004 		}
1005 
1006 		return xPath.toString();
1007 	}
1008 
1009 	/*
1010 	 * Homemade has text so we dont need outside libs.
1011 	 */
1012 	private boolean hasText(String string) {
1013 
1014 		if (string == null || string.length() < 1) {
1015 			return false;
1016 		}
1017 		int stringLength = string.length();
1018 
1019 		for (int i = 0; i < stringLength; i++) {
1020 			char currentChar = string.charAt(i);
1021 			if (' ' != currentChar || '\t' != currentChar
1022 					|| '\n' != currentChar) {
1023 				return true;
1024 			}
1025 		}
1026 
1027 		return false;
1028 	}
1029 
1030 	private static class BaseConstraintBean {
1031 		public boolean initialized = false;
1032 		public Integer minOccurs = 0;
1033 		public String maxOccurs = UNBOUNDED_CHECK;
1034 		public Integer minLength = 0;
1035 		public String maxLength = UNBOUNDED_CHECK;
1036 		public String dataType = null;
1037 		public String minValue = null;
1038 		public String maxValue = null;
1039 
1040 		public Map<String, Object> toMap() {
1041 			Map<String, Object> result = new HashMap<String, Object>();
1042 			result.put("minOccurs", minOccurs);
1043 			result.put("maxOccurs", maxOccurs);
1044 			result.put("minLength", minLength);
1045 			result.put("maxLength", maxLength);
1046 			result.put("minValue", minValue);
1047 			result.put("maxValue", maxValue);
1048 			result.put("dataType", dataType);
1049 
1050 			return result;
1051 		}
1052 	}
1053 }