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 {