View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.validator.routines;
18  
19  import java.io.Serializable;
20  
21  import org.apache.commons.validator.GenericValidator;
22  import org.apache.commons.validator.routines.checkdigit.CheckDigit;
23  
24  /**
25   * Generic <strong>Code Validation</strong> providing format, minimum/maximum
26   * length and {@link CheckDigit} validations.
27   * <p>
28   * Performs the following validations on a code:
29   * <ul>
30   *   <li>if the code is null, return null/false as appropriate</li>
31   *   <li>trim the input. If the resulting code is empty, return null/false as appropriate</li>
32   *   <li>Check the <em>format</em> of the code using a <em>regular expression.</em> (if specified)</li>
33   *   <li>Check the <em>minimum</em> and <em>maximum</em> length  (if specified) of the <em>parsed</em> code
34   *      (that is, parsed by the <em>regular expression</em>).</li>
35   *   <li>Performs {@link CheckDigit} validation on the parsed code (if specified).</li>
36   *   <li>The {@link #validate(String)} method returns the trimmed, parsed input (or null if validation failed)</li>
37   * </ul>
38   * <p>
39   * <strong>Note</strong>
40   * The {@link #isValid(String)} method will return true if the input passes validation.
41   * Since this includes trimming as well as potentially dropping parts of the input,
42   * it is possible for a String to pass validation
43   * but fail the checkdigit test if passed directly to it (the check digit routines generally don't trim input
44   * nor do they generally check the format/length).
45   * To be sure that you are passing valid input to a method use {@link #validate(String)} as follows:
46   * <pre>
47   * Object valid = validator.validate(input);
48   * if (valid != null) {
49   *    some_method(valid.toString());
50   * }
51   * </pre>
52   * <p>
53   * Configure the validator with the appropriate regular expression, minimum/maximum length
54   * and {@link CheckDigit} validator and then call one of the two validation
55   * methods provided:</p>
56   *    <ul>
57   *       <li>{@code boolean isValid(code)}</li>
58   *       <li>{@code String validate(code)}</li>
59   *    </ul>
60   * <p>
61   * Codes often include <em>format</em> characters - such as hyphens - to make them
62   * more easily human-readable. These can be removed prior to length and check digit
63   * validation by specifying them as a <em>non-capturing</em> group in the regular
64   * expression (that is, use the {@code (?:   )} notation).
65   * <br>
66   * Or just avoid using parentheses except for the parts you want to capture
67   *
68   * @since 1.4
69   */
70  public final class CodeValidator implements Serializable {
71  
72      private static final long serialVersionUID = 446960910870938233L;
73  
74      /** The format regular expression validator. */
75      private final RegexValidator regexValidator;
76  
77      /** The minimum length of the code. */
78      private final int minLength;
79  
80      /** The maximum length of the code. */
81      private final int maxLength;
82  
83      /** The check digit validation routine. */
84      private final CheckDigit checkdigit;
85  
86      /**
87       * Constructs a code validator with a specified regular expression, validator and {@link CheckDigit} validation.
88       *
89       * @param regexValidator The format regular expression validator.
90       * @param checkdigit     The check digit validation routine.
91       */
92      public CodeValidator(final RegexValidator regexValidator, final CheckDigit checkdigit) {
93          this(regexValidator, -1, -1, checkdigit);
94      }
95  
96      /**
97       * Constructs a code validator with a specified regular expression, validator, length and {@link CheckDigit} validation.
98       *
99       * @param regexValidator The format regular expression validator.
100      * @param length         The length of the code. (sets the minimum/maximum to the same value).
101      * @param checkdigit     The check digit validation routine.
102      */
103     public CodeValidator(final RegexValidator regexValidator, final int length, final CheckDigit checkdigit) {
104         this(regexValidator, length, length, checkdigit);
105     }
106 
107     /**
108      * Constructs a code validator with a specified regular expression validator, minimum/maximum length and {@link CheckDigit} validation.
109      *
110      * @param regexValidator The format regular expression validator.
111      * @param minLength      The minimum length of the code.
112      * @param maxLength      The maximum length of the code.
113      * @param checkdigit     The check digit validation routine.
114      */
115     public CodeValidator(final RegexValidator regexValidator, final int minLength, final int maxLength, final CheckDigit checkdigit) {
116         this.regexValidator = regexValidator;
117         this.minLength = minLength;
118         this.maxLength = maxLength;
119         this.checkdigit = checkdigit;
120     }
121 
122     /**
123      * Constructs a code validator with a specified regular expression and {@link CheckDigit}. The RegexValidator validator is created to be case-sensitive
124      *
125      * @param regex      The format regular expression.
126      * @param checkdigit The check digit validation routine.
127      */
128     public CodeValidator(final String regex, final CheckDigit checkdigit) {
129         this(regex, -1, -1, checkdigit);
130     }
131 
132     /**
133      * Constructs a code validator with a specified regular expression, length and {@link CheckDigit}. The RegexValidator validator is created to be
134      * case-sensitive
135      *
136      * @param regex      The format regular expression.
137      * @param length     The length of the code (sets the minimum/maximum to the same).
138      * @param checkdigit The check digit validation routine.
139      */
140     public CodeValidator(final String regex, final int length, final CheckDigit checkdigit) {
141         this(regex, length, length, checkdigit);
142     }
143 
144     /**
145      * Constructs a code validator with a specified regular expression, minimum/maximum length and {@link CheckDigit} validation. The RegexValidator validator
146      * is created to be case-sensitive
147      *
148      * @param regex      The regular expression.
149      * @param minLength  The minimum length of the code.
150      * @param maxLength  The maximum length of the code.
151      * @param checkdigit The check digit validation routine.
152      */
153     public CodeValidator(final String regex, final int minLength, final int maxLength, final CheckDigit checkdigit) {
154         this.regexValidator = GenericValidator.isBlankOrNull(regex) ? null : new RegexValidator(regex);
155         this.minLength = minLength;
156         this.maxLength = maxLength;
157         this.checkdigit = checkdigit;
158     }
159 
160     /**
161      * Gets the check digit validation routine.
162      * <p>
163      * <strong>N.B.</strong> Optional, if not set no Check Digit validation will be performed on the code.
164      *
165      * @return The check digit validation routine
166      */
167     public CheckDigit getCheckDigit() {
168         return checkdigit;
169     }
170 
171     /**
172      * Gets the maximum length of the code.
173      * <p>
174      * <strong>N.B.</strong> Optional, if less than zero the maximum length will not be checked.
175      * </p>
176      *
177      * @return The maximum length of the code or {@code -1} if the code has no maximum length
178      */
179     public int getMaxLength() {
180         return maxLength;
181     }
182 
183     /**
184      * Gets the minimum length of the code.
185      * <p>
186      * <strong>N.B.</strong> Optional, if less than zero the minimum length will not be checked.
187      * </p>
188      *
189      * @return The minimum length of the code or {@code -1} if the code has no minimum length.
190      */
191     public int getMinLength() {
192         return minLength;
193     }
194 
195     /**
196      * Gets the <em>regular expression</em> validator.
197      * <p>
198      * <strong>N.B.</strong> Optional, if not set no regular expression validation will be performed on the code.
199      * </p>
200      *
201      * @return The regular expression validator.
202      */
203     public RegexValidator getRegexValidator() {
204         return regexValidator;
205     }
206 
207     /**
208      * Validates the code returning either {@code true} or {@code false}.
209      * <p>
210      * This calls {@link #validate(String)} and returns false if the return value is null, true otherwise.
211      * </p>
212      * <p>
213      * Note that {@link #validate(String)} trims the input and if there is a {@link RegexValidator} it may also change the input as part of the validation.
214      * </p>
215      *
216      * @param input The code to validate.
217      * @return {@code true} if valid, otherwise {@code false}.
218      */
219     public boolean isValid(final String input) {
220         return validate(input) != null;
221     }
222 
223     /**
224      * Validates the code returning either the valid code or {@code null} if invalid.
225      * <p>
226      * Note that this method trims the input and if there is a {@link RegexValidator} it may also change the input as part of the validation.
227      * </p>
228      *
229      * @param input The code to validate.
230      * @return The code if valid, otherwise {@code null} if invalid.
231      */
232     public Object validate(final String input) {
233         if (input == null) {
234             return null;
235         }
236         String code = input.trim();
237         if (code.isEmpty()) {
238             return null;
239         }
240         // validate/reformat using regular expression
241         if (regexValidator != null) {
242             code = regexValidator.validate(code);
243             if (code == null) {
244                 return null;
245             }
246         }
247         // check the length (must be done after validate as that can change the code)
248         if (minLength >= 0 && code.length() < minLength || maxLength >= 0 && code.length() > maxLength) {
249             return null;
250         }
251         // validate the check digit
252         if (checkdigit != null && !checkdigit.isValid(code)) {
253             return null;
254         }
255         return code;
256     }
257 
258 }