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,
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 minimum/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 minimum/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 this.regexValidator = GenericValidator.isBlankOrNull(regex) ? null : new RegexValidator(regex);
166 this.minLength = minLength;
167 this.maxLength = maxLength;
168 this.checkdigit = checkdigit;
169 }
170
171 /**
172 * Gets the check digit validation routine.
173 * <p>
174 * <strong>N.B.</strong> Optional, if not set no Check Digit
175 * validation will be performed on the code.
176 *
177 * @return The check digit validation routine
178 */
179 public CheckDigit getCheckDigit() {
180 return checkdigit;
181 }
182
183 /**
184 * Gets the maximum length of the code.
185 * <p>
186 * <strong>N.B.</strong> Optional, if less than zero the
187 * maximum length will not be checked.
188 *
189 * @return The maximum length of the code or
190 * {@code -1} if the code has no maximum length
191 */
192 public int getMaxLength() {
193 return maxLength;
194 }
195
196 /**
197 * Gets the minimum length of the code.
198 * <p>
199 * <strong>N.B.</strong> Optional, if less than zero the
200 * minimum length will not be checked.
201 *
202 * @return The minimum length of the code or
203 * {@code -1} if the code has no minimum length
204 */
205 public int getMinLength() {
206 return minLength;
207 }
208
209 /**
210 * Gets the <em>regular expression</em> validator.
211 * <p>
212 * <strong>N.B.</strong> Optional, if not set no regular
213 * expression validation will be performed on the code.
214 *
215 * @return The regular expression validator
216 */
217 public RegexValidator getRegexValidator() {
218 return regexValidator;
219 }
220
221 /**
222 * Validate the code returning either {@code true}
223 * or {@code false}.
224 * <p>
225 * This calls {@link #validate(String)} and returns false
226 * if the return value is null, true otherwise.
227 * <p>
228 * Note that {@link #validate(String)} trims the input
229 * and if there is a {@link RegexValidator} it may also
230 * change the input as part of the validation.
231 *
232 * @param input The code to validate
233 * @return {@code true} if valid, otherwise
234 * {@code false}
235 */
236 public boolean isValid(final String input) {
237 return validate(input) != null;
238 }
239
240 /**
241 * Validate the code returning either the valid code or
242 * {@code null} if invalid.
243 * <p>
244 * Note that this method trims the input
245 * and if there is a {@link RegexValidator} it may also
246 * change the input as part of the validation.
247 *
248 * @param input The code to validate
249 * @return The code if valid, otherwise {@code null}
250 * if invalid
251 */
252 public Object validate(final String input) {
253 if (input == null) {
254 return null;
255 }
256 String code = input.trim();
257 if (code.isEmpty()) {
258 return null;
259 }
260 // validate/reformat using regular expression
261 if (regexValidator != null) {
262 code = regexValidator.validate(code);
263 if (code == null) {
264 return null;
265 }
266 }
267 // check the length (must be done after validate as that can change the code)
268 if (minLength >= 0 && code.length() < minLength ||
269 maxLength >= 0 && code.length() > maxLength) {
270 return null;
271 }
272 // validate the check digit
273 if (checkdigit != null && !checkdigit.isValid(code)) {
274 return null;
275 }
276 return code;
277 }
278
279 }