001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.validator.routines;
018
019import java.io.Serializable;
020
021import org.apache.commons.validator.GenericValidator;
022import org.apache.commons.validator.routines.checkdigit.CheckDigit;
023
024/**
025 * Generic <b>Code Validation</b> providing format, minimum/maximum
026 * length and {@link CheckDigit} validations.
027 * <p>
028 * Performs the following validations on a code:
029 * <ul>
030 *   <li>if the code is null, return null/false as appropriate</li>
031 *   <li>trim the input. If the resulting code is empty, return null/false as appropriate</li>
032 *   <li>Check the <i>format</i> of the code using a <i>regular expression.</i> (if specified)</li>
033 *   <li>Check the <i>minimum</i> and <i>maximum</i> length  (if specified) of the <i>parsed</i> code
034 *      (i.e. parsed by the <i>regular expression</i>).</li>
035 *   <li>Performs {@link CheckDigit} validation on the parsed code (if specified).</li>
036 *   <li>The {@link #validate(String)} method returns the trimmed, parsed input (or null if validation failed)</li>
037 * </ul>
038 * <p>
039 * <b>Note</b>
040 * The {@link #isValid(String)} method will return true if the input passes validation.
041 * Since this includes trimming as well as potentially dropping parts of the input,
042 * it is possible for a String to pass validation
043 * but fail the checkdigit test if passed directly to it (the check digit routines generally don't trim input
044 * nor do they generally check the format/length).
045 * To be sure that you are passing valid input to a method use {@link #validate(String)} as follows:
046 * <pre>
047 * Object valid = validator.validate(input);
048 * if (valid != null) {
049 *    some_method(valid.toString());
050 * }
051 * </pre>
052 * <p>
053 * Configure the validator with the appropriate regular expression, minimum/maximum length
054 * and {@link CheckDigit} validator and then call one of the two validation
055 * methods provided:</p>
056 *    <ul>
057 *       <li><code>boolean isValid(code)</code></li>
058 *       <li><code>String validate(code)</code></li>
059 *    </ul>
060 * <p>
061 * Codes often include <i>format</i> characters - such as hyphens - to make them
062 * more easily human readable. These can be removed prior to length and check digit
063 * validation by  specifying them as a <i>non-capturing</i> group in the regular
064 * expression (i.e. use the <code>(?:   )</code> notation).
065 * <br>
066 * Or just avoid using parentheses except for the parts you want to capture
067 *
068 * @since 1.4
069 */
070public final class CodeValidator implements Serializable {
071
072    private static final long serialVersionUID = 446960910870938233L;
073
074    /** The format regular expression validator. */
075    private final RegexValidator regexValidator;
076
077    /** The minimum length of the code. */
078    private final int minLength;
079
080    /** The maximum length of the code. */
081    private final int maxLength;
082
083    /** The check digit validation routine. */
084    private final CheckDigit checkdigit;
085
086    /**
087     * Constructs a code validator with a specified regular expression,
088     * validator and {@link CheckDigit} validation.
089     *
090     * @param regexValidator The format regular expression validator
091     * @param checkdigit The check digit validation routine.
092     */
093    public CodeValidator(final RegexValidator regexValidator, final CheckDigit checkdigit) {
094        this(regexValidator, -1, -1, checkdigit);
095    }
096
097    /**
098     * Constructs a code validator with a specified regular expression,
099     * 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}