View Javadoc
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 &amp; 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 &amp; 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 &amp; 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 &amp; 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 }