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.GenericValidator;
22  import org.apache.commons.validator.routines.checkdigit.CheckDigit;
23  
24  /**
25   * Generic <b>Code Validation</b> 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 <i>format</i> of the code using a <i>regular expression.</i> (if specified)</li>
33   *   <li>Check the <i>minimum</i> and <i>maximum</i> length  (if specified) of the <i>parsed</i> code
34   *      (i.e. parsed by the <i>regular expression</i>).</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   * <b>Note</b>
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)</code></li>
58   *       <li><code>String validate(code)</code></li>
59   *    </ul>
60   * <p>
61   * Codes often include <i>format</i> 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 <i>non-capturing</i> group in the regular
64   * expression (i.e. use the <code>(?:   )</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,
88       * validator and {@link CheckDigit} validation.
89       *
90       * @param regexValidator The format regular expression validator
91       * @param checkdigit The check digit validation routine.
92       */
93      public CodeValidator(final RegexValidator regexValidator, final CheckDigit checkdigit) {
94          this(regexValidator, -1, -1, checkdigit);
95      }
96  
97      /**
98       * Constructs a code validator with a specified regular expression,
99       * validator, length and {@link CheckDigit} validation.
100      *
101      * @param regexValidator The format regular expression validator
102      * @param length The length of the code
103      *  (sets the mimimum/maximum to the same value)
104      * @param checkdigit The check digit validation routine
105      */
106     public CodeValidator(final RegexValidator regexValidator, final int length, final CheckDigit checkdigit) {
107         this(regexValidator, length, length, checkdigit);
108     }
109 
110     /**
111      * Constructs a code validator with a specified regular expression
112      * validator, minimum/maximum length and {@link CheckDigit} validation.
113      *
114      * @param regexValidator The format regular expression validator
115      * @param minLength The minimum length of the code
116      * @param maxLength The maximum length of the code
117      * @param checkdigit The check digit validation routine
118      */
119     public CodeValidator(final RegexValidator regexValidator, final int minLength, final int maxLength,
120             final CheckDigit checkdigit) {
121         this.regexValidator = regexValidator;
122         this.minLength = minLength;
123         this.maxLength = maxLength;
124         this.checkdigit = checkdigit;
125     }
126 
127     /**
128      * Constructs a code validator with a specified regular
129      * expression and {@link CheckDigit}.
130      * The RegexValidator validator is created to be case-sensitive
131      *
132      * @param regex The format regular expression
133      * @param checkdigit The check digit validation routine
134      */
135     public CodeValidator(final String regex, final CheckDigit checkdigit) {
136         this(regex, -1, -1, checkdigit);
137     }
138 
139     /**
140      * Constructs a code validator with a specified regular
141      * expression, length and {@link CheckDigit}.
142      * The RegexValidator validator is created to be case-sensitive
143      *
144      * @param regex The format regular expression.
145      * @param length The length of the code
146      *  (sets the mimimum/maximum to the same)
147      * @param checkdigit The check digit validation routine
148      */
149     public CodeValidator(final String regex, final int length, final CheckDigit checkdigit) {
150         this(regex, length, length, checkdigit);
151     }
152 
153     /**
154      * Constructs a code validator with a specified regular
155      * expression, minimum/maximum length and {@link CheckDigit} validation.
156      * The RegexValidator validator is created to be case-sensitive
157      *
158      * @param regex The regular expression
159      * @param minLength The minimum length of the code
160      * @param maxLength The maximum length of the code
161      * @param checkdigit The check digit validation routine
162      */
163     public CodeValidator(final String regex, final int minLength, final int maxLength,
164             final CheckDigit checkdigit) {
165         if (!GenericValidator.isBlankOrNull(regex)) {
166             this.regexValidator = new RegexValidator(regex);
167         } else {
168             this.regexValidator = null;
169         }
170         this.minLength = minLength;
171         this.maxLength = maxLength;
172         this.checkdigit = checkdigit;
173     }
174 
175     /**
176      * Gets the check digit validation routine.
177      * <p>
178      * <b>N.B.</b> Optional, if not set no Check Digit
179      * validation will be performed on the code.
180      *
181      * @return The check digit validation routine
182      */
183     public CheckDigit getCheckDigit() {
184         return checkdigit;
185     }
186 
187     /**
188      * Gets the maximum length of the code.
189      * <p>
190      * <b>N.B.</b> Optional, if less than zero the
191      * maximum length will not be checked.
192      *
193      * @return The maximum length of the code or
194      * <code>-1</code> if the code has no maximum length
195      */
196     public int getMaxLength() {
197         return maxLength;
198     }
199 
200     /**
201      * Gets the minimum length of the code.
202      * <p>
203      * <b>N.B.</b> Optional, if less than zero the
204      * minimum length will not be checked.
205      *
206      * @return The minimum length of the code or
207      * <code>-1</code> if the code has no minimum length
208      */
209     public int getMinLength() {
210         return minLength;
211     }
212 
213     /**
214      * Gets the <i>regular expression</i> validator.
215      * <p>
216      * <b>N.B.</b> Optional, if not set no regular
217      * expression validation will be performed on the code.
218      *
219      * @return The regular expression validator
220      */
221     public RegexValidator getRegexValidator() {
222         return regexValidator;
223     }
224 
225     /**
226      * Validate the code returning either {@code true}
227      * or {@code false}.
228      * <p>
229      * This calls {@link #validate(String)} and returns false
230      * if the return value is null, true otherwise.
231      * <p>
232      * Note that {@link #validate(String)} trims the input
233      * and if there is a {@link RegexValidator} it may also
234      * change the input as part of the validation.
235      *
236      * @param input The code to validate
237      * @return {@code true} if valid, otherwise
238      * {@code false}
239      */
240     public boolean isValid(final String input) {
241         return validate(input) != null;
242     }
243 
244     /**
245      * Validate the code returning either the valid code or
246      * {@code null} if invalid.
247      * <p>
248      * Note that this method trims the input
249      * and if there is a {@link RegexValidator} it may also
250      * change the input as part of the validation.
251      *
252      * @param input The code to validate
253      * @return The code if valid, otherwise {@code null}
254      * if invalid
255      */
256     public Object validate(final String input) {
257         if (input == null) {
258             return null;
259         }
260         String code = input.trim();
261         if (code.isEmpty()) {
262             return null;
263         }
264         // validate/reformat using regular expression
265         if (regexValidator != null) {
266             code = regexValidator.validate(code);
267             if (code == null) {
268                 return null;
269             }
270         }
271         // check the length (must be done after validate as that can change the code)
272         if (minLength >= 0 && code.length() < minLength ||
273             maxLength >= 0 && code.length() > maxLength) {
274             return null;
275         }
276         // validate the check digit
277         if (checkdigit != null && !checkdigit.isValid(code)) {
278             return null;
279         }
280         return code;
281     }
282 
283 }