diff --git a/src/main/java/org/apache/commons/validator/routines/CodeValidator.java b/src/main/java/org/apache/commons/validator/routines/CodeValidator.java new file mode 100644 index 0000000..d07beb9 --- /dev/null +++ b/src/main/java/org/apache/commons/validator/routines/CodeValidator.java @@ -0,0 +1,279 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.validator.routines; + +import java.io.Serializable; + +import org.apache.commons.validator.GenericValidator; +import org.apache.commons.validator.routines.checkdigit.CheckDigit; + +/** + * Generic Code Validation providing format, minimum/maximum + * length and {@link CheckDigit} validations. + *

+ * Performs the following validations on a code: + *

+ *

+ * Note + * The {@link #isValid(String)} method will return true if the input passes validation. + * Since this includes trimming as well as potentially dropping parts of the input, + * it is possible for a String to pass validation + * but fail the checkdigit test if passed directly to it (the check digit routines generally don't trim input + * nor do they generally check the format/length). + * To be sure that you are passing valid input to a method use {@link #validate(String)} as follows: + *

+ * Object valid = validator.validate(input);
+ * if (valid != null) {
+ *    some_method(valid.toString());
+ * }
+ * 
+ *

+ * Configure the validator with the appropriate regular expression, minimum/maximum length + * and {@link CheckDigit} validator and then call one of the two validation + * methods provided:

+ * + *

+ * Codes often include format characters - such as hyphens - to make them + * more easily human readable. These can be removed prior to length and check digit + * validation by specifying them as a non-capturing group in the regular + * expression (i.e. use the {@code (?: )} notation). + *
+ * Or just avoid using parentheses except for the parts you want to capture + * + * @since 1.4 + */ +public final class CodeValidator implements Serializable { + + private static final long serialVersionUID = 446960910870938233L; + + /** The format regular expression validator. */ + private final RegexValidator regexValidator; + + /** The minimum length of the code. */ + private final int minLength; + + /** The maximum length of the code. */ + private final int maxLength; + + /** The check digit validation routine. */ + private final CheckDigit checkdigit; + + /** + * Constructs a code validator with a specified regular expression, + * validator and {@link CheckDigit} validation. + * + * @param regexValidator The format regular expression validator + * @param checkdigit The check digit validation routine. + */ + public CodeValidator(final RegexValidator regexValidator, final CheckDigit checkdigit) { + this(regexValidator, -1, -1, checkdigit); + } + + /** + * Constructs a code validator with a specified regular expression, + * validator, length and {@link CheckDigit} validation. + * + * @param regexValidator The format regular expression validator + * @param length The length of the code + * (sets the minimum/maximum to the same value) + * @param checkdigit The check digit validation routine + */ + public CodeValidator(final RegexValidator regexValidator, final int length, final CheckDigit checkdigit) { + this(regexValidator, length, length, checkdigit); + } + + /** + * Constructs a code validator with a specified regular expression + * validator, minimum/maximum length and {@link CheckDigit} validation. + * + * @param regexValidator The format regular expression validator + * @param minLength The minimum length of the code + * @param maxLength The maximum length of the code + * @param checkdigit The check digit validation routine + */ + public CodeValidator(final RegexValidator regexValidator, final int minLength, final int maxLength, + final CheckDigit checkdigit) { + this.regexValidator = regexValidator; + this.minLength = minLength; + this.maxLength = maxLength; + this.checkdigit = checkdigit; + } + + /** + * Constructs a code validator with a specified regular + * expression and {@link CheckDigit}. + * The RegexValidator validator is created to be case-sensitive + * + * @param regex The format regular expression + * @param checkdigit The check digit validation routine + */ + public CodeValidator(final String regex, final CheckDigit checkdigit) { + this(regex, -1, -1, checkdigit); + } + + /** + * Constructs a code validator with a specified regular + * expression, length and {@link CheckDigit}. + * The RegexValidator validator is created to be case-sensitive + * + * @param regex The format regular expression. + * @param length The length of the code + * (sets the minimum/maximum to the same) + * @param checkdigit The check digit validation routine + */ + public CodeValidator(final String regex, final int length, final CheckDigit checkdigit) { + this(regex, length, length, checkdigit); + } + + /** + * Constructs a code validator with a specified regular + * expression, minimum/maximum length and {@link CheckDigit} validation. + * The RegexValidator validator is created to be case-sensitive + * + * @param regex The regular expression + * @param minLength The minimum length of the code + * @param maxLength The maximum length of the code + * @param checkdigit The check digit validation routine + */ + public CodeValidator(final String regex, final int minLength, final int maxLength, + final CheckDigit checkdigit) { + this.regexValidator = GenericValidator.isBlankOrNull(regex) ? null : new RegexValidator(regex); + this.minLength = minLength; + this.maxLength = maxLength; + this.checkdigit = checkdigit; + } + + /** + * Gets the check digit validation routine. + *

+ * N.B. Optional, if not set no Check Digit + * validation will be performed on the code. + * + * @return The check digit validation routine + */ + public CheckDigit getCheckDigit() { + return checkdigit; + } + + /** + * Gets the maximum length of the code. + *

+ * N.B. Optional, if less than zero the + * maximum length will not be checked. + * + * @return The maximum length of the code or + * {@code -1} if the code has no maximum length + */ + public int getMaxLength() { + return maxLength; + } + + /** + * Gets the minimum length of the code. + *

+ * N.B. Optional, if less than zero the + * minimum length will not be checked. + * + * @return The minimum length of the code or + * {@code -1} if the code has no minimum length + */ + public int getMinLength() { + return minLength; + } + + /** + * Gets the regular expression validator. + *

+ * N.B. Optional, if not set no regular + * expression validation will be performed on the code. + * + * @return The regular expression validator + */ + public RegexValidator getRegexValidator() { + return regexValidator; + } + + /** + * Validate the code returning either {@code true} + * or {@code false}. + *

+ * This calls {@link #validate(String)} and returns false + * if the return value is null, true otherwise. + *

+ * 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. + * + * @param input The code to validate + * @return {@code true} if valid, otherwise + * {@code false} + */ + public boolean isValid(final String input) { + return validate(input) != null; + } + + /** + * Validate the code returning either the valid code or + * {@code null} if invalid. + *

+ * 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. + * + * @param input The code to validate + * @return The code if valid, otherwise {@code null} + * if invalid + */ + public Object validate(final String input) { + if (input == null) { + return null; + } + String code = input.trim(); + if (code.isEmpty()) { + return null; + } + // validate/reformat using regular expression + if (regexValidator != null) { + code = regexValidator.validate(code); + if (code == null) { + return null; + } + } + // check the length (must be done after validate as that can change the code) + if (minLength >= 0 && code.length() < minLength || + maxLength >= 0 && code.length() > maxLength) { + return null; + } + // validate the check digit + if (checkdigit != null && !checkdigit.isValid(code)) { + return null; + } + return code; + } + +} diff --git a/src/main/java/org/apache/commons/validator/routines/LeitwegValidator.java b/src/main/java/org/apache/commons/validator/routines/LeitwegValidator.java index 34392e1..cfefa68 100644 --- a/src/main/java/org/apache/commons/validator/routines/LeitwegValidator.java +++ b/src/main/java/org/apache/commons/validator/routines/LeitwegValidator.java @@ -16,8 +16,6 @@ */ package org.apache.commons.validator.routines; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.commons.validator.routines.checkdigit.Modulus97CheckDigit; /** @@ -46,16 +44,21 @@ */ public class LeitwegValidator { - private static final Log LOG = LogFactory.getLog(LeitwegValidator.class); - - private final Validator formatValidator; - private static final char MINUS = '\u002D'; // '-' Separator - private static final Validator DEFAULT_FORMAT = - new Validator("^(01|02|03|04|05|06|07|08|09|10|11|12|13|14|16|99)((\\d)((\\d{2})(\\d{3}|\\d{4}|\\d{7})?)?)?" - + "(-([A-Za-z0-9]{1,30}))?" // optional alphanumeric detail - + "-(\\d{2})$"); // two mandatory check digits + private static final String STARTWITHREGION = "^(01|02|03|04|05|06|07|08|09|10|11|12|13|14|16|99)"; + private static final String OPTIONAL_DETAIL = MINUS + "([A-Za-z0-9]{1,30})?"; // optional alphanumeric detail + private static final String MD_CHECK_DIGITS = MINUS + "(\\d{2})$"; // two mandatory check digits + // Regierungsbezirk (\\d) + Landkreis (\\d{2}) + Gemeinde (3, 4 oder 7 Stellen) + private static final String FORMAT1 = STARTWITHREGION + "(\\d)?(\\d{2})?" + + OPTIONAL_DETAIL + MD_CHECK_DIGITS; + private static final String FORMAT2 = STARTWITHREGION + "(\\d\\d{2}\\d{7})" + + OPTIONAL_DETAIL + MD_CHECK_DIGITS; + private static final String FORMAT3 = STARTWITHREGION + "(\\d\\d{2}\\d{4})" + + OPTIONAL_DETAIL + MD_CHECK_DIGITS; + private static final String FORMAT4 = STARTWITHREGION + "(\\d\\d{2}\\d{3})" + + OPTIONAL_DETAIL + MD_CHECK_DIGITS; + private static final String[] FORMAT = new String[] {FORMAT1, FORMAT2, FORMAT3, FORMAT4}; /* * in theory the shortest Leitweg-ID has a minimal general part and check digits @@ -71,24 +74,10 @@ public class LeitwegValidator { */ private static final int MAX_CODE_LEN = 44; - /** - * The validation class - */ - public static class Validator { - final RegexValidator validator; - - /** - * Creates the format validator - * - * @param format the regex to use to check the format - */ - public Validator(String format) { - this.validator = new RegexValidator(format); - } - } + private static final CodeValidator VALIDATOR = new CodeValidator(new RegexValidator(FORMAT), MIN_CODE_LEN, MAX_CODE_LEN, Modulus97CheckDigit.getInstance()); /** The singleton instance which uses the default formats */ - public static final LeitwegValidator DEFAULT_LEITWEG_VALIDATOR = new LeitwegValidator(); + private static final LeitwegValidator DEFAULT_LEITWEG_VALIDATOR = new LeitwegValidator(); /** * Return a singleton instance of the validator using the default formats @@ -99,21 +88,6 @@ public static LeitwegValidator getInstance() { return DEFAULT_LEITWEG_VALIDATOR; } - /** - * Create a default format validator. - */ - public LeitwegValidator() { - this.formatValidator = DEFAULT_FORMAT; - } - - /** - * Retuens the RegexValidator for the format - * @return formatValidator - */ - public RegexValidator getFormatValidator() { - return formatValidator.validator; - } - /** * Validate a Leitweg-ID * @@ -121,24 +95,7 @@ public RegexValidator getFormatValidator() { * @return true if the value is valid */ public boolean isValid(String id) { - - id = id.trim(); - if (id == null || id.length() > MAX_CODE_LEN || id.length() < MIN_CODE_LEN) { - if (LOG.isDebugEnabled()) { - LOG.debug("format length error for " + id); - } - return false; - } - - // format check: - // der RegexValidator kann mehrere pattern prüfen!!! - if (!formatValidator.validator.isValid(id)) { - if (LOG.isDebugEnabled()) { - LOG.debug("format " + id + " is NOT valid."); - } - return false; - } - return Modulus97CheckDigit.getInstance().isValid(removeMinus(id)); + return VALIDATOR.isValid(id); } private String removeMinus(String id) { diff --git a/src/main/java/org/apache/commons/validator/routines/checkdigit/CheckDigit.java b/src/main/java/org/apache/commons/validator/routines/checkdigit/CheckDigit.java index 91d0778..24d5e40 100644 --- a/src/main/java/org/apache/commons/validator/routines/checkdigit/CheckDigit.java +++ b/src/main/java/org/apache/commons/validator/routines/checkdigit/CheckDigit.java @@ -16,8 +16,6 @@ */ package org.apache.commons.validator.routines.checkdigit; -//import org.apache.commons.validator.routines.CodeValidator; - /** * Check Digit calculation and validation. *

@@ -40,7 +38,7 @@ * Package Summary for a full * list of implementations provided within Commons Validator. * -// * @see org.apache.commons.validator.routines.CodeValidator + * @see org.apache.commons.validator.routines.CodeValidator * @since 1.4 */ public interface CheckDigit {