1 /*
2 * $Id$
3 *
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21 package org.apache.struts.validator;
22
23 import java.io.Serializable;
24 import java.util.Collection;
25 import java.util.Locale;
26 import java.util.StringTokenizer;
27
28 import jakarta.servlet.http.HttpServletRequest;
29
30 import org.apache.commons.beanutils.PropertyUtils;
31 import org.apache.commons.validator.Field;
32 import org.apache.commons.validator.GenericTypeValidator;
33 import org.apache.commons.validator.GenericValidator;
34 import org.apache.commons.validator.Validator;
35 import org.apache.commons.validator.ValidatorAction;
36 import org.apache.commons.validator.routines.UrlValidator;
37 import org.apache.commons.validator.util.ValidatorUtils;
38 import org.apache.struts.action.ActionMessage;
39 import org.apache.struts.action.ActionMessages;
40 import org.apache.struts.util.MessageResources;
41 import org.apache.struts.util.RequestUtils;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 /**
46 * <p> This class contains the default validations that are used in the
47 * validator-rules.xml file. </p> <p> In general passing in a null or blank
48 * will return a null Object or a false boolean. However, nulls and blanks do
49 * not result in an error being added to the errors. </p>
50 *
51 * @since Struts 1.1
52 */
53 public class FieldChecks implements Serializable {
54 private static final long serialVersionUID = 6466045187316462715L;
55
56 /**
57 * The {@code Log} instance for this class.
58 */
59 private final static Logger LOG =
60 LoggerFactory.getLogger(FieldChecks.class);
61
62 /**
63 * The message resources for this package.
64 */
65 private static MessageResources sysmsgs =
66 MessageResources.getMessageResources(
67 "org.apache.struts.validator.LocalStrings");
68 public static final String FIELD_TEST_NULL = "NULL";
69 public static final String FIELD_TEST_NOTNULL = "NOTNULL";
70 public static final String FIELD_TEST_EQUAL = "EQUAL";
71
72 /**
73 * Convenience method for getting a value from a bean property as a
74 * <code>String</code>. If the property is a <code>String[]</code> or
75 * <code>Collection</code> and it is empty, an empty <code>String</code>
76 * "" is returned. Otherwise, property.toString() is returned. This method
77 * may return <code>null</code> if there was an error retrieving the
78 * property.
79 * <p>
80 * <b>NOTE</b>: This method is a port from Commons Validator
81 * <code>ValidatorUtils</code> because the original version swallows
82 * exceptions and thus cannot indicate to the caller that the bean
83 * property was invalid. This version will throw an exception.
84 *
85 * @param bean The bean object.
86 * @param property The name of the property to access.
87 * @return The value of the property.
88 * @throws Exception if an error occurs retrieving the property
89 */
90 private static String getValueAsString(Object bean, String property)
91 throws Exception {
92
93 Object value = PropertyUtils.getProperty(bean, property);
94 if (value == null) {
95 return null;
96 }
97
98 if (value instanceof String[]) {
99 return ((String[]) value).length > 0 ? value.toString() : "";
100
101 } else if (value instanceof Collection) {
102 return ((Collection<?>) value).isEmpty() ? "" : value.toString();
103
104 } else {
105 return value.toString();
106 }
107 }
108
109 /**
110 * Checks if the field isn't null and length of the field is greater than
111 * zero not including whitespace.
112 *
113 * @param bean The bean validation is being performed on.
114 * @param va The <code>ValidatorAction</code> that is currently
115 * being performed.
116 * @param field The <code>Field</code> object associated with the
117 * current field being validated.
118 * @param errors The <code>ActionMessages</code> object to add errors
119 * to if any validation errors occur.
120 * @param validator The <code>Validator</code> instance, used to access
121 * other field values.
122 * @param request Current request object.
123 * @return true if meets stated requirements, false otherwise.
124 */
125 public static boolean validateRequired(Object bean, ValidatorAction va,
126 Field field, ActionMessages errors, Validator validator,
127 HttpServletRequest request) {
128 String value = null;
129
130 try {
131 value = evaluateBean(bean, field);
132 } catch (Exception e) {
133 processFailure(errors, field, validator.getFormName(), "required", e);
134 return false;
135 }
136
137 if (GenericValidator.isBlankOrNull(value)) {
138 errors.add(field.getKey(),
139 Resources.getActionMessage(validator, request, va, field));
140
141 return false;
142 } else {
143 return true;
144 }
145 }
146
147 /**
148 * Checks if the field isn't null based on the values of other fields.
149 *
150 * @param bean The bean validation is being performed on.
151 * @param va The <code>ValidatorAction</code> that is currently
152 * being performed.
153 * @param field The <code>Field</code> object associated with the
154 * current field being validated.
155 * @param errors The <code>ActionMessages</code> object to add errors
156 * to if any validation errors occur.
157 * @param validator The <code>Validator</code> instance, used to access
158 * other field values.
159 * @param request Current request object.
160 * @return true if meets stated requirements, false otherwise.
161 */
162 public static boolean validateRequiredIf(Object bean, ValidatorAction va,
163 Field field, ActionMessages errors, Validator validator,
164 HttpServletRequest request) {
165 Object form =
166 validator.getParameterValue(org.apache.commons.validator.Validator.BEAN_PARAM);
167 String value = null;
168 boolean required = false;
169
170 try {
171 value = evaluateBean(bean, field);
172 } catch (Exception e) {
173 processFailure(errors, field, validator.getFormName(), "requiredif", e);
174 return false;
175 }
176
177 int i = 0;
178 String fieldJoin = "AND";
179
180 if (!GenericValidator.isBlankOrNull(field.getVarValue("fieldJoin"))) {
181 fieldJoin = field.getVarValue("fieldJoin");
182 }
183
184 if (fieldJoin.equalsIgnoreCase("AND")) {
185 required = true;
186 }
187
188 while (!GenericValidator.isBlankOrNull(field.getVarValue("field[" + i
189 + "]"))) {
190 String dependProp = field.getVarValue("field[" + i + "]");
191 String dependTest = field.getVarValue("fieldTest[" + i + "]");
192 String dependTestValue = field.getVarValue("fieldValue[" + i + "]");
193 String dependIndexed = field.getVarValue("fieldIndexed[" + i + "]");
194
195 if (dependIndexed == null) {
196 dependIndexed = "false";
197 }
198
199 String dependVal = null;
200 boolean thisRequired = false;
201
202 if (field.isIndexed() && dependIndexed.equalsIgnoreCase("true")) {
203 String key = field.getKey();
204
205 if ((key.indexOf("[") > -1) && (key.indexOf("]") > -1)) {
206 String ind = key.substring(0, key.indexOf(".") + 1);
207
208 dependProp = ind + dependProp;
209 }
210 }
211
212 dependVal = ValidatorUtils.getValueAsString(form, dependProp);
213
214 if (dependTest.equals(FIELD_TEST_NULL)) {
215 if ((dependVal != null) && (dependVal.length() > 0)) {
216 thisRequired = false;
217 } else {
218 thisRequired = true;
219 }
220 }
221
222 if (dependTest.equals(FIELD_TEST_NOTNULL)) {
223 if ((dependVal != null) && (dependVal.length() > 0)) {
224 thisRequired = true;
225 } else {
226 thisRequired = false;
227 }
228 }
229
230 if (dependTest.equals(FIELD_TEST_EQUAL)) {
231 thisRequired = dependTestValue.equalsIgnoreCase(dependVal);
232 }
233
234 if (fieldJoin.equalsIgnoreCase("AND")) {
235 required = required && thisRequired;
236 } else {
237 required = required || thisRequired;
238 }
239
240 i++;
241 }
242
243 if (required) {
244 if (GenericValidator.isBlankOrNull(value)) {
245 errors.add(field.getKey(),
246 Resources.getActionMessage(validator, request, va, field));
247
248 return false;
249 } else {
250 return true;
251 }
252 }
253
254 return true;
255 }
256
257 /**
258 * Checks if the field matches the regular expression in the field's mask
259 * attribute.
260 *
261 * @param bean The bean validation is being performed on.
262 * @param va The <code>ValidatorAction</code> that is currently
263 * being performed.
264 * @param field The <code>Field</code> object associated with the
265 * current field being validated.
266 * @param errors The <code>ActionMessages</code> object to add errors
267 * to if any validation errors occur.
268 * @param validator The <code>Validator</code> instance, used to access
269 * other field values.
270 * @param request Current request object.
271 * @return true if field matches mask, false otherwise.
272 */
273 public static boolean validateMask(Object bean, ValidatorAction va,
274 Field field, ActionMessages errors, Validator validator,
275 HttpServletRequest request) {
276 String value = null;
277
278 try {
279 value = evaluateBean(bean, field);
280
281 String mask =
282 Resources.getVarValue("mask", field, validator, request, true);
283
284 if (value != null && value.length()>0
285 && !GenericValidator.matchRegexp(value, mask)) {
286 errors.add(field.getKey(),
287 Resources.getActionMessage(validator, request, va, field));
288
289 return false;
290 } else {
291 return true;
292 }
293 } catch (Exception e) {
294 processFailure(errors, field, validator.getFormName(), "mask", e);
295
296 return false;
297 }
298 }
299
300 /**
301 * Checks if the field can safely be converted to a byte primitive.
302 *
303 * @param bean The bean validation is being performed on.
304 * @param va The <code>ValidatorAction</code> that is currently
305 * being performed.
306 * @param field The <code>Field</code> object associated with the
307 * current field being validated.
308 * @param errors The <code>ActionMessages</code> object to add errors
309 * to if any validation errors occur.
310 * @param validator The <code>Validator</code> instance, used to access
311 * other field values.
312 * @param request Current request object.
313 * @return true if valid, false otherwise.
314 */
315 public static Object validateByte(Object bean, ValidatorAction va,
316 Field field, ActionMessages errors, Validator validator,
317 HttpServletRequest request) {
318 Object result = null;
319 String value = null;
320
321 try {
322 value = evaluateBean(bean, field);
323 } catch (Exception e) {
324 processFailure(errors, field, validator.getFormName(), "byte", e);
325 return Boolean.FALSE;
326 }
327
328 if (GenericValidator.isBlankOrNull(value)) {
329 return Boolean.TRUE;
330 }
331
332 result = GenericTypeValidator.formatByte(value);
333
334 if (result == null) {
335 errors.add(field.getKey(),
336 Resources.getActionMessage(validator, request, va, field));
337 }
338
339 return (result == null) ? Boolean.FALSE : result;
340 }
341
342 /**
343 * Checks if the field can safely be converted to a byte primitive.
344 *
345 * @param bean The bean validation is being performed on.
346 * @param va The <code>ValidatorAction</code> that is currently
347 * being performed.
348 * @param field The <code>Field</code> object associated with the
349 * current field being validated.
350 * @param errors The <code>ActionMessages</code> object to add errors
351 * to if any validation errors occur.
352 * @param validator The <code>Validator</code> instance, used to access
353 * other field values.
354 * @param request Current request object.
355 * @return true if valid, false otherwise.
356 */
357 public static Object validateByteLocale(Object bean, ValidatorAction va,
358 Field field, ActionMessages errors, Validator validator,
359 HttpServletRequest request) {
360 Object result = null;
361 String value = null;
362
363 try {
364 value = evaluateBean(bean, field);
365 } catch (Exception e) {
366 processFailure(errors, field, validator.getFormName(), "byteLocale", e);
367 return Boolean.FALSE;
368 }
369
370 if (GenericValidator.isBlankOrNull(value)) {
371 return Boolean.TRUE;
372 }
373
374 Locale locale = RequestUtils.getUserLocale(request, null);
375
376 result = GenericTypeValidator.formatByte(value, locale);
377
378 if (result == null) {
379 errors.add(field.getKey(),
380 Resources.getActionMessage(validator, request, va, field));
381 }
382
383 return (result == null) ? Boolean.FALSE : result;
384 }
385
386 /**
387 * @param bean
388 * @param field
389 * @return the
390 * @throws Exception if an error occurs accessing the bean property
391 */
392 private static String evaluateBean(Object bean, Field field) throws Exception {
393 String value;
394
395 if (isString(bean)) {
396 value = (String) bean;
397 } else {
398 value = getValueAsString(bean, field.getProperty());
399 }
400
401 return value;
402 }
403
404 /**
405 * Checks if the field can safely be converted to a short primitive.
406 *
407 * @param bean The bean validation is being performed on.
408 * @param va The <code>ValidatorAction</code> that is currently
409 * being performed.
410 * @param field The <code>Field</code> object associated with the
411 * current field being validated.
412 * @param errors The <code>ActionMessages</code> object to add errors
413 * to if any validation errors occur.
414 * @param validator The <code>Validator</code> instance, used to access
415 * other field values.
416 * @param request Current request object.
417 * @return true if valid, false otherwise.
418 */
419 public static Object validateShort(Object bean, ValidatorAction va,
420 Field field, ActionMessages errors, Validator validator,
421 HttpServletRequest request) {
422 Object result = null;
423 String value = null;
424
425 try {
426 value = evaluateBean(bean, field);
427 } catch (Exception e) {
428 processFailure(errors, field, validator.getFormName(), "short", e);
429 return Boolean.FALSE;
430 }
431
432 if (GenericValidator.isBlankOrNull(value)) {
433 return Boolean.TRUE;
434 }
435
436 result = GenericTypeValidator.formatShort(value);
437
438 if (result == null) {
439 errors.add(field.getKey(),
440 Resources.getActionMessage(validator, request, va, field));
441 }
442
443 return (result == null) ? Boolean.FALSE : result;
444 }
445
446 /**
447 * Checks if the field can safely be converted to a short primitive.
448 *
449 * @param bean The bean validation is being performed on.
450 * @param va The <code>ValidatorAction</code> that is currently
451 * being performed.
452 * @param field The <code>Field</code> object associated with the
453 * current field being validated.
454 * @param errors The <code>ActionMessages</code> object to add errors
455 * to if any validation errors occur.
456 * @param validator The <code>Validator</code> instance, used to access
457 * other field values.
458 * @param request Current request object.
459 * @return true if valid, false otherwise.
460 */
461 public static Object validateShortLocale(Object bean, ValidatorAction va,
462 Field field, ActionMessages errors, Validator validator,
463 HttpServletRequest request) {
464 Object result = null;
465 String value = null;
466
467 try {
468 value = evaluateBean(bean, field);
469 } catch (Exception e) {
470 processFailure(errors, field, validator.getFormName(), "shortLocale", e);
471 return Boolean.FALSE;
472 }
473
474 if (GenericValidator.isBlankOrNull(value)) {
475 return Boolean.TRUE;
476 }
477
478 Locale locale = RequestUtils.getUserLocale(request, null);
479
480 result = GenericTypeValidator.formatShort(value, locale);
481
482 if (result == null) {
483 errors.add(field.getKey(),
484 Resources.getActionMessage(validator, request, va, field));
485 }
486
487 return (result == null) ? Boolean.FALSE : result;
488 }
489
490 /**
491 * Checks if the field can safely be converted to an int primitive.
492 *
493 * @param bean The bean validation is being performed on.
494 * @param va The <code>ValidatorAction</code> that is currently
495 * being performed.
496 * @param field The <code>Field</code> object associated with the
497 * current field being validated.
498 * @param errors The <code>ActionMessages</code> object to add errors
499 * to if any validation errors occur.
500 * @param validator The <code>Validator</code> instance, used to access
501 * other field values.
502 * @param request Current request object.
503 * @return true if valid, false otherwise.
504 */
505 public static Object validateInteger(Object bean, ValidatorAction va,
506 Field field, ActionMessages errors, Validator validator,
507 HttpServletRequest request) {
508 Object result = null;
509 String value = null;
510
511 try {
512 value = evaluateBean(bean, field);
513 } catch (Exception e) {
514 processFailure(errors, field, validator.getFormName(), "integer", e);
515 return Boolean.FALSE;
516 }
517
518 if (GenericValidator.isBlankOrNull(value)) {
519 return Boolean.TRUE;
520 }
521
522 result = GenericTypeValidator.formatInt(value);
523
524 if (result == null) {
525 errors.add(field.getKey(),
526 Resources.getActionMessage(validator, request, va, field));
527 }
528
529 return (result == null) ? Boolean.FALSE : result;
530 }
531
532 /**
533 * Checks if the field can safely be converted to an int primitive.
534 *
535 * @param bean The bean validation is being performed on.
536 * @param va The <code>ValidatorAction</code> that is currently
537 * being performed.
538 * @param field The <code>Field</code> object associated with the
539 * current field being validated.
540 * @param errors The <code>ActionMessages</code> object to add errors
541 * to if any validation errors occur.
542 * @param validator The <code>Validator</code> instance, used to access
543 * other field values.
544 * @param request Current request object.
545 * @return true if valid, false otherwise.
546 */
547 public static Object validateIntegerLocale(Object bean, ValidatorAction va,
548 Field field, ActionMessages errors, Validator validator,
549 HttpServletRequest request) {
550 Object result = null;
551 String value = null;
552
553 try {
554 value = evaluateBean(bean, field);
555 } catch (Exception e) {
556 processFailure(errors, field, validator.getFormName(), "integerLocale", e);
557 return Boolean.FALSE;
558 }
559
560 if (GenericValidator.isBlankOrNull(value)) {
561 return Boolean.TRUE;
562 }
563
564 Locale locale = RequestUtils.getUserLocale(request, null);
565
566 result = GenericTypeValidator.formatInt(value, locale);
567
568 if (result == null) {
569 errors.add(field.getKey(),
570 Resources.getActionMessage(validator, request, va, field));
571 }
572
573 return (result == null) ? Boolean.FALSE : result;
574 }
575
576 /**
577 * Checks if the field can safely be converted to a long primitive.
578 *
579 * @param bean The bean validation is being performed on.
580 * @param va The <code>ValidatorAction</code> that is currently
581 * being performed.
582 * @param field The <code>Field</code> object associated with the
583 * current field being validated.
584 * @param errors The <code>ActionMessages</code> object to add errors
585 * to if any validation errors occur.
586 * @param validator The <code>Validator</code> instance, used to access
587 * other field values.
588 * @param request Current request object.
589 * @return true if valid, false otherwise.
590 */
591 public static Object validateLong(Object bean, ValidatorAction va,
592 Field field, ActionMessages errors, Validator validator,
593 HttpServletRequest request) {
594 Object result = null;
595 String value = null;
596
597 try {
598 value = evaluateBean(bean, field);
599 } catch (Exception e) {
600 processFailure(errors, field, validator.getFormName(), "long", e);
601 return Boolean.FALSE;
602 }
603
604 if (GenericValidator.isBlankOrNull(value)) {
605 return Boolean.TRUE;
606 }
607
608 result = GenericTypeValidator.formatLong(value);
609
610 if (result == null) {
611 errors.add(field.getKey(),
612 Resources.getActionMessage(validator, request, va, field));
613 }
614
615 return (result == null) ? Boolean.FALSE : result;
616 }
617
618 /**
619 * Checks if the field can safely be converted to a long primitive.
620 *
621 * @param bean The bean validation is being performed on.
622 * @param va The <code>ValidatorAction</code> that is currently
623 * being performed.
624 * @param field The <code>Field</code> object associated with the
625 * current field being validated.
626 * @param errors The <code>ActionMessages</code> object to add errors
627 * to if any validation errors occur.
628 * @param validator The <code>Validator</code> instance, used to access
629 * other field values.
630 * @param request Current request object.
631 * @return true if valid, false otherwise.
632 */
633 public static Object validateLongLocale(Object bean, ValidatorAction va,
634 Field field, ActionMessages errors, Validator validator,
635 HttpServletRequest request) {
636 Object result = null;
637 String value = null;
638
639 try {
640 value = evaluateBean(bean, field);
641 } catch (Exception e) {
642 processFailure(errors, field, validator.getFormName(), "longLocale", e);
643 return Boolean.FALSE;
644 }
645
646 if (GenericValidator.isBlankOrNull(value)) {
647 return Boolean.TRUE;
648 }
649
650 Locale locale = RequestUtils.getUserLocale(request, null);
651
652 result = GenericTypeValidator.formatLong(value, locale);
653
654 if (result == null) {
655 errors.add(field.getKey(),
656 Resources.getActionMessage(validator, request, va, field));
657 }
658
659 return (result == null) ? Boolean.FALSE : result;
660 }
661
662 /**
663 * Checks if the field can safely be converted to a float primitive.
664 *
665 * @param bean The bean validation is being performed on.
666 * @param va The <code>ValidatorAction</code> that is currently
667 * being performed.
668 * @param field The <code>Field</code> object associated with the
669 * current field being validated.
670 * @param errors The <code>ActionMessages</code> object to add errors
671 * to if any validation errors occur.
672 * @param validator The <code>Validator</code> instance, used to access
673 * other field values.
674 * @param request Current request object.
675 * @return true if valid, false otherwise.
676 */
677 public static Object validateFloat(Object bean, ValidatorAction va,
678 Field field, ActionMessages errors, Validator validator,
679 HttpServletRequest request) {
680 Object result = null;
681 String value = null;
682
683 try {
684 value = evaluateBean(bean, field);
685 } catch (Exception e) {
686 processFailure(errors, field, validator.getFormName(), "float", e);
687 return Boolean.FALSE;
688 }
689
690 if (GenericValidator.isBlankOrNull(value)) {
691 return Boolean.TRUE;
692 }
693
694 result = GenericTypeValidator.formatFloat(value);
695
696 if (result == null) {
697 errors.add(field.getKey(),
698 Resources.getActionMessage(validator, request, va, field));
699 }
700
701 return (result == null) ? Boolean.FALSE : result;
702 }
703
704 /**
705 * Checks if the field can safely be converted to a float primitive.
706 *
707 * @param bean The bean validation is being performed on.
708 * @param va The <code>ValidatorAction</code> that is currently
709 * being performed.
710 * @param field The <code>Field</code> object associated with the
711 * current field being validated.
712 * @param errors The <code>ActionMessages</code> object to add errors
713 * to if any validation errors occur.
714 * @param validator The <code>Validator</code> instance, used to access
715 * other field values.
716 * @param request Current request object.
717 * @return true if valid, false otherwise.
718 */
719 public static Object validateFloatLocale(Object bean, ValidatorAction va,
720 Field field, ActionMessages errors, Validator validator,
721 HttpServletRequest request) {
722 Object result = null;
723 String value = null;
724
725 try {
726 value = evaluateBean(bean, field);
727 } catch (Exception e) {
728 processFailure(errors, field, validator.getFormName(), "floatLocale", e);
729 return Boolean.FALSE;
730 }
731
732 if (GenericValidator.isBlankOrNull(value)) {
733 return Boolean.TRUE;
734 }
735
736 Locale locale = RequestUtils.getUserLocale(request, null);
737
738 result = GenericTypeValidator.formatFloat(value, locale);
739
740 if (result == null) {
741 errors.add(field.getKey(),
742 Resources.getActionMessage(validator, request, va, field));
743 }
744
745 return (result == null) ? Boolean.FALSE : result;
746 }
747
748 /**
749 * Checks if the field can safely be converted to a double primitive.
750 *
751 * @param bean The bean validation is being performed on.
752 * @param va The <code>ValidatorAction</code> that is currently
753 * being performed.
754 * @param field The <code>Field</code> object associated with the
755 * current field being validated.
756 * @param errors The <code>ActionMessages</code> object to add errors
757 * to if any validation errors occur.
758 * @param validator The <code>Validator</code> instance, used to access
759 * other field values.
760 * @param request Current request object.
761 * @return true if valid, false otherwise.
762 */
763 public static Object validateDouble(Object bean, ValidatorAction va,
764 Field field, ActionMessages errors, Validator validator,
765 HttpServletRequest request) {
766 Object result = null;
767 String value = null;
768
769 try {
770 value = evaluateBean(bean, field);
771 } catch (Exception e) {
772 processFailure(errors, field, validator.getFormName(), "double", e);
773 return Boolean.FALSE;
774 }
775
776 if (GenericValidator.isBlankOrNull(value)) {
777 return Boolean.TRUE;
778 }
779
780 result = GenericTypeValidator.formatDouble(value);
781
782 if (result == null) {
783 errors.add(field.getKey(),
784 Resources.getActionMessage(validator, request, va, field));
785 }
786
787 return (result == null) ? Boolean.FALSE : result;
788 }
789
790 /**
791 * Checks if the field can safely be converted to a double primitive.
792 *
793 * @param bean The bean validation is being performed on.
794 * @param va The <code>ValidatorAction</code> that is currently
795 * being performed.
796 * @param field The <code>Field</code> object associated with the
797 * current field being validated.
798 * @param errors The <code>ActionMessages</code> object to add errors
799 * to if any validation errors occur.
800 * @param validator The <code>Validator</code> instance, used to access
801 * other field values.
802 * @param request Current request object.
803 * @return true if valid, false otherwise.
804 */
805 public static Object validateDoubleLocale(Object bean, ValidatorAction va,
806 Field field, ActionMessages errors, Validator validator,
807 HttpServletRequest request) {
808 Object result = null;
809 String value = null;
810
811 try {
812 value = evaluateBean(bean, field);
813 } catch (Exception e) {
814 processFailure(errors, field, validator.getFormName(), "doubleLocale", e);
815 return Boolean.FALSE;
816 }
817
818 if (GenericValidator.isBlankOrNull(value)) {
819 return Boolean.TRUE;
820 }
821
822 Locale locale = RequestUtils.getUserLocale(request, null);
823
824 result = GenericTypeValidator.formatDouble(value, locale);
825
826 if (result == null) {
827 errors.add(field.getKey(),
828 Resources.getActionMessage(validator, request, va, field));
829 }
830
831 return (result == null) ? Boolean.FALSE : result;
832 }
833
834 /**
835 * Checks if the field is a valid date. If the field has a datePattern
836 * variable, that will be used to format <code>java.text.SimpleDateFormat</code>.
837 * If the field has a datePatternStrict variable, that will be used to
838 * format <code>java.text.SimpleDateFormat</code> and the length will be
839 * checked so '2/12/1999' will not pass validation with the format
840 * 'MM/dd/yyyy' because the month isn't two digits. If no datePattern
841 * variable is specified, then the field gets the DateFormat.SHORT format
842 * for the locale. The setLenient method is set to <code>false</code> for
843 * all variations.
844 *
845 * @param bean The bean validation is being performed on.
846 * @param va The <code>ValidatorAction</code> that is currently
847 * being performed.
848 * @param field The <code>Field</code> object associated with the
849 * current field being validated.
850 * @param errors The <code>ActionMessages</code> object to add errors
851 * to if any validation errors occur.
852 * @param validator The <code>Validator</code> instance, used to access
853 * other field values.
854 * @param request Current request object.
855 * @return true if valid, false otherwise.
856 */
857 public static Object validateDate(Object bean, ValidatorAction va,
858 Field field, ActionMessages errors, Validator validator,
859 HttpServletRequest request) {
860 Object result = null;
861 String value = null;
862
863 try {
864 value = evaluateBean(bean, field);
865 } catch (Exception e) {
866 processFailure(errors, field, validator.getFormName(), "date", e);
867 return Boolean.FALSE;
868 }
869
870 boolean isStrict = false;
871 String datePattern =
872 Resources.getVarValue("datePattern", field, validator, request,
873 false);
874
875 if (GenericValidator.isBlankOrNull(datePattern)) {
876 datePattern =
877 Resources.getVarValue("datePatternStrict", field, validator,
878 request, false);
879
880 if (!GenericValidator.isBlankOrNull(datePattern)) {
881 isStrict = true;
882 }
883 }
884
885 Locale locale = RequestUtils.getUserLocale(request, null);
886
887 if (GenericValidator.isBlankOrNull(value)) {
888 return Boolean.TRUE;
889 }
890
891 try {
892 if (GenericValidator.isBlankOrNull(datePattern)) {
893 result = GenericTypeValidator.formatDate(value, locale);
894 } else {
895 result =
896 GenericTypeValidator.formatDate(value, datePattern, isStrict);
897 }
898 } catch (Exception e) {
899 LOG.error(e.getMessage(), e);
900 }
901
902 if (result == null) {
903 errors.add(field.getKey(),
904 Resources.getActionMessage(validator, request, va, field));
905 }
906
907 return (result == null) ? Boolean.FALSE : result;
908 }
909
910 /**
911 * Checks if a fields value is within a range (min & max specified in
912 * the vars attribute).
913 *
914 * @param bean The bean validation is being performed on.
915 * @param va The <code>ValidatorAction</code> that is currently
916 * being performed.
917 * @param field The <code>Field</code> object associated with the
918 * current field being validated.
919 * @param errors The <code>ActionMessages</code> object to add errors
920 * to if any validation errors occur.
921 * @param validator The <code>Validator</code> instance, used to access
922 * other field values.
923 * @param request Current request object.
924 * @return True if in range, false otherwise.
925 */
926 public static boolean validateLongRange(Object bean, ValidatorAction va,
927 Field field, ActionMessages errors, Validator validator,
928 HttpServletRequest request) {
929 String value = null;
930
931 try {
932 value = evaluateBean(bean, field);
933 if (!GenericValidator.isBlankOrNull(value)) {
934 String minVar =
935 Resources.getVarValue("min", field, validator, request, true);
936 String maxVar =
937 Resources.getVarValue("max", field, validator, request, true);
938 long longValue = Long.parseLong(value);
939 long min = Long.parseLong(minVar);
940 long max = Long.parseLong(maxVar);
941
942 if (min > max) {
943 throw new IllegalArgumentException(sysmsgs.getMessage(
944 "invalid.range", minVar, maxVar));
945 }
946
947 if (!GenericValidator.isInRange(longValue, min, max)) {
948 errors.add(field.getKey(),
949 Resources.getActionMessage(validator, request, va, field));
950
951 return false;
952 }
953 }
954 } catch (Exception e) {
955 processFailure(errors, field, validator.getFormName(), "longRange", e);
956
957 return false;
958 }
959
960 return true;
961 }
962
963 /**
964 * Checks if a fields value is within a range (min & max specified in
965 * the vars attribute).
966 *
967 * @param bean The bean validation is being performed on.
968 * @param va The <code>ValidatorAction</code> that is currently
969 * being performed.
970 * @param field The <code>Field</code> object associated with the
971 * current field being validated.
972 * @param errors The <code>ActionMessages</code> object to add errors
973 * to if any validation errors occur.
974 * @param validator The <code>Validator</code> instance, used to access
975 * other field values.
976 * @param request Current request object.
977 * @return True if in range, false otherwise.
978 */
979 public static boolean validateIntRange(Object bean, ValidatorAction va,
980 Field field, ActionMessages errors, Validator validator,
981 HttpServletRequest request) {
982 String value = null;
983
984 try {
985 value = evaluateBean(bean, field);
986 if (!GenericValidator.isBlankOrNull(value)) {
987 String minVar =
988 Resources.getVarValue("min", field, validator, request, true);
989 String maxVar =
990 Resources.getVarValue("max", field, validator, request, true);
991 int min = Integer.parseInt(minVar);
992 int max = Integer.parseInt(maxVar);
993 int intValue = Integer.parseInt(value);
994
995 if (min > max) {
996 throw new IllegalArgumentException(sysmsgs.getMessage(
997 "invalid.range", minVar, maxVar));
998 }
999
1000 if (!GenericValidator.isInRange(intValue, min, max)) {
1001 errors.add(field.getKey(),
1002 Resources.getActionMessage(validator, request, va, field));
1003
1004 return false;
1005 }
1006 }
1007 } catch (Exception e) {
1008 processFailure(errors, field, validator.getFormName(), "intRange", e);
1009
1010 return false;
1011 }
1012
1013 return true;
1014 }
1015
1016 /**
1017 * Checks if a fields value is within a range (min & max specified in
1018 * the vars attribute).
1019 *
1020 * @param bean The bean validation is being performed on.
1021 * @param va The <code>ValidatorAction</code> that is currently
1022 * being performed.
1023 * @param field The <code>Field</code> object associated with the
1024 * current field being validated.
1025 * @param errors The <code>ActionMessages</code> object to add errors
1026 * to if any validation errors occur.
1027 * @param validator The <code>Validator</code> instance, used to access
1028 * other field values.
1029 * @param request Current request object.
1030 * @return True if in range, false otherwise.
1031 */
1032 public static boolean validateDoubleRange(Object bean, ValidatorAction va,
1033 Field field, ActionMessages errors, Validator validator,
1034 HttpServletRequest request) {
1035 String value = null;
1036
1037 try {
1038 value = evaluateBean(bean, field);
1039 if (!GenericValidator.isBlankOrNull(value)) {
1040 String minVar =
1041 Resources.getVarValue("min", field, validator, request, true);
1042 String maxVar =
1043 Resources.getVarValue("max", field, validator, request, true);
1044 double doubleValue = Double.parseDouble(value);
1045 double min = Double.parseDouble(minVar);
1046 double max = Double.parseDouble(maxVar);
1047
1048 if (min > max) {
1049 throw new IllegalArgumentException(sysmsgs.getMessage(
1050 "invalid.range", minVar, maxVar));
1051 }
1052
1053 if (!GenericValidator.isInRange(doubleValue, min, max)) {
1054 errors.add(field.getKey(),
1055 Resources.getActionMessage(validator, request, va, field));
1056
1057 return false;
1058 }
1059 }
1060 } catch (Exception e) {
1061 processFailure(errors, field, validator.getFormName(), "doubleRange", e);
1062
1063 return false;
1064 }
1065
1066 return true;
1067 }
1068
1069 /**
1070 * Checks if a fields value is within a range (min & max specified in
1071 * the vars attribute).
1072 *
1073 * @param bean The bean validation is being performed on.
1074 * @param va The <code>ValidatorAction</code> that is currently
1075 * being performed.
1076 * @param field The <code>Field</code> object associated with the
1077 * current field being validated.
1078 * @param errors The <code>ActionMessages</code> object to add errors
1079 * to if any validation errors occur.
1080 * @param validator The <code>Validator</code> instance, used to access
1081 * other field values.
1082 * @param request Current request object.
1083 * @return True if in range, false otherwise.
1084 */
1085 public static boolean validateFloatRange(Object bean, ValidatorAction va,
1086 Field field, ActionMessages errors, Validator validator,
1087 HttpServletRequest request) {
1088 String value = null;
1089
1090 try {
1091 value = evaluateBean(bean, field);
1092 if (!GenericValidator.isBlankOrNull(value)) {
1093 String minVar =
1094 Resources.getVarValue("min", field, validator, request, true);
1095 String maxVar =
1096 Resources.getVarValue("max", field, validator, request, true);
1097 float floatValue = Float.parseFloat(value);
1098 float min = Float.parseFloat(minVar);
1099 float max = Float.parseFloat(maxVar);
1100
1101 if (min > max) {
1102 throw new IllegalArgumentException(sysmsgs.getMessage(
1103 "invalid.range", minVar, maxVar));
1104 }
1105
1106 if (!GenericValidator.isInRange(floatValue, min, max)) {
1107 errors.add(field.getKey(),
1108 Resources.getActionMessage(validator, request, va, field));
1109
1110 return false;
1111 }
1112 }
1113 } catch (Exception e) {
1114 processFailure(errors, field, validator.getFormName(), "floatRange", e);
1115
1116 return false;
1117 }
1118
1119 return true;
1120 }
1121
1122 /**
1123 * Checks if the field is a valid credit card number.
1124 *
1125 * @param bean The bean validation is being performed on.
1126 * @param va The <code>ValidatorAction</code> that is currently
1127 * being performed.
1128 * @param field The <code>Field</code> object associated with the
1129 * current field being validated.
1130 * @param errors The <code>ActionMessages</code> object to add errors
1131 * to if any validation errors occur.
1132 * @param validator The <code>Validator</code> instance, used to access
1133 * other field values.
1134 * @param request Current request object.
1135 * @return true if valid, false otherwise.
1136 */
1137 public static Object validateCreditCard(Object bean, ValidatorAction va,
1138 Field field, ActionMessages errors, Validator validator,
1139 HttpServletRequest request) {
1140 Object result = null;
1141 String value = null;
1142
1143 try {
1144 value = evaluateBean(bean, field);
1145 } catch (Exception e) {
1146 processFailure(errors, field, validator.getFormName(), "creditCard", e);
1147 return Boolean.FALSE;
1148 }
1149
1150 if (GenericValidator.isBlankOrNull(value)) {
1151 return Boolean.TRUE;
1152 }
1153
1154 result = GenericTypeValidator.formatCreditCard(value);
1155
1156 if (result == null) {
1157 errors.add(field.getKey(),
1158 Resources.getActionMessage(validator, request, va, field));
1159 }
1160
1161 return (result == null) ? Boolean.FALSE : result;
1162 }
1163
1164 /**
1165 * Checks if a field has a valid e-mail address.
1166 *
1167 * @param bean The bean validation is being performed on.
1168 * @param va The <code>ValidatorAction</code> that is currently
1169 * being performed.
1170 * @param field The <code>Field</code> object associated with the
1171 * current field being validated.
1172 * @param errors The <code>ActionMessages</code> object to add errors
1173 * to if any validation errors occur.
1174 * @param validator The <code>Validator</code> instance, used to access
1175 * other field values.
1176 * @param request Current request object.
1177 * @return True if valid, false otherwise.
1178 */
1179 public static boolean validateEmail(Object bean, ValidatorAction va,
1180 Field field, ActionMessages errors, Validator validator,
1181 HttpServletRequest request) {
1182 String value = null;
1183
1184 try {
1185 value = evaluateBean(bean, field);
1186 } catch (Exception e) {
1187 processFailure(errors, field, validator.getFormName(), "email", e);
1188 return false;
1189 }
1190
1191 if (!GenericValidator.isBlankOrNull(value)
1192 && !GenericValidator.isEmail(value)) {
1193 errors.add(field.getKey(),
1194 Resources.getActionMessage(validator, request, va, field));
1195
1196 return false;
1197 } else {
1198 return true;
1199 }
1200 }
1201
1202 /**
1203 * Checks if the field's length is less than or equal to the maximum
1204 * value. A <code>Null</code> will be considered an error.
1205 *
1206 * @param bean The bean validation is being performed on.
1207 * @param va The <code>ValidatorAction</code> that is currently
1208 * being performed.
1209 * @param field The <code>Field</code> object associated with the
1210 * current field being validated.
1211 * @param errors The <code>ActionMessages</code> object to add errors
1212 * to if any validation errors occur.
1213 * @param validator The <code>Validator</code> instance, used to access
1214 * other field values.
1215 * @param request Current request object.
1216 * @return True if stated conditions met.
1217 */
1218 public static boolean validateMaxLength(Object bean, ValidatorAction va,
1219 Field field, ActionMessages errors, Validator validator,
1220 HttpServletRequest request) {
1221 String value = null;
1222
1223 try {
1224 value = evaluateBean(bean, field);
1225 if (value != null) {
1226 String maxVar =
1227 Resources.getVarValue("maxlength", field, validator,
1228 request, true);
1229 int max = Integer.parseInt(maxVar);
1230
1231 boolean isValid = false;
1232 String endLth = Resources.getVarValue("lineEndLength", field,
1233 validator, request, false);
1234 if (GenericValidator.isBlankOrNull(endLth)) {
1235 isValid = GenericValidator.maxLength(value, max);
1236 } else {
1237 isValid = GenericValidator.maxLength(value, max,
1238 Integer.parseInt(endLth));
1239 }
1240
1241 if (!isValid) {
1242 errors.add(field.getKey(),
1243 Resources.getActionMessage(validator, request, va, field));
1244
1245 return false;
1246 }
1247 }
1248 } catch (Exception e) {
1249 processFailure(errors, field, validator.getFormName(), "maxlength", e);
1250
1251 return false;
1252 }
1253
1254 return true;
1255 }
1256
1257 /**
1258 * Checks if the field's length is greater than or equal to the minimum
1259 * value. A <code>Null</code> will be considered an error.
1260 *
1261 * @param bean The bean validation is being performed on.
1262 * @param va The <code>ValidatorAction</code> that is currently
1263 * being performed.
1264 * @param field The <code>Field</code> object associated with the
1265 * current field being validated.
1266 * @param errors The <code>ActionMessages</code> object to add errors
1267 * to if any validation errors occur.
1268 * @param validator The <code>Validator</code> instance, used to access
1269 * other field values.
1270 * @param request Current request object.
1271 * @return True if stated conditions met.
1272 */
1273 public static boolean validateMinLength(Object bean, ValidatorAction va,
1274 Field field, ActionMessages errors, Validator validator,
1275 HttpServletRequest request) {
1276 String value = null;
1277
1278 try {
1279 value = evaluateBean(bean, field);
1280 if (!GenericValidator.isBlankOrNull(value)) {
1281 String minVar =
1282 Resources.getVarValue("minlength", field, validator,
1283 request, true);
1284 int min = Integer.parseInt(minVar);
1285
1286 boolean isValid = false;
1287 String endLth = Resources.getVarValue("lineEndLength", field,
1288 validator, request, false);
1289 if (GenericValidator.isBlankOrNull(endLth)) {
1290 isValid = GenericValidator.minLength(value, min);
1291 } else {
1292 isValid = GenericValidator.minLength(value, min,
1293 Integer.parseInt(endLth));
1294 }
1295
1296 if (!isValid) {
1297 errors.add(field.getKey(),
1298 Resources.getActionMessage(validator, request, va, field));
1299
1300 return false;
1301 }
1302 }
1303 } catch (Exception e) {
1304 processFailure(errors, field, validator.getFormName(), "minlength", e);
1305
1306 return false;
1307 }
1308
1309 return true;
1310 }
1311
1312 /**
1313 * Checks if a field has a valid url. Four optional variables can be
1314 * specified to configure url validation.
1315 *
1316 * <ul>
1317 *
1318 * <li>Variable <code>allow2slashes</code> can be set to <code>true</code>
1319 * or <code>false</code> to control whether two slashes are allowed -
1320 * default is <code>false</code> (i.e. two slashes are NOT allowed).</li>
1321 *
1322 * <li>Variable <code>nofragments</code> can be set to <code>true</code>
1323 * or <code>false</code> to control whether fragments are allowed -
1324 * default is <code>false</code> (i.e. fragments ARE allowed).</li>
1325 *
1326 * <li>Variable <code>allowallschemes</code> can be set to
1327 * <code>true</code> or <code>false</code> to control if all schemes are
1328 * allowed - default is <code>false</code> (i.e. all schemes are NOT
1329 * allowed).</li>
1330 *
1331 * <li>Variable <code>schemes</code> can be set to a comma delimited list
1332 * of valid schemes. This value is ignored if <code>allowallschemes</code>
1333 * is set to <code>true</code>. Default schemes allowed are "http",
1334 * "https" and "ftp" if this variable is not specified.</li>
1335 *
1336 * </ul>
1337 *
1338 * @param bean The bean validation is being performed on.
1339 * @param va The <code>ValidatorAction</code> that is currently
1340 * being performed.
1341 * @param field The <code>Field</code> object associated with the
1342 * current field being validated.
1343 * @param errors The <code>ActionMessages</code> object to add errors
1344 * to if any validation errors occur.
1345 * @param validator The <code>Validator</code> instance, used to access
1346 * other field values.
1347 * @param request Current request object.
1348 * @return True if valid, false otherwise.
1349 */
1350 public static boolean validateUrl(Object bean, ValidatorAction va,
1351 Field field, ActionMessages errors, Validator validator,
1352 HttpServletRequest request) {
1353 String value = null;
1354
1355 try {
1356 value = evaluateBean(bean, field);
1357 } catch (Exception e) {
1358 processFailure(errors, field, validator.getFormName(), "url", e);
1359 return false;
1360 }
1361
1362 if (GenericValidator.isBlankOrNull(value)) {
1363 return true;
1364 }
1365
1366 // Get the options and schemes Vars
1367 String allowallschemesVar =
1368 Resources.getVarValue("allowallschemes", field, validator, request,
1369 false);
1370 boolean allowallschemes = "true".equalsIgnoreCase(allowallschemesVar);
1371 long options = allowallschemes ? UrlValidator.ALLOW_ALL_SCHEMES : 0;
1372
1373 String allow2slashesVar =
1374 Resources.getVarValue("allow2slashes", field, validator, request,
1375 false);
1376
1377 if ("true".equalsIgnoreCase(allow2slashesVar)) {
1378 options += UrlValidator.ALLOW_2_SLASHES;
1379 }
1380
1381 String nofragmentsVar =
1382 Resources.getVarValue("nofragments", field, validator, request,
1383 false);
1384
1385 if ("true".equalsIgnoreCase(nofragmentsVar)) {
1386 options += UrlValidator.NO_FRAGMENTS;
1387 }
1388
1389 String schemesVar =
1390 allowallschemes ? null
1391 : Resources.getVarValue("schemes", field,
1392 validator, request, false);
1393
1394 // No options or schemes - use GenericValidator as default
1395 if ((options == 0) && (schemesVar == null)) {
1396 if (GenericValidator.isUrl(value)) {
1397 return true;
1398 } else {
1399 errors.add(field.getKey(),
1400 Resources.getActionMessage(validator, request, va, field));
1401
1402 return false;
1403 }
1404 }
1405
1406 // Parse comma delimited list of schemes into a String[]
1407 String[] schemes = null;
1408
1409 if (schemesVar != null) {
1410 StringTokenizer st = new StringTokenizer(schemesVar, ",");
1411
1412 schemes = new String[st.countTokens()];
1413
1414 int i = 0;
1415
1416 while (st.hasMoreTokens()) {
1417 schemes[i++] = st.nextToken().trim();
1418 }
1419 }
1420
1421 // Create UrlValidator and validate with options/schemes
1422 UrlValidator urlValidator = new UrlValidator(schemes, options);
1423
1424 if (urlValidator.isValid(value)) {
1425 return true;
1426 } else {
1427 errors.add(field.getKey(),
1428 Resources.getActionMessage(validator, request, va, field));
1429
1430 return false;
1431 }
1432 }
1433
1434 /**
1435 * Process a validation failure.
1436 */
1437 private static void processFailure(ActionMessages errors, Field field,
1438 String formName, String validatorName, Throwable t) {
1439 // Logger the error
1440 LOG.atError()
1441 .setMessage(() -> sysmsgs.getMessage("validation.failed",
1442 validatorName, field.getProperty(), formName, t.toString()))
1443 .setCause(t).log();
1444
1445 // Add general "system error" message to show to the user
1446 String userErrorMsg = sysmsgs.getMessage("system.error");
1447
1448 errors.add(field.getKey(), new ActionMessage(userErrorMsg, false));
1449 }
1450
1451 /**
1452 * Return <code>true</code> if the specified object is a String or a
1453 * <code>null</code> value.
1454 *
1455 * @param o Object to be tested
1456 * @return The string value
1457 */
1458 protected static boolean isString(Object o) {
1459 return (o == null) ? true : String.class.isInstance(o);
1460 }
1461 }