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>Check the <i>format</i> of the code using a <i>regular expression.</i> (if specified)</li>
030 *   <li>Check the <i>minimum</i> and <i>maximum</i> length  (if specified) of the <i>parsed</i> code
031 *      (i.e. parsed by the <i>regular expression</i>).</li>
032 *   <li>Performs {@link CheckDigit} validation on the parsed code (if specified).</li>
033 * </ul>
034 * <p>
035 * Configure the validator with the appropriate regular expression, minimum/maximum length
036 * and {@link CheckDigit} validator and then call one of the two validation
037 * methods provided:</p>
038 *    <ul>
039 *       <li><code>boolean isValid(code)</code></li>
040 *       <li><code>String validate(code)</code></li>
041 *    </ul>
042 * <p>
043 * Codes often include <i>format</i> characters - such as hyphens - to make them
044 * more easily human readable. These can be removed prior to length and check digit
045 * validation by  specifying them as a <i>non-capturing</i> group in the regular
046 * expression (i.e. use the <code>(?:   )</code> notation).
047 *
048 * @version $Revision: 1227719 $ $Date: 2012-01-05 18:45:51 +0100 (Do, 05 Jan 2012) $
049 * @since Validator 1.4
050 */
051public final class CodeValidator implements Serializable {
052
053    private static final long serialVersionUID = 446960910870938233L;
054
055    private final RegexValidator regexValidator;
056    private final int minLength;
057    private final int maxLength;
058    private final CheckDigit checkdigit;
059
060    /**
061     * Construct a code validator with a specified regular
062     * expression and {@link CheckDigit}.
063     *
064     * @param regex The format regular expression
065     * @param checkdigit The check digit validation routine
066     */
067    public CodeValidator(String regex, CheckDigit checkdigit) {
068        this(regex, -1, -1, checkdigit);
069    }
070
071    /**
072     * Construct a code validator with a specified regular
073     * expression, length and {@link CheckDigit}.
074     *
075     * @param regex The format regular expression.
076     * @param length The length of the code
077     *  (sets the mimimum/maximum to the same)
078     * @param checkdigit The check digit validation routine
079     */
080    public CodeValidator(String regex, int length, CheckDigit checkdigit) {
081        this(regex, length, length, checkdigit);
082    }
083
084    /**
085     * Construct a code validator with a specified regular
086     * expression, minimum/maximum length and {@link CheckDigit} validation.
087     *
088     * @param regex The regular expression validator
089     * @param minLength The minimum length of the code
090     * @param maxLength The maximum length of the code
091     * @param checkdigit The check digit validation routine
092     */
093    public CodeValidator(String regex, int minLength, int maxLength,
094            CheckDigit checkdigit) {
095        if (regex != null && regex.length() > 0) {
096            this.regexValidator = new RegexValidator(regex);
097        } else {
098            this.regexValidator = null;
099        }
100        this.minLength = minLength;
101        this.maxLength = maxLength;
102        this.checkdigit = checkdigit;
103    }
104
105    /**
106     * Construct a code validator with a specified regular expression,
107     * validator and {@link CheckDigit} validation.
108     *
109     * @param regexValidator The format regular expression validator
110     * @param checkdigit The check digit validation routine.
111     */
112    public CodeValidator(RegexValidator regexValidator, CheckDigit checkdigit) {
113        this(regexValidator, -1, -1, checkdigit);
114    }
115
116    /**
117     * Construct a code validator with a specified regular expression,
118     * validator, length and {@link CheckDigit} validation.
119     *
120     * @param regexValidator The format regular expression validator
121     * @param length The length of the code
122     *  (sets the mimimum/maximum to the same value)
123     * @param checkdigit The check digit validation routine
124     */
125    public CodeValidator(RegexValidator regexValidator, int length, CheckDigit checkdigit) {
126        this(regexValidator, length, length, checkdigit);
127    }
128
129    /**
130     * Construct a code validator with a specified regular expression
131     * validator, minimum/maximum length and {@link CheckDigit} validation.
132     *
133     * @param regexValidator The format regular expression validator
134     * @param minLength The minimum length of the code
135     * @param maxLength The maximum length of the code
136     * @param checkdigit The check digit validation routine
137     */
138    public CodeValidator(RegexValidator regexValidator, int minLength, int maxLength,
139            CheckDigit checkdigit) {
140        this.regexValidator = regexValidator;
141        this.minLength = minLength;
142        this.maxLength = maxLength;
143        this.checkdigit = checkdigit;
144    }
145
146    /**
147     * Return the check digit validation routine.
148     * <p>
149     * <b>N.B.</b> Optional, if not set no Check Digit
150     * validation will be performed on the code.
151     *
152     * @return The check digit validation routine
153     */
154    public CheckDigit getCheckDigit() {
155        return checkdigit;
156    }
157
158    /**
159     * Return the minimum length of the code.
160     * <p>
161     * <b>N.B.</b> Optional, if less than zero the
162     * minimum length will not be checked.
163     *
164     * @return The minimum length of the code or
165     * <code>-1</code> if the code has no minimum length
166     */
167    public int getMinLength() {
168        return minLength;
169    }
170
171    /**
172     * Return the maximum length of the code.
173     * <p>
174     * <b>N.B.</b> Optional, if less than zero the
175     * maximum length will not be checked.
176     *
177     * @return The maximum length of the code or
178     * <code>-1</code> if the code has no maximum length
179     */
180    public int getMaxLength() {
181        return maxLength;
182    }
183
184    /**
185     * Return the <i>regular expression</i> validator.
186     * <p>
187     * <b>N.B.</b> Optional, if not set no regular
188     * expression validation will be performed on the code.
189     *
190     * @return The regular expression validator
191     */
192    public RegexValidator getRegexValidator() {
193        return regexValidator;
194    }
195
196    /**
197     * Validate the code returning either <code>true</code>
198     * or <code>false</code>.
199     *
200     * @param input The code to validate
201     * @return <code>true</code> if valid, otherwise
202     * <code>false</code>
203     */
204    public boolean isValid(String input) {
205        return (validate(input) != null);
206    }
207
208    /**
209     * Validate the code returning either the valid code or
210     * <code>null</code> if invalid.
211     *
212     * @param input The code to validate
213     * @return The code if valid, otherwise <code>null</code>
214     * if invalid
215     */
216    public Object validate(String input) {
217
218        if (input == null) {
219            return null;
220        }
221
222        String code = input.trim();
223        if (code.length() == 0) {
224            return null;
225        }
226
227        // validate/reformat using regular expression
228        if (regexValidator != null) {
229            code = regexValidator.validate(code);
230            if (code == null) {
231                return null;
232            }
233        }
234
235        // check the length
236        if ((minLength >= 0 && code.length() < minLength) ||
237            (maxLength >= 0 && code.length() > maxLength)) {
238            return null;
239        }
240
241        // validate the check digit
242        if (checkdigit != null && !checkdigit.isValid(code)) {
243            return null;
244        }
245
246        return code;
247
248    }
249
250}