1
2
3
4
5
6
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 import org.kuali.student.common.validation.dto.ValidationResultInfo.ErrorLevel;
43 import org.springframework.beans.BeanUtils;
44
45 public class DefaultValidatorImpl extends BaseAbstractValidator {
46 final static Logger LOG = Logger.getLogger(DefaultValidatorImpl.class);
47
48 private MessageService messageService = null;
49
50 private SearchDispatcher searchDispatcher;
51
52 private String messageLocaleKey = "en";
53
54 private String messageGroupKey = "validation";
55
56 private DateParser dateParser = new ServerDateParser();
57
58 private boolean serverSide = true;
59
60 public MessageService getMessageService() {
61 return messageService;
62 }
63
64 public void setMessageService(MessageService messageService) {
65 this.messageService = messageService;
66 }
67
68 public String getMessageLocaleKey() {
69 return messageLocaleKey;
70 }
71
72 public void setMessageLocaleKey(String messageLocaleKey) {
73 this.messageLocaleKey = messageLocaleKey;
74 }
75
76 public String getMessageGroupKey() {
77 return messageGroupKey;
78 }
79
80 public void setMessageGroupKey(String messageGroupKey) {
81 this.messageGroupKey = messageGroupKey;
82 }
83
84 public void setDateParser(DateParser dateParser) {
85 this.dateParser = dateParser;
86 }
87
88
89
90
91 public boolean isServerSide() {
92 return serverSide;
93 }
94
95
96
97
98
99 public void setServerSide(boolean serverSide) {
100 this.serverSide = serverSide;
101 }
102
103
104
105
106 public DateParser getDateParser() {
107 return dateParser;
108 }
109
110
111
112
113
114
115
116
117 public List<ValidationResultInfo> validateObject(Object data, ObjectStructureDefinition objStructure) {
118
119 List<ValidationResultInfo> results = new ArrayList<ValidationResultInfo>();
120 Stack<String> elementStack = new Stack<String>();
121
122 validateObject(results, data, objStructure, elementStack, data, objStructure, true);
123
124 return results;
125 }
126
127 private void validateObject(List<ValidationResultInfo> results, Object data, ObjectStructureDefinition objStructure, Stack<String> elementStack, Object rootData, ObjectStructureDefinition rootObjStructure, boolean isRoot) {
128
129 ConstraintDataProvider dataProvider = new BeanConstraintDataProvider();
130 dataProvider.initialize(data);
131
132
133 StringBuilder objXPathElement = new StringBuilder(dataProvider.getPath());
134
135 if(!isRoot && !objXPathElement.toString().isEmpty()){
136 elementStack.push(objXPathElement.toString());
137 }
138
139
140
141
142
143 if (null == objStructure) {
144 return;
145 }
146
147 for (FieldDefinition f : objStructure.getAttributes()) {
148 validateField(results, f, objStructure, dataProvider, elementStack, rootData, rootObjStructure);
149
150
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 List<ValidationResultInfo> 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
165
166
167
168
169
170
171
172
173 }
174
175 public void validateField(List<ValidationResultInfo> results, FieldDefinition field, ObjectStructureDefinition objStruct, ConstraintDataProvider dataProvider, Stack<String> elementStack, Object rootData, ObjectStructureDefinition rootObjectStructure) {
176
177 Object value = dataProvider.getValue(field.getName());
178
179
180 if (value == null || "".equals(value.toString().trim())) {
181 processConstraint(results, field, objStruct, value, dataProvider, elementStack, rootData, rootObjectStructure);
182 return;
183 }
184
185
186
187
188 if (DataType.COMPLEX.equals(field.getDataType())) {
189 ObjectStructureDefinition nestedObjStruct = null;
190
191 if (null != field.getDataObjectStructure()) {
192 nestedObjStruct = field.getDataObjectStructure();
193 }
194
195 elementStack.push(field.getName());
196
197
198 if (value instanceof Collection) {
199
200 String xPathForCollection = getElementXpath(elementStack) + "/*";
201
202 int i=0;
203 for (Object o : (Collection<?>) value) {
204 elementStack.push(Integer.toString(i));
205
206 processNestedObjectStructure(results, o, nestedObjStruct, field, elementStack, rootData, rootObjectStructure);
207
208
209 elementStack.pop();
210 i++;
211 }
212 if (field.getMinOccurs() != null && field.getMinOccurs() > ((Collection<?>) value).size()) {
213 ValidationResultInfo valRes = new ValidationResultInfo(xPathForCollection, value);
214 valRes.setError(MessageUtils.interpolate(getMessage("validation.minOccurs"), toMap(field)));
215 results.add(valRes);
216 }
217
218 Integer maxOccurs = tryParse(field.getMaxOccurs());
219 if (maxOccurs != null && maxOccurs < ((Collection<?>) value).size()) {
220 ValidationResultInfo valRes = new ValidationResultInfo(xPathForCollection, value);
221 valRes.setError(MessageUtils.interpolate(getMessage("validation.maxOccurs"), toMap(field)));
222 results.add(valRes);
223 }
224 } else {
225 if (null != value) {
226 processNestedObjectStructure(results, value, nestedObjStruct, field, elementStack, rootData, rootObjectStructure);
227 } else {
228 if (field.getMinOccurs() != null && field.getMinOccurs() > 0) {
229 ValidationResultInfo val = new ValidationResultInfo(getElementXpath(elementStack), value);
230 if(field.getLabelKey()!=null){
231 val.setError(getMessage(field.getLabelKey()));
232 } else {
233 val.setError(getMessage("validation.required"));
234 }
235 results.add(val);
236 }
237 }
238 }
239
240
241 elementStack.pop();
242
243 } else {
244
245 if (value instanceof Collection) {
246
247 if(((Collection<?>)value).isEmpty()){
248 processConstraint(results, field, objStruct, "", dataProvider, elementStack, rootData, rootObjectStructure);
249 }
250
251 int i = 0;
252 for (Object o : (Collection<?>) value) {
253
254 elementStack.push(field.getName());
255 FieldDefinition tempField = new FieldDefinition();
256 BeanUtils.copyProperties(field, tempField);
257 tempField.setName(Integer.toBinaryString(i));
258 processConstraint(results, tempField, objStruct, o, dataProvider, elementStack, rootData, rootObjectStructure);
259 elementStack.pop();
260 i++;
261 }
262
263 String xPath = getElementXpath(elementStack) + "/" + field.getName() + "/*";
264 if (field.getMinOccurs() != null && field.getMinOccurs() > ((Collection<?>) value).size()) {
265 ValidationResultInfo valRes = new ValidationResultInfo(xPath, value);
266 valRes.setError(MessageUtils.interpolate(getMessage("validation.minOccurs"), toMap(field)));
267 results.add(valRes);
268 }
269
270 Integer maxOccurs = tryParse(field.getMaxOccurs());
271 if (maxOccurs != null && maxOccurs < ((Collection<?>) value).size()) {
272 ValidationResultInfo valRes = new ValidationResultInfo(xPath, value);
273 valRes.setError(MessageUtils.interpolate(getMessage("validation.maxOccurs"), toMap(field)));
274 results.add(valRes);
275 }
276 } else {
277 processConstraint(results, field, objStruct, value, dataProvider, elementStack, rootData, rootObjectStructure);
278 }
279
280 }
281 }
282
283 protected Integer tryParse(String s) {
284 Integer result = null;
285 if (s != null) {
286 try {
287 result = Integer.valueOf(s);
288 } catch (NumberFormatException e) {
289
290 }
291 }
292 return result;
293 }
294
295 protected void processNestedObjectStructure(List<ValidationResultInfo> results, Object value, ObjectStructureDefinition nestedObjStruct, FieldDefinition field, Stack<String> elementStack, Object rootData, ObjectStructureDefinition rootObjStructure) {
296 validateObject(results, value, nestedObjStruct, elementStack, rootData, rootObjStructure, false);
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
302
303 Constraint caseConstraint = processCaseConstraint(valResults, field.getCaseConstraint(), objStructure, value, dataProvider, elementStack, rootData, rootObjStructure);
304
305 Constraint constraint = (null != caseConstraint) ? caseConstraint : field;
306
307 processBaseConstraints(valResults, constraint, field, value, elementStack);
308
309
310 if (value == null || "".equals(value.toString().trim())) {
311 return;
312 }
313
314 String elementPath = getElementXpath(elementStack) + "/" + field.getName();
315
316
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
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 processCrossFieldWarning(valResults, rc, val.getErrorLevel(), field.getName());
333 }
334 }
335 }
336 }
337
338
339 if (null != constraint.getOccursConstraint() && constraint.getOccursConstraint().size() > 0) {
340 for (MustOccurConstraint oc : constraint.getOccursConstraint()) {
341 ValidationResultInfo val = processOccursConstraint(elementPath, oc, field, objStructure, dataProvider);
342 if (null != val) {
343 valResults.add(val);
344 }
345 }
346 }
347
348
349 if (null != constraint.getLookupDefinition()) {
350 processLookupConstraint(valResults, constraint.getLookupDefinition(), field, elementStack, dataProvider,objStructure, rootData, rootObjStructure, value);
351 }
352 }
353
354 protected ValidationResultInfo processRequireConstraint(String element, RequiredConstraint constraint, FieldDefinition field, ObjectStructureDefinition objStructure, ConstraintDataProvider dataProvider) {
355
356 ValidationResultInfo val = null;
357
358 String fieldName = constraint.getFieldPath();
359 Object fieldValue = dataProvider.getValue(fieldName);
360
361 boolean result = true;
362
363 if (fieldValue instanceof java.lang.String) {
364 result = hasText((String) fieldValue);
365 } else if (fieldValue instanceof Collection) {
366 result = (((Collection<?>) fieldValue).size() > 0);
367 } else {
368 result = (null != fieldValue) ? true : false;
369 }
370
371 if (!result) {
372 Map<String, Object> rMap = new HashMap<String, Object>();
373 rMap.put("field1", field.getName());
374 rMap.put("field2", fieldName);
375 val = new ValidationResultInfo(element, fieldValue);
376 val.setMessage(MessageUtils.interpolate(getMessage("validation.requiresField"), rMap));
377 val.setLevel(constraint.getErrorLevel());
378 }
379
380 return val;
381 }
382
383
384
385
386
387
388
389
390 protected Constraint processCaseConstraint(List<ValidationResultInfo> valResults, CaseConstraint caseConstraint, ObjectStructureDefinition objStructure, Object value, ConstraintDataProvider dataProvider, Stack<String> elementStack, Object rootData, ObjectStructureDefinition rootObjStructure) {
391
392 if (null == caseConstraint) {
393 return null;
394 }
395
396 String operator = (hasText(caseConstraint.getOperator())) ? caseConstraint.getOperator() : "EQUALS";
397 FieldDefinition caseField = null;
398 boolean absolutePath = false;
399 if(hasText(caseConstraint.getFieldPath())){
400 if(caseConstraint.getFieldPath().startsWith("/")){
401 absolutePath = true;
402 caseField = ValidatorUtils.getField(caseConstraint.getFieldPath().substring(1), rootObjStructure);
403 }else{
404 caseField = ValidatorUtils.getField(caseConstraint.getFieldPath(), objStructure);
405 }
406 }
407
408
409 Object fieldValue = value;
410 if(caseField!=null){
411 if(absolutePath){
412 try {
413 if(caseField.isDynamic()){
414
415
416
417 Map<String,String> attributes = (Map<String,String>) PropertyUtils.getNestedProperty(rootData, "attributes");
418 if(attributes!=null){
419 fieldValue = attributes.get(caseConstraint.getFieldPath().substring(1));
420 }
421 }else{
422 fieldValue = PropertyUtils.getNestedProperty(rootData, caseConstraint.getFieldPath().substring(1));
423 }
424 } catch (IllegalAccessException e) {
425 } catch (InvocationTargetException e) {
426 } catch (NoSuchMethodException e) {
427 }
428 }else{
429 fieldValue = dataProvider.getValue(caseField.getName());
430 }
431 }
432 DataType fieldDataType = (null != caseField ? caseField.getDataType():null);
433
434
435 if(null == fieldValue) {
436 return null;
437 }
438
439
440 for (WhenConstraint wc : caseConstraint.getWhenConstraint()) {
441
442 if(hasText(wc.getValuePath())){
443 Object whenValue = null;
444 if(wc.getValuePath().startsWith("/")){
445 try {
446 whenValue = PropertyUtils.getNestedProperty(rootData, wc.getValuePath().substring(1));
447 } catch (IllegalAccessException e) {
448 } catch (InvocationTargetException e) {
449 } catch (NoSuchMethodException e) {
450 }
451 }else{
452 whenValue = dataProvider.getValue(wc.getValuePath());
453 }
454 if (ValidatorUtils.compareValues(fieldValue, whenValue, fieldDataType, operator, caseConstraint.isCaseSensitive(), dateParser) && null != wc.getConstraint()) {
455 Constraint constraint = wc.getConstraint();
456 if (constraint.getCaseConstraint() != null){
457 return processCaseConstraint(valResults, constraint.getCaseConstraint(), objStructure, value, dataProvider, elementStack, rootData, rootObjStructure);
458 } else {
459 processCrossFieldWarning(valResults, caseConstraint, constraint, value, constraint.getErrorLevel());
460 return constraint;
461 }
462 }
463 }else{
464 List<Object> whenValueList = wc.getValues();
465
466 for (Object whenValue : whenValueList) {
467 if (ValidatorUtils.compareValues(fieldValue, whenValue, fieldDataType, operator, caseConstraint.isCaseSensitive(), dateParser) && null != wc.getConstraint()) {
468 Constraint constraint = wc.getConstraint();
469 if (constraint.getCaseConstraint() != null){
470 return processCaseConstraint(valResults, constraint.getCaseConstraint(), objStructure, value, dataProvider, elementStack, rootData, rootObjStructure);
471 } else {
472 processCrossFieldWarning(valResults, caseConstraint, constraint, value, constraint.getErrorLevel());
473 return constraint;
474 }
475 }
476 }
477 }
478 }
479
480 return null;
481 }
482
483 public ValidationResultInfo processValidCharConstraint(String element, ValidCharsConstraint vcConstraint, ConstraintDataProvider dataProvider, Object value) {
484
485 ValidationResultInfo val = null;
486
487 StringBuilder fieldValue = new StringBuilder();
488 String validChars = vcConstraint.getValue();
489
490 fieldValue.append(ValidatorUtils.getString(value));
491
492 int typIdx = validChars.indexOf(":");
493 String processorType = "regex";
494 if (-1 == typIdx) {
495 validChars = "[" + validChars + "]*";
496 } else {
497 processorType = validChars.substring(0, typIdx);
498 validChars = validChars.substring(typIdx + 1);
499 }
500
501 if ("regex".equalsIgnoreCase(processorType)) {
502 if (fieldValue == null || !fieldValue.toString().matches(validChars)) {
503 val = new ValidationResultInfo(element, fieldValue);
504 if(vcConstraint.getLabelKey()!=null){
505 val.setError(getMessage(vcConstraint.getLabelKey()));
506 }else{
507 val.setError(getMessage("validation.validCharsFailed"));
508 }
509 }
510 }
511
512 return val;
513 }
514
515
516
517
518
519
520
521
522
523
524
525
526
527 protected ValidationResultInfo processOccursConstraint(String element, MustOccurConstraint constraint, FieldDefinition field, ObjectStructureDefinition objStructure, ConstraintDataProvider dataProvider) {
528
529 boolean result = false;
530 int trueCount = 0;
531
532 ValidationResultInfo val = null;
533
534 for (RequiredConstraint rc : constraint.getRequiredFields()) {
535 trueCount += (processRequireConstraint("", rc, field, objStructure, dataProvider) != null) ? 1 : 0;
536 }
537
538 for (MustOccurConstraint oc : constraint.getOccurs()) {
539 trueCount += (processOccursConstraint("", oc, field, objStructure, dataProvider) != null) ? 1 : 0;
540 }
541
542 result = (trueCount >= constraint.getMin() && trueCount <= constraint.getMax()) ? true : false;
543
544 if (!result) {
545
546 val = new ValidationResultInfo(element, null);
547 val.setMessage(getMessage("validation.occurs"));
548 val.setLevel(constraint.getErrorLevel());
549 }
550
551 return val;
552 }
553
554
555 protected void processLookupConstraint(List<ValidationResultInfo> valResults, LookupConstraint lookupConstraint, FieldDefinition field, Stack<String> elementStack, ConstraintDataProvider dataProvider, ObjectStructureDefinition objStructure, Object rootData, ObjectStructureDefinition rootObjStructure, Object value) {
556 if (lookupConstraint == null) {
557 return;
558 }
559
560
561 List<SearchParam> params = new ArrayList<SearchParam>();
562
563 for (CommonLookupParam paramMapping : lookupConstraint.getParams()) {
564
565 if(lookupConstraint.getSearchParamIdKey()!=null&&lookupConstraint.getSearchParamIdKey().equals(paramMapping.getKey())){
566 continue;
567 }
568
569 SearchParam param = new SearchParam();
570
571 param.setKey(paramMapping.getKey());
572
573
574 if (paramMapping.getFieldPath() != null && !paramMapping.getFieldPath().isEmpty()) {
575 FieldDefinition lookupField = null;
576 boolean absolutePath = false;
577 if(hasText(paramMapping.getFieldPath())){
578 if(paramMapping.getFieldPath().startsWith("/")){
579 absolutePath = true;
580 lookupField = ValidatorUtils.getField(paramMapping.getFieldPath().substring(1), rootObjStructure);
581 }else{
582 lookupField = ValidatorUtils.getField(paramMapping.getFieldPath(), objStructure);
583 }
584 }
585 Object fieldValue = null;
586 if(lookupField!=null){
587 if(absolutePath){
588 try {
589 if(lookupField.isDynamic()){
590
591
592 Map<String,String> attributes = (Map<String,String>) PropertyUtils.getNestedProperty(rootData, "attributes");
593 if(attributes!=null){
594 fieldValue = attributes.get(paramMapping.getFieldPath().substring(1));
595 }
596 }else{
597 fieldValue = PropertyUtils.getNestedProperty(rootData, paramMapping.getFieldPath().substring(1));
598 }
599 } catch (IllegalAccessException e) {
600 } catch (InvocationTargetException e) {
601 } catch (NoSuchMethodException e) {
602 }
603 }else{
604 fieldValue = dataProvider.getValue(lookupField.getName());
605 }
606 }else{
607 fieldValue = dataProvider.getValue(paramMapping.getFieldPath());
608 }
609
610 if (fieldValue instanceof String) {
611 param.setValue((String) fieldValue);
612 } else if (fieldValue instanceof List<?>) {
613 param.setValue((List<String>) fieldValue);
614 }
615 } else if (paramMapping.getDefaultValueString() != null) {
616 param.setValue(paramMapping.getDefaultValueString());
617 } else {
618 param.setValue(paramMapping.getDefaultValueList());
619 }
620 params.add(param);
621 }
622
623 if(lookupConstraint.getSearchParamIdKey()!=null){
624 SearchParam param = new SearchParam();
625 param.setKey(lookupConstraint.getSearchParamIdKey());
626 if (value instanceof String) {
627 param.setValue((String) value);
628 } else if (value instanceof List<?>) {
629 param.setValue((List<String>) value);
630 }
631 params.add(param);
632 }
633
634 SearchRequest searchRequest = new SearchRequest();
635 searchRequest.setMaxResults(1);
636 searchRequest.setStartAt(0);
637 searchRequest.setNeededTotalResults(false);
638 searchRequest.setSearchKey(lookupConstraint.getSearchTypeId());
639 searchRequest.setParams(params);
640
641 SearchResult searchResult = null;
642 try {
643 searchResult = searchDispatcher.dispatchSearch(searchRequest);
644 } catch (Exception e) {
645 LOG.info("Error calling Search", e);
646 }
647
648 if (searchResult == null || searchResult.getRows() == null || searchResult.getRows().isEmpty()) {
649 ValidationResultInfo val = new ValidationResultInfo(getElementXpath(elementStack) + "/" + field.getName(), value);
650 val.setLevel(lookupConstraint.getErrorLevel());
651 val.setMessage(getMessage("validation.lookup"));
652 valResults.add(val);
653 processCrossFieldWarning(valResults, lookupConstraint, lookupConstraint.getErrorLevel());
654 }
655 }
656
657 protected void processBaseConstraints(List<ValidationResultInfo> valResults, Constraint constraint, FieldDefinition field, Object value, Stack<String> elementStack) {
658 DataType dataType = field.getDataType();
659 String name = field.getName();
660
661 if (value == null || "".equals(value.toString().trim())) {
662 if (constraint.getMinOccurs() != null && constraint.getMinOccurs() > 0) {
663 ValidationResultInfo val = new ValidationResultInfo(getElementXpath(elementStack) + "/" + name, value);
664 if(constraint.getLabelKey()!=null){
665 val.setError(getMessage(constraint.getLabelKey()));
666 } else {
667 val.setMessage(getMessage("validation.required"));
668 }
669 val.setLevel(constraint.getErrorLevel());
670 valResults.add(val);
671 }
672 return;
673 }
674
675 String elementPath = getElementXpath(elementStack) + "/" + name;
676
677 if (DataType.STRING.equals(dataType)) {
678 validateString(value, constraint, elementPath, valResults);
679 } else if (DataType.INTEGER.equals(dataType)) {
680 validateInteger(value, constraint, elementPath, valResults);
681 } else if (DataType.LONG.equals(dataType)) {
682 validateLong(value, constraint, elementPath, valResults);
683 } else if (DataType.DOUBLE.equals(dataType)) {
684 validateDouble(value, constraint, elementPath, valResults);
685 } else if (DataType.FLOAT.equals(dataType)) {
686 validateFloat(value, constraint, elementPath, valResults);
687 } else if (DataType.BOOLEAN.equals(dataType)) {
688 validateBoolean(value, constraint, elementPath, valResults);
689 } else if (DataType.DATE.equals(dataType)) {
690 validateDate(value, constraint, elementPath, valResults, dateParser);
691 }
692 }
693
694
695
696
697
698
699
700
701 protected void processCrossFieldWarning(List<ValidationResultInfo> valResults, CaseConstraint crossConstraint, Constraint constraint, Object value, ErrorLevel errorLevel){
702 if ((ErrorLevel.WARN == errorLevel || ErrorLevel.ERROR == errorLevel) && (value == null || "".equals(value.toString().trim()))) {
703 if (constraint.getMinOccurs() != null && constraint.getMinOccurs() > 0) {
704
705 String crossFieldPath = crossConstraint.getFieldPath();
706 String crossFieldMessageId = crossConstraint.getFieldPathMessageId() == null ?
707 "validation.required":crossConstraint.getFieldPathMessageId();
708 addCrossFieldWarning(valResults, crossFieldPath, getMessage(crossFieldMessageId), errorLevel);
709 }
710 }
711 }
712
713
714
715
716
717
718
719
720 protected void processCrossFieldWarning(List<ValidationResultInfo> valResults, RequiredConstraint requiredConstraint, ErrorLevel errorLevel, String field){
721 if ((ErrorLevel.WARN == errorLevel || ErrorLevel.ERROR == errorLevel) && requiredConstraint != null){
722 String crossFieldPath = requiredConstraint.getFieldPath();
723 String crossFieldMessageId = requiredConstraint.getFieldPathMessageId() == null ?
724 "validation.required":requiredConstraint.getFieldPathMessageId();
725 addCrossFieldWarning(valResults, crossFieldPath, getMessage(crossFieldMessageId), errorLevel);
726 }
727 }
728
729
730
731
732
733
734
735 protected void processCrossFieldWarning(List<ValidationResultInfo> valResults, LookupConstraint lookupConstraint, ErrorLevel errorLevel){
736 if ((ErrorLevel.WARN == errorLevel || ErrorLevel.ERROR == errorLevel) && lookupConstraint != null){
737 for(CommonLookupParam param:lookupConstraint.getParams()){
738 if(param.getFieldPath()!=null && !param.getFieldPath().isEmpty()){
739 String crossFieldPath = param.getFieldPath();
740 String crossFieldMessageId = param.getFieldPathMessageId() == null ?
741 "validation.lookup.cause":param.getFieldPathMessageId();
742 addCrossFieldWarning(valResults, crossFieldPath, getMessage(crossFieldMessageId), errorLevel);
743 }
744 }
745 }
746 }
747
748 protected void addCrossFieldWarning(List<ValidationResultInfo> valResults, String crossFieldPath, String message, ErrorLevel errorLevel){
749
750 boolean warnAlreadyExists = false;
751 for (ValidationResultInfo vr:valResults){
752 if (vr.getElement().equals(crossFieldPath) && vr.getMessage().equals(message)){
753 warnAlreadyExists = true;
754 }
755 }
756
757
758 if (!warnAlreadyExists){
759 ValidationResultInfo val = new ValidationResultInfo(crossFieldPath, null);
760 val.setMessage(message);
761 val.setLevel(errorLevel);
762 valResults.add(val);
763 }
764 }
765
766 protected void validateBoolean(Object value, Constraint constraint, String element, List<ValidationResultInfo> results) {
767 if (!(value instanceof Boolean)) {
768 try {
769 Boolean.valueOf(value.toString());
770 } catch (Exception e) {
771 ValidationResultInfo val = new ValidationResultInfo(element, value);
772 val.setError(getMessage("validation.mustBeBoolean"));
773 results.add(val);
774 }
775 }
776 }
777
778 protected void validateDouble(Object value, Constraint constraint, String element, List<ValidationResultInfo> results) {
779 Double v = null;
780
781 ValidationResultInfo val = new ValidationResultInfo(element, value);
782
783 if (value instanceof Number) {
784 v = ((Number) value).doubleValue();
785 } else {
786 try {
787 v = Double.valueOf(value.toString());
788 } catch (Exception e) {
789 val.setError(getMessage("validation.mustBeDouble"));
790 }
791 }
792
793 if (val.isOk()) {
794 Double maxValue = ValidatorUtils.getDouble(constraint.getInclusiveMax());
795 Double minValue = ValidatorUtils.getDouble(constraint.getExclusiveMin());
796
797 if (maxValue != null && minValue != null) {
798
799 if (v > maxValue || v < minValue) {
800 val.setError(MessageUtils.interpolate(getMessage("validation.outOfRange"), toMap(constraint)));
801 }
802 } else if (maxValue != null) {
803 if (v > maxValue) {
804 val.setError(MessageUtils.interpolate(getMessage("validation.maxValueFailed"), toMap(constraint)));
805 }
806 } else if (minValue != null) {
807 if (v < minValue) {
808 val.setError(MessageUtils.interpolate(getMessage("validation.minValueFailed"), toMap(constraint)));
809 }
810 }
811 }
812
813 if (!val.isOk()) {
814 results.add(val);
815 }
816 }
817
818 protected void validateFloat(Object value, Constraint constraint, String element, List<ValidationResultInfo> results) {
819 Float v = null;
820
821 ValidationResultInfo val = new ValidationResultInfo(element, value);
822 if (value instanceof Number) {
823 v = ((Number) value).floatValue();
824 } else {
825 try {
826 v = Float.valueOf(value.toString());
827 } catch (Exception e) {
828 val.setError(getMessage("validation.mustBeFloat"));
829 }
830 }
831
832 if (val.isOk()) {
833 Float maxValue = ValidatorUtils.getFloat(constraint.getInclusiveMax());
834 Float minValue = ValidatorUtils.getFloat(constraint.getExclusiveMin());
835
836 if (maxValue != null && minValue != null) {
837
838 if (v > maxValue || v < minValue) {
839 val.setError(MessageUtils.interpolate(getMessage("validation.outOfRange"), toMap(constraint)));
840 }
841 } else if (maxValue != null) {
842 if (v > maxValue) {
843 val.setError(MessageUtils.interpolate(getMessage("validation.maxValueFailed"), toMap(constraint)));
844 }
845 } else if (minValue != null) {
846 if (v < minValue) {
847 val.setError(MessageUtils.interpolate(getMessage("validation.minValueFailed"), toMap(constraint)));
848 }
849 }
850 }
851
852 if (!val.isOk()) {
853 results.add(val);
854 }
855 }
856
857 protected void validateLong(Object value, Constraint constraint, String element, List<ValidationResultInfo> results) {
858 Long v = null;
859
860 ValidationResultInfo val = new ValidationResultInfo(element, value);
861 if (value instanceof Number) {
862 v = ((Number) value).longValue();
863 } else {
864 try {
865 v = Long.valueOf(value.toString());
866 } catch (Exception e) {
867 val.setError(getMessage("validation.mustBeLong"));
868 }
869 }
870
871 if (val.isOk()) {
872 Long maxValue = ValidatorUtils.getLong(constraint.getInclusiveMax());
873 Long minValue = ValidatorUtils.getLong(constraint.getExclusiveMin());
874
875 if (maxValue != null && minValue != null) {
876
877 if (v > maxValue || v < minValue) {
878 val.setError(MessageUtils.interpolate(getMessage("validation.outOfRange"), toMap(constraint)));
879 }
880 } else if (maxValue != null) {
881 if (v > maxValue) {
882 val.setError(MessageUtils.interpolate(getMessage("validation.maxValueFailed"), toMap(constraint)));
883 }
884 } else if (minValue != null) {
885 if (v < minValue) {
886 val.setError(MessageUtils.interpolate(getMessage("validation.minValueFailed"), toMap(constraint)));
887 }
888 }
889 }
890
891 if (!val.isOk()) {
892 results.add(val);
893 }
894
895 }
896
897 protected void validateInteger(Object value, Constraint constraint, String element, List<ValidationResultInfo> results) {
898 Integer v = null;
899
900 ValidationResultInfo val = new ValidationResultInfo(element, value);
901
902 if (value instanceof Number) {
903 v = ((Number) value).intValue();
904 } else {
905 try {
906 v = Integer.valueOf(value.toString());
907 } catch (Exception e) {
908 val.setError(getMessage("validation.mustBeInteger"));
909 }
910 }
911
912 if (val.isOk()) {
913 Integer maxValue = ValidatorUtils.getInteger(constraint.getInclusiveMax());
914 Integer minValue = ValidatorUtils.getInteger(constraint.getExclusiveMin());
915
916 if (maxValue != null && minValue != null) {
917
918 if (v > maxValue || v < minValue) {
919 val.setError(MessageUtils.interpolate(getMessage("validation.outOfRange"), toMap(constraint)));
920 }
921 } else if (maxValue != null) {
922 if (v > maxValue) {
923 val.setError(MessageUtils.interpolate(getMessage("validation.maxValueFailed"), toMap(constraint)));
924 }
925 } else if (minValue != null) {
926 if (v < minValue) {
927 val.setError(MessageUtils.interpolate(getMessage("validation.minValueFailed"), toMap(constraint)));
928 }
929 }
930 }
931
932 if (!val.isOk()) {
933 results.add(val);
934 }
935 }
936
937 protected void validateDate(Object value, Constraint constraint, String element, List<ValidationResultInfo> results, DateParser dateParser) {
938 ValidationResultInfo val = new ValidationResultInfo(element, value);
939
940 Date v = null;
941
942 if (value instanceof Date) {
943 v = (Date) value;
944 } else {
945 try {
946 v = dateParser.parseDate(value.toString());
947 } catch (Exception e) {
948 val.setError(getMessage("validation.mustBeDate"));
949 }
950 }
951
952 if (val.isOk()) {
953 Date maxValue = ValidatorUtils.getDate(constraint.getInclusiveMax(), dateParser);
954 Date minValue = ValidatorUtils.getDate(constraint.getExclusiveMin(), dateParser);
955
956 if (maxValue != null && minValue != null) {
957
958 if (v.getTime() > maxValue.getTime() || v.getTime() < minValue.getTime()) {
959 val.setError(MessageUtils.interpolate(getMessage("validation.outOfRange"), toMap(constraint)));
960 }
961 } else if (maxValue != null) {
962 if (v.getTime() > maxValue.getTime()) {
963 val.setError(MessageUtils.interpolate(getMessage("validation.maxValueFailed"), toMap(constraint)));
964 }
965 } else if (minValue != null) {
966 if (v.getTime() < minValue.getTime()) {
967 val.setError(MessageUtils.interpolate(getMessage("validation.minValueFailed"), toMap(constraint)));
968 }
969 }
970 }
971
972 if (!val.isOk()) {
973 results.add(val);
974 }
975 }
976
977 protected void validateString(Object value, Constraint constraint, String element, List<ValidationResultInfo> results) {
978
979 if (value == null) {
980 value = "";
981 }
982 String s = value.toString().trim();
983
984 ValidationResultInfo val = new ValidationResultInfo(element, value);
985
986 Integer maxLength = tryParse(constraint.getMaxLength());
987 if (maxLength != null && constraint.getMinLength() != null && constraint.getMinLength() > 0) {
988 if (s.length() > maxLength || s.length() < constraint.getMinLength()) {
989 val.setError(MessageUtils.interpolate(getMessage("validation.lengthOutOfRange"), toMap(constraint)));
990 }
991 } else if (maxLength != null) {
992 if (s.length() > Integer.parseInt(constraint.getMaxLength())) {
993 val.setError(MessageUtils.interpolate(getMessage("validation.maxLengthFailed"), toMap(constraint)));
994 }
995 } else if (constraint.getMinLength() != null && constraint.getMinLength() > 0) {
996 if (s.length() < constraint.getMinLength()) {
997 val.setError(MessageUtils.interpolate(getMessage("validation.minLengthFailed"), toMap(constraint)));
998 }
999 }
1000
1001 if (!val.isOk()) {
1002 results.add(val);
1003 }
1004 }
1005
1006 protected String getMessage(String messageId) {
1007 if (null == messageService) {
1008 return messageId;
1009 }
1010
1011 Message msg = messageService.getMessage(messageLocaleKey, messageGroupKey, messageId);
1012
1013 return msg.getValue();
1014 }
1015
1016 protected String getElementXpath(Stack<String> elementStack) {
1017 StringBuilder xPath = new StringBuilder();
1018 Iterator<String> itr = elementStack.iterator();
1019 while (itr.hasNext()) {
1020 xPath.append(itr.next());
1021 if(itr.hasNext()){
1022 xPath.append("/");
1023 }
1024 }
1025
1026 return xPath.toString();
1027 }
1028
1029
1030
1031
1032 protected boolean hasText(String string) {
1033
1034 if (string == null || string.length() < 1) {
1035 return false;
1036 }
1037 int stringLength = string.length();
1038
1039 for (int i = 0; i < stringLength; i++) {
1040 char currentChar = string.charAt(i);
1041 if (' ' != currentChar || '\t' != currentChar || '\n' != currentChar) {
1042 return true;
1043 }
1044 }
1045
1046 return false;
1047 }
1048
1049 protected Map<String, Object> toMap(Constraint c) {
1050 Map<String, Object> result = new HashMap<String, Object>();
1051 result.put("minOccurs", c.getMinOccurs());
1052 result.put("maxOccurs", c.getMaxOccurs());
1053 result.put("minLength", c.getMinLength());
1054 result.put("maxLength", c.getMaxLength());
1055 result.put("minValue", c.getExclusiveMin());
1056 result.put("maxValue", c.getInclusiveMax());
1057
1058
1059 return result;
1060 }
1061
1062 public SearchDispatcher getSearchDispatcher() {
1063 return searchDispatcher;
1064 }
1065
1066 public void setSearchDispatcher(SearchDispatcher searchDispatcher) {
1067 this.searchDispatcher = searchDispatcher;
1068 }
1069
1070 @Override
1071 public List<ValidationResultInfo> validateObject(FieldDefinition field,
1072 Object o, ObjectStructureDefinition objStructure,Stack<String> elementStack) {
1073 return null;
1074 }
1075 }