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   * @version $Revision: 1713388 $
68   * @since Validator 1.4
69   */
70  public final class CodeValidator implements Serializable {
71  
72      private static final long serialVersionUID = 446960910870938233L;
73  
74      private final RegexValidator regexValidator;
75      private final int minLength;
76      private final int maxLength;
77      private final CheckDigit checkdigit;
78  
79      /**
80       * Construct a code validator with a specified regular
81       * expression and {@link CheckDigit}.
82       * The RegexValidator validator is created to be case-sensitive
83       *
84       * @param regex The format regular expression
85       * @param checkdigit The check digit validation routine
86       */
87      public CodeValidator(String regex, CheckDigit checkdigit) {
88          this(regex, -1, -1, checkdigit);
89      }
90  
91      /**
92       * Construct a code validator with a specified regular
93       * expression, length and {@link CheckDigit}.
94       * The RegexValidator validator is created to be case-sensitive
95       *
96       * @param regex The format regular expression.
97       * @param length The length of the code
98       *  (sets the mimimum/maximum to the same)
99       * @param checkdigit The check digit validation routine
100      */
101     public CodeValidator(String regex, int length, CheckDigit checkdigit) {
102         this(regex, length, length, checkdigit);
103     }
104 
105     /**
106      * Construct a code validator with a specified regular
107      * expression, minimum/maximum length and {@link CheckDigit} validation.
108      * The RegexValidator validator is created to be case-sensitive
109      *
110      * @param regex The regular expression
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(String regex, int minLength, int maxLength,
116             CheckDigit checkdigit) {
117         if (regex != null && regex.length() > 0) {
118             this.regexValidator = new RegexValidator(regex);
119         } else {
120             this.regexValidator = null;
121         }
122         this.minLength = minLength;
123         this.maxLength = maxLength;
124         this.checkdigit = checkdigit;
125     }
126 
127     /**
128      * Construct a code validator with a specified regular expression,
129      * validator and {@link CheckDigit} validation.
130      *
131      * @param regexValidator The format regular expression validator
132      * @param checkdigit The check digit validation routine.
133      */
134     public CodeValidator(RegexValidator regexValidator, CheckDigit checkdigit) {
135         this(regexValidator, -1, -1, checkdigit);
136     }
137 
138     /**
139      * Construct a code validator with a specified regular expression,
140      * validator, length and {@link CheckDigit} validation.
141      *
142      * @param regexValidator The format regular expression validator
143      * @param length The length of the code
144      *  (sets the mimimum/maximum to the same value)
145      * @param checkdigit The check digit validation routine
146      */
147     public CodeValidator(RegexValidator regexValidator, int length, CheckDigit checkdigit) {
148         this(regexValidator, length, length, checkdigit);
149     }
150 
151     /**
152      * Construct a code validator with a specified regular expression
153      * validator, minimum/maximum length and {@link CheckDigit} validation.
154      *
155      * @param regexValidator The format regular expression validator
156      * @param minLength The minimum length of the code
157      * @param maxLength The maximum length of the code
158      * @param checkdigit The check digit validation routine
159      */
160     public CodeValidator(RegexValidator regexValidator, int minLength, int maxLength,
161             CheckDigit checkdigit) {
162         this.regexValidator = regexValidator;
163         this.minLength = minLength;
164         this.maxLength = maxLength;
165         this.checkdigit = checkdigit;
166     }
167 
168     /**
169      * Return the check digit validation routine.
170      * <p>
171      * <b>N.B.</b> Optional, if not set no Check Digit
172      * validation will be performed on the code.
173      *
174      * @return The check digit validation routine
175      */
176     public CheckDigit getCheckDigit() {
177         return checkdigit;
178     }
179 
180     /**
181      * Return the minimum length of the code.
182      * <p>
183      * <b>N.B.</b> Optional, if less than zero the
184      * minimum length will not be checked.
185      *
186      * @return The minimum length of the code or
187      * <code>-1</code> if the code has no minimum length
188      */
189     public int getMinLength() {
190         return minLength;
191     }
192 
193     /**
194      * Return the maximum length of the code.
195      * <p>
196      * <b>N.B.</b> Optional, if less than zero the
197      * maximum length will not be checked.
198      *
199      * @return The maximum length of the code or
200      * <code>-1</code> if the code has no maximum length
201      */
202     public int getMaxLength() {
203         return maxLength;
204     }
205 
206     /**
207      * Return the <i>regular expression</i> validator.
208      * <p>
209      * <b>N.B.</b> Optional, if not set no regular
210      * expression validation will be performed on the code.
211      *
212      * @return The regular expression validator
213      */
214     public RegexValidator getRegexValidator() {
215         return regexValidator;
216     }
217 
218     /**
219      * Validate the code returning either <code>true</code>
220      * or <code>false</code>.
221      *
222      * @param input The code to validate
223      * @return <code>true</code> if valid, otherwise
224      * <code>false</code>
225      */
226     public boolean isValid(String input) {
227         return (validate(input) != null);
228     }
229 
230     /**
231      * Validate the code returning either the valid code or
232      * <code>null</code> if invalid.
233      *
234      * @param input The code to validate
235      * @return The code if valid, otherwise <code>null</code>
236      * if invalid
237      */
238     public Object validate(String input) {
239 
240         if (input == null) {
241             return null;
242         }
243 
244         String code = input.trim();
245         if (code.length() == 0) {
246             return null;
247         }
248 
249         // validate/reformat using regular expression
250         if (regexValidator != null) {
251             code = regexValidator.validate(code);
252             if (code == null) {
253                 return null;
254             }
255         }
256 
257         // check the length
258         if ((minLength >= 0 && code.length() < minLength) ||
259             (maxLength >= 0 && code.length() > maxLength)) {
260             return null;
261         }
262 
263         // validate the check digit
264         if (checkdigit != null && !checkdigit.isValid(code)) {
265             return null;
266         }
267 
268         return code;
269 
270     }
271 
272 }