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    *      http://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.routines.checkdigit.CheckDigit;
22  
23  /**
24   * Generic <b>Code Validation</b> providing format, minimum/maximum
25   * length and {@link CheckDigit} validations.
26   * <p>
27   * Performs the following validations on a code:
28   * <ul>
29   *   <li>if the code is null, return null/false as appropriate</li>
30   *   <li>trim the input. If the resulting code is empty, return null/false as appropriate</li>
31   *   <li>Check the <i>format</i> of the code using a <i>regular expression.</i> (if specified)</li>
32   *   <li>Check the <i>minimum</i> and <i>maximum</i> length  (if specified) of the <i>parsed</i> code
33   *      (i.e. parsed by the <i>regular expression</i>).</li>
34   *   <li>Performs {@link CheckDigit} validation on the parsed code (if specified).</li>
35   *   <li>The {@link #validate(String)} method returns the trimmed, parsed input (or null if validation failed)</li>
36   * </ul>
37   * <p>
38   * <b>Note</b>
39   * The {@link #isValid(String)} method will return true if the input passes validation.
40   * Since this includes trimming as well as potentially dropping parts of the input,
41   * it is possible for a String to pass validation
42   * but fail the checkdigit test if passed directly to it (the check digit routines generally don't trim input
43   * nor do they generally check the format/length).
44   * To be sure that you are passing valid input to a method use {@link #validate(String)} as follows:
45   * <pre>
46   * Object valid = validator.validate(input);
47   * if (valid != null) {
48   *    some_method(valid.toString());
49   * }
50   * </pre>
51   * <p>
52   * Configure the validator with the appropriate regular expression, minimum/maximum length
53   * and {@link CheckDigit} validator and then call one of the two validation
54   * methods provided:</p>
55   *    <ul>
56   *       <li><code>boolean isValid(code)</code></li>
57   *       <li><code>String validate(code)</code></li>
58   *    </ul>
59   * <p>
60   * Codes often include <i>format</i> characters - such as hyphens - to make them
61   * more easily human readable. These can be removed prior to length and check digit
62   * validation by  specifying them as a <i>non-capturing</i> group in the regular
63   * expression (i.e. use the <code>(?:   )</code> notation).
64   * <br>
65   * Or just avoid using parentheses except for the parts you want to capture
66   *
67   * @since 1.4
68   */
69  public final class CodeValidator implements Serializable {
70  
71      private static final long serialVersionUID = 446960910870938233L;
72  
73      /** The format regular expression validator. */
74      private final RegexValidator regexValidator;
75  
76      /** The minimum length of the code. */
77      private final int minLength;
78  
79      /** The maximum length of the code. */
80      private final int maxLength;
81  
82      /** The check digit validation routine. */
83      private final CheckDigit checkdigit;
84  
85      /**
86       * Constructs a code validator with a specified regular expression,
87       * 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,
98       * validator, length and {@link CheckDigit} validation.
99       *
100      * @param regexValidator The format regular expression validator
101      * @param length The length of the code
102      *  (sets the mimimum/maximum to the same value)
103      * @param checkdigit The check digit validation routine
104      */
105     public CodeValidator(final RegexValidator regexValidator, final int length, final CheckDigit checkdigit) {
106         this(regexValidator, length, length, checkdigit);
107     }
108 
109     /**
110      * Constructs a code validator with a specified regular expression
111      * validator, minimum/maximum length and {@link CheckDigit} validation.
112      *
113      * @param regexValidator The format regular expression validator
114      * @param minLength The minimum length of the code
115      * @param maxLength The maximum length of the code
116      * @param checkdigit The check digit validation routine
117      */
118     public CodeValidator(final RegexValidator regexValidator, final int minLength, final int maxLength,
119             final CheckDigit checkdigit) {
120         this.regexValidator = regexValidator;
121         this.minLength = minLength;
122         this.maxLength = maxLength;
123         this.checkdigit = checkdigit;
124     }
125 
126     /**
127      * Constructs a code validator with a specified regular
128      * expression and {@link CheckDigit}.
129      * The RegexValidator validator is created to be case-sensitive
130      *
131      * @param regex The format regular expression
132      * @param checkdigit The check digit validation routine
133      */
134     public CodeValidator(final String regex, final CheckDigit checkdigit) {
135         this(regex, -1, -1, checkdigit);
136     }
137 
138     /**
139      * Constructs a code validator with a specified regular
140      * expression, length and {@link CheckDigit}.
141      * The RegexValidator validator is created to be case-sensitive
142      *
143      * @param regex The format regular expression.
144      * @param length The length of the code
145      *  (sets the mimimum/maximum to the same)
146      * @param checkdigit The check digit validation routine
147      */
148     public CodeValidator(final String regex, final int length, final CheckDigit checkdigit) {
149         this(regex, length, length, checkdigit);
150     }
151 
152     /**
153      * Constructs a code validator with a specified regular
154      * expression, minimum/maximum length and {@link CheckDigit} validation.
155      * The RegexValidator validator is created to be case-sensitive
156      *
157      * @param regex The regular expression
158      * @param minLength The minimum length of the code
159      * @param maxLength The maximum length of the code
160      * @param checkdigit The check digit validation routine
161      */
162     public CodeValidator(final String regex, final int minLength, final int maxLength,
163             final CheckDigit checkdigit) {
164         if (regex != null && !regex.isEmpty()) {
165             this.regexValidator = new RegexValidator(regex);
166         } else {
167             this.regexValidator = null;
168         }
169         this.minLength = minLength;
170         this.maxLength = maxLength;
171         this.checkdigit = checkdigit;
172     }
173 
174     /**
175      * Return the check digit validation routine.
176      * <p>
177      * <b>N.B.</b> Optional, if not set no Check Digit
178      * validation will be performed on the code.
179      *
180      * @return The check digit validation routine
181      */
182     public CheckDigit getCheckDigit() {
183         return checkdigit;
184     }
185 
186     /**
187      * Return the maximum length of the code.
188      * <p>
189      * <b>N.B.</b> Optional, if less than zero the
190      * maximum length will not be checked.
191      *
192      * @return The maximum length of the code or
193      * <code>-1</code> if the code has no maximum length
194      */
195     public int getMaxLength() {
196         return maxLength;
197     }
198 
199     /**
200      * Return the minimum length of the code.
201      * <p>
202      * <b>N.B.</b> Optional, if less than zero the
203      * minimum length will not be checked.
204      *
205      * @return The minimum length of the code or
206      * <code>-1</code> if the code has no minimum length
207      */
208     public int getMinLength() {
209         return minLength;
210     }
211 
212     /**
213      * Return the <i>regular expression</i> validator.
214      * <p>
215      * <b>N.B.</b> Optional, if not set no regular
216      * expression validation will be performed on the code.
217      *
218      * @return The regular expression validator
219      */
220     public RegexValidator getRegexValidator() {
221         return regexValidator;
222     }
223 
224     /**
225      * Validate the code returning either {@code true}
226      * or {@code false}.
227      * <p>
228      * This calls {@link #validate(String)} and returns false
229      * if the return value is null, true otherwise.
230      * <p>
231      * Note that {@link #validate(String)} trims the input
232      * and if there is a {@link RegexValidator} it may also
233      * change the input as part of the validation.
234      *
235      * @param input The code to validate
236      * @return {@code true} if valid, otherwise
237      * {@code false}
238      */
239     public boolean isValid(final String input) {
240         return validate(input) != null;
241     }
242 
243     /**
244      * Validate the code returning either the valid code or
245      * <code>null</code> if invalid.
246      * <p>
247      * Note that this method trims the input
248      * and if there is a {@link RegexValidator} it may also
249      * change the input as part of the validation.
250      *
251      * @param input The code to validate
252      * @return The code if valid, otherwise <code>null</code>
253      * if invalid
254      */
255     public Object validate(final String input) {
256 
257         if (input == null) {
258             return null;
259         }
260 
261         String code = input.trim();
262         if (code.isEmpty()) {
263             return null;
264         }
265 
266         // validate/reformat using regular expression
267         if (regexValidator != null) {
268             code = regexValidator.validate(code);
269             if (code == null) {
270                 return null;
271             }
272         }
273 
274         // check the length (must be done after validate as that can change the code)
275         if (minLength >= 0 && code.length() < minLength ||
276             maxLength >= 0 && code.length() > maxLength) {
277             return null;
278         }
279 
280         // validate the check digit
281         if (checkdigit != null && !checkdigit.isValid(code)) {
282             return null;
283         }
284 
285         return code;
286 
287     }
288 
289 }