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 }