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