    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}