    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.routines.checkdigit.CheckDigit;
    022
    023/**
    024 * Generic <b>Code Validation</b> providing format, minimum/maximum
    025 * length and {@link CheckDigit} validations.
    026 * <p>
    027 * Performs the following validations on a code:
    028 * <ul>
    029 *   <li>if the code is null, return null/false as appropriate</li>
    030 *   <li>trim the input. If the resulting code is empty, return null/false as appropriate</li>
    031 *   <li>Check the <i>format</i> of the code using a <i>regular expression.</i> (if specified)</li>
    032 *   <li>Check the <i>minimum</i> and <i>maximum</i> length  (if specified) of the <i>parsed</i> code
    033 *      (i.e. parsed by the <i>regular expression</i>).</li>
    034 *   <li>Performs {@link CheckDigit} validation on the parsed code (if specified).</li>
    035 *   <li>The {@link #validate(String)} method returns the trimmed, parsed input (or null if validation failed)</li>
    036 * </ul>
    037 * <p>
    038 * <b>Note</b>
    039 * The {@link #isValid(String)} method will return true if the input passes validation.
    040 * Since this includes trimming as well as potentially dropping parts of the input,
    041 * it is possible for a String to pass validation
    042 * but fail the checkdigit test if passed directly to it (the check digit routines generally don't trim input
    043 * nor do they generally check the format/length).
    044 * To be sure that you are passing valid input to a method use {@link #validate(String)} as follows:
    045 * <pre>
    046 * Object valid = validator.validate(input);
    047 * if (valid != null) {
    048 *    some_method(valid.toString());
    049 * }
    050 * </pre>
    051 * <p>
    052 * Configure the validator with the appropriate regular expression, minimum/maximum length
    053 * and {@link CheckDigit} validator and then call one of the two validation
    054 * methods provided:</p>
    055 *    <ul>
    056 *       <li><code>boolean isValid(code)</code></li>
    057 *       <li><code>String validate(code)</code></li>
    058 *    </ul>
    059 * <p>
    060 * Codes often include <i>format</i> characters - such as hyphens - to make them
    061 * more easily human readable. These can be removed prior to length and check digit
    062 * validation by  specifying them as a <i>non-capturing</i> group in the regular
    063 * expression (i.e. use the <code>(?:   )</code> notation).
    064 * <br>
    065 * Or just avoid using parentheses except for the parts you want to capture
    066 *
    067 * @since 1.4
    068 */
    069public final class CodeValidator implements Serializable {
    070
    071    private static final long serialVersionUID = 446960910870938233L;
    072
    073    /** The format regular expression validator. */
    074    private final RegexValidator regexValidator;
    075
    076    /** The minimum length of the code. */
    077    private final int minLength;
    078
    079    /** The maximum length of the code. */
    080    private final int maxLength;
    081
    082    /** The check digit validation routine. */
    083    private final CheckDigit checkdigit;
    084
    085    /**
    086     * Constructs a code validator with a specified regular expression,
    087     * validator and {@link CheckDigit} validation.
    088     *
    089     * @param regexValidator The format regular expression validator
    090     * @param checkdigit The check digit validation routine.
    091     */
    092    public CodeValidator(final RegexValidator regexValidator, final CheckDigit checkdigit) {
    093        this(regexValidator, -1, -1, checkdigit);
    094    }
    095
    096    /**
    097     * Constructs a code validator with a specified regular expression,
    098     * validator, length and {@link CheckDigit} validation.
    099     *
    100     * @param regexValidator The format regular expression validator
    101     * @param length The length of the code
    102     *  (sets the mimimum/maximum to the same value)
    103     * @param checkdigit The check digit validation routine
    104     */
    105    public CodeValidator(final RegexValidator regexValidator, final int length, final CheckDigit checkdigit) {
    106        this(regexValidator, length, length, checkdigit);
    107    }
    108
    109    /**
    110     * Constructs a code validator with a specified regular expression
    111     * validator, minimum/maximum length and {@link CheckDigit} validation.
    112     *
    113     * @param regexValidator The format regular expression validator
    114     * @param minLength The minimum length of the code
    115     * @param maxLength The maximum length of the code
    116     * @param checkdigit The check digit validation routine
    117     */
    118    public CodeValidator(final RegexValidator regexValidator, final int minLength, final int maxLength,
    119            final CheckDigit checkdigit) {
    120        this.regexValidator = regexValidator;
    121        this.minLength = minLength;
    122        this.maxLength = maxLength;
    123        this.checkdigit = checkdigit;
    124    }
    125
    126    /**
    127     * Constructs a code validator with a specified regular
    128     * expression and {@link CheckDigit}.
    129     * The RegexValidator validator is created to be case-sensitive
    130     *
    131     * @param regex The format regular expression
    132     * @param checkdigit The check digit validation routine
    133     */
    134    public CodeValidator(final String regex, final CheckDigit checkdigit) {
    135        this(regex, -1, -1, checkdigit);
    136    }
    137
    138    /**
    139     * Constructs a code validator with a specified regular
    140     * expression, length and {@link CheckDigit}.
    141     * The RegexValidator validator is created to be case-sensitive
    142     *
    143     * @param regex The format regular expression.
    144     * @param length The length of the code
    145     *  (sets the mimimum/maximum to the same)
    146     * @param checkdigit The check digit validation routine
    147     */
    148    public CodeValidator(final String regex, final int length, final CheckDigit checkdigit) {
    149        this(regex, length, length, checkdigit);
    150    }
    151
    152    /**
    153     * Constructs a code validator with a specified regular
    154     * expression, minimum/maximum length and {@link CheckDigit} validation.
    155     * The RegexValidator validator is created to be case-sensitive
    156     *
    157     * @param regex The regular expression
    158     * @param minLength The minimum length of the code
    159     * @param maxLength The maximum length of the code
    160     * @param checkdigit The check digit validation routine
    161     */
    162    public CodeValidator(final String regex, final int minLength, final int maxLength,
    163            final CheckDigit checkdigit) {
    164        if (regex != null && !regex.isEmpty()) {
    165            this.regexValidator = new RegexValidator(regex);
    166        } else {
    167            this.regexValidator = null;
    168        }
    169        this.minLength = minLength;
    170        this.maxLength = maxLength;
    171        this.checkdigit = checkdigit;
    172    }
    173
    174    /**
    175     * Return the check digit validation routine.
    176     * <p>
    177     * <b>N.B.</b> Optional, if not set no Check Digit
    178     * validation will be performed on the code.
    179     *
    180     * @return The check digit validation routine
    181     */
    182    public CheckDigit getCheckDigit() {
    183        return checkdigit;
    184    }
    185
    186    /**
    187     * Return the maximum length of the code.
    188     * <p>
    189     * <b>N.B.</b> Optional, if less than zero the
    190     * maximum length will not be checked.
    191     *
    192     * @return The maximum length of the code or
    193     * <code>-1</code> if the code has no maximum length
    194     */
    195    public int getMaxLength() {
    196        return maxLength;
    197    }
    198
    199    /**
    200     * Return the minimum length of the code.
    201     * <p>
    202     * <b>N.B.</b> Optional, if less than zero the
    203     * minimum length will not be checked.
    204     *
    205     * @return The minimum length of the code or
    206     * <code>-1</code> if the code has no minimum length
    207     */
    208    public int getMinLength() {
    209        return minLength;
    210    }
    211
    212    /**
    213     * Return the <i>regular expression</i> validator.
    214     * <p>
    215     * <b>N.B.</b> Optional, if not set no regular
    216     * expression validation will be performed on the code.
    217     *
    218     * @return The regular expression validator
    219     */
    220    public RegexValidator getRegexValidator() {
    221        return regexValidator;
    222    }
    223
    224    /**
    225     * Validate the code returning either {@code true}
    226     * or {@code false}.
    227     * <p>
    228     * This calls {@link #validate(String)} and returns false
    229     * if the return value is null, true otherwise.
    230     * <p>
    231     * Note that {@link #validate(String)} trims the input
    232     * and if there is a {@link RegexValidator} it may also
    233     * change the input as part of the validation.
    234     *
    235     * @param input The code to validate
    236     * @return {@code true} if valid, otherwise
    237     * {@code false}
    238     */
    239    public boolean isValid(final String input) {
    240        return validate(input) != null;
    241    }
    242
    243    /**
    244     * Validate the code returning either the valid code or
    245     * <code>null</code> if invalid.
    246     * <p>
    247     * Note that this method trims the input
    248     * and if there is a {@link RegexValidator} it may also
    249     * change the input as part of the validation.
    250     *
    251     * @param input The code to validate
    252     * @return The code if valid, otherwise <code>null</code>
    253     * if invalid
    254     */
    255    public Object validate(final String input) {
    256
    257        if (input == null) {
    258            return null;
    259        }
    260
    261        String code = input.trim();
    262        if (code.isEmpty()) {
    263            return null;
    264        }
    265
    266        // validate/reformat using regular expression
    267        if (regexValidator != null) {
    268            code = regexValidator.validate(code);
    269            if (code == null) {
    270                return null;
    271            }
    272        }
    273
    274        // check the length (must be done after validate as that can change the code)
    275        if (minLength >= 0 && code.length() < minLength ||
    276            maxLength >= 0 && code.length() > maxLength) {
    277            return null;
    278        }
    279
    280        // validate the check digit
    281        if (checkdigit != null && !checkdigit.isValid(code)) {
    282            return null;
    283        }
    284
    285        return code;
    286
    287    }
    288
    289}