Skip to content

Commit daad58e

Browse files
committed
Leitweg Validator
1 parent 36ef106 commit daad58e

File tree

3 files changed

+296
-62
lines changed

3 files changed

+296
-62
lines changed
Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.commons.validator.routines;
18+
19+
import java.io.Serializable;
20+
21+
import org.apache.commons.validator.GenericValidator;
22+
import org.apache.commons.validator.routines.checkdigit.CheckDigit;
23+
24+
/**
25+
* Generic <strong>Code Validation</strong> providing format, minimum/maximum
26+
* length and {@link CheckDigit} validations.
27+
* <p>
28+
* Performs the following validations on a code:
29+
* <ul>
30+
* <li>if the code is null, return null/false as appropriate</li>
31+
* <li>trim the input. If the resulting code is empty, return null/false as appropriate</li>
32+
* <li>Check the <em>format</em> of the code using a <em>regular expression.</em> (if specified)</li>
33+
* <li>Check the <em>minimum</em> and <em>maximum</em> length (if specified) of the <em>parsed</em> code
34+
* (i.e. parsed by the <em>regular expression</em>).</li>
35+
* <li>Performs {@link CheckDigit} validation on the parsed code (if specified).</li>
36+
* <li>The {@link #validate(String)} method returns the trimmed, parsed input (or null if validation failed)</li>
37+
* </ul>
38+
* <p>
39+
* <strong>Note</strong>
40+
* The {@link #isValid(String)} method will return true if the input passes validation.
41+
* Since this includes trimming as well as potentially dropping parts of the input,
42+
* it is possible for a String to pass validation
43+
* but fail the checkdigit test if passed directly to it (the check digit routines generally don't trim input
44+
* nor do they generally check the format/length).
45+
* To be sure that you are passing valid input to a method use {@link #validate(String)} as follows:
46+
* <pre>
47+
* Object valid = validator.validate(input);
48+
* if (valid != null) {
49+
* some_method(valid.toString());
50+
* }
51+
* </pre>
52+
* <p>
53+
* Configure the validator with the appropriate regular expression, minimum/maximum length
54+
* and {@link CheckDigit} validator and then call one of the two validation
55+
* methods provided:</p>
56+
* <ul>
57+
* <li>{@code boolean isValid(code)}</li>
58+
* <li>{@code String validate(code)}</li>
59+
* </ul>
60+
* <p>
61+
* Codes often include <em>format</em> characters - such as hyphens - to make them
62+
* more easily human readable. These can be removed prior to length and check digit
63+
* validation by specifying them as a <em>non-capturing</em> group in the regular
64+
* expression (i.e. use the {@code (?: )} notation).
65+
* <br>
66+
* Or just avoid using parentheses except for the parts you want to capture
67+
*
68+
* @since 1.4
69+
*/
70+
public final class CodeValidator implements Serializable {
71+
72+
private static final long serialVersionUID = 446960910870938233L;
73+
74+
/** The format regular expression validator. */
75+
private final RegexValidator regexValidator;
76+
77+
/** The minimum length of the code. */
78+
private final int minLength;
79+
80+
/** The maximum length of the code. */
81+
private final int maxLength;
82+
83+
/** The check digit validation routine. */
84+
private final CheckDigit checkdigit;
85+
86+
/**
87+
* Constructs a code validator with a specified regular expression,
88+
* validator and {@link CheckDigit} validation.
89+
*
90+
* @param regexValidator The format regular expression validator
91+
* @param checkdigit The check digit validation routine.
92+
*/
93+
public CodeValidator(final RegexValidator regexValidator, final CheckDigit checkdigit) {
94+
this(regexValidator, -1, -1, checkdigit);
95+
}
96+
97+
/**
98+
* Constructs a code validator with a specified regular expression,
99+
* 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 minimum/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 minimum/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+
this.regexValidator = GenericValidator.isBlankOrNull(regex) ? null : new RegexValidator(regex);
166+
this.minLength = minLength;
167+
this.maxLength = maxLength;
168+
this.checkdigit = checkdigit;
169+
}
170+
171+
/**
172+
* Gets the check digit validation routine.
173+
* <p>
174+
* <strong>N.B.</strong> Optional, if not set no Check Digit
175+
* validation will be performed on the code.
176+
*
177+
* @return The check digit validation routine
178+
*/
179+
public CheckDigit getCheckDigit() {
180+
return checkdigit;
181+
}
182+
183+
/**
184+
* Gets the maximum length of the code.
185+
* <p>
186+
* <strong>N.B.</strong> Optional, if less than zero the
187+
* maximum length will not be checked.
188+
*
189+
* @return The maximum length of the code or
190+
* {@code -1} if the code has no maximum length
191+
*/
192+
public int getMaxLength() {
193+
return maxLength;
194+
}
195+
196+
/**
197+
* Gets the minimum length of the code.
198+
* <p>
199+
* <strong>N.B.</strong> Optional, if less than zero the
200+
* minimum length will not be checked.
201+
*
202+
* @return The minimum length of the code or
203+
* {@code -1} if the code has no minimum length
204+
*/
205+
public int getMinLength() {
206+
return minLength;
207+
}
208+
209+
/**
210+
* Gets the <em>regular expression</em> validator.
211+
* <p>
212+
* <strong>N.B.</strong> Optional, if not set no regular
213+
* expression validation will be performed on the code.
214+
*
215+
* @return The regular expression validator
216+
*/
217+
public RegexValidator getRegexValidator() {
218+
return regexValidator;
219+
}
220+
221+
/**
222+
* Validate the code returning either {@code true}
223+
* or {@code false}.
224+
* <p>
225+
* This calls {@link #validate(String)} and returns false
226+
* if the return value is null, true otherwise.
227+
* <p>
228+
* Note that {@link #validate(String)} trims the input
229+
* and if there is a {@link RegexValidator} it may also
230+
* change the input as part of the validation.
231+
*
232+
* @param input The code to validate
233+
* @return {@code true} if valid, otherwise
234+
* {@code false}
235+
*/
236+
public boolean isValid(final String input) {
237+
return validate(input) != null;
238+
}
239+
240+
/**
241+
* Validate the code returning either the valid code or
242+
* {@code null} if invalid.
243+
* <p>
244+
* Note that this method trims the input
245+
* and if there is a {@link RegexValidator} it may also
246+
* change the input as part of the validation.
247+
*
248+
* @param input The code to validate
249+
* @return The code if valid, otherwise {@code null}
250+
* if invalid
251+
*/
252+
public Object validate(final String input) {
253+
if (input == null) {
254+
return null;
255+
}
256+
String code = input.trim();
257+
if (code.isEmpty()) {
258+
return null;
259+
}
260+
// validate/reformat using regular expression
261+
if (regexValidator != null) {
262+
code = regexValidator.validate(code);
263+
if (code == null) {
264+
return null;
265+
}
266+
}
267+
// check the length (must be done after validate as that can change the code)
268+
if (minLength >= 0 && code.length() < minLength ||
269+
maxLength >= 0 && code.length() > maxLength) {
270+
return null;
271+
}
272+
// validate the check digit
273+
if (checkdigit != null && !checkdigit.isValid(code)) {
274+
return null;
275+
}
276+
return code;
277+
}
278+
279+
}

src/main/java/org/apache/commons/validator/routines/LeitwegValidator.java

Lines changed: 16 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616
*/
1717
package org.apache.commons.validator.routines;
1818

19-
import org.apache.commons.logging.Log;
20-
import org.apache.commons.logging.LogFactory;
2119
import org.apache.commons.validator.routines.checkdigit.Modulus97CheckDigit;
2220

2321
/**
@@ -46,16 +44,21 @@
4644
*/
4745
public class LeitwegValidator {
4846

49-
private static final Log LOG = LogFactory.getLog(LeitwegValidator.class);
50-
51-
private final Validator formatValidator;
52-
5347
private static final char MINUS = '\u002D'; // '-' Separator
5448

55-
private static final Validator DEFAULT_FORMAT =
56-
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})?)?)?"
57-
+ "(-([A-Za-z0-9]{1,30}))?" // optional alphanumeric detail
58-
+ "-(\\d{2})$"); // two mandatory check digits
49+
private static final String STARTWITHREGION = "^(01|02|03|04|05|06|07|08|09|10|11|12|13|14|16|99)";
50+
private static final String OPTIONAL_DETAIL = MINUS + "([A-Za-z0-9]{1,30})?"; // optional alphanumeric detail
51+
private static final String MD_CHECK_DIGITS = MINUS + "(\\d{2})$"; // two mandatory check digits
52+
// Regierungsbezirk (\\d) + Landkreis (\\d{2}) + Gemeinde (3, 4 oder 7 Stellen)
53+
private static final String FORMAT1 = STARTWITHREGION + "(\\d)?(\\d{2})?"
54+
+ OPTIONAL_DETAIL + MD_CHECK_DIGITS;
55+
private static final String FORMAT2 = STARTWITHREGION + "(\\d\\d{2}\\d{7})"
56+
+ OPTIONAL_DETAIL + MD_CHECK_DIGITS;
57+
private static final String FORMAT3 = STARTWITHREGION + "(\\d\\d{2}\\d{4})"
58+
+ OPTIONAL_DETAIL + MD_CHECK_DIGITS;
59+
private static final String FORMAT4 = STARTWITHREGION + "(\\d\\d{2}\\d{3})"
60+
+ OPTIONAL_DETAIL + MD_CHECK_DIGITS;
61+
private static final String[] FORMAT = new String[] {FORMAT1, FORMAT2, FORMAT3, FORMAT4};
5962

6063
/*
6164
* in theory the shortest Leitweg-ID has a minimal general part and check digits
@@ -71,24 +74,10 @@ public class LeitwegValidator {
7174
*/
7275
private static final int MAX_CODE_LEN = 44;
7376

74-
/**
75-
* The validation class
76-
*/
77-
public static class Validator {
78-
final RegexValidator validator;
79-
80-
/**
81-
* Creates the format validator
82-
*
83-
* @param format the regex to use to check the format
84-
*/
85-
public Validator(String format) {
86-
this.validator = new RegexValidator(format);
87-
}
88-
}
77+
private static final CodeValidator VALIDATOR = new CodeValidator(new RegexValidator(FORMAT), MIN_CODE_LEN, MAX_CODE_LEN, Modulus97CheckDigit.getInstance());
8978

9079
/** The singleton instance which uses the default formats */
91-
public static final LeitwegValidator DEFAULT_LEITWEG_VALIDATOR = new LeitwegValidator();
80+
private static final LeitwegValidator DEFAULT_LEITWEG_VALIDATOR = new LeitwegValidator();
9281

9382
/**
9483
* Return a singleton instance of the validator using the default formats
@@ -99,46 +88,14 @@ public static LeitwegValidator getInstance() {
9988
return DEFAULT_LEITWEG_VALIDATOR;
10089
}
10190

102-
/**
103-
* Create a default format validator.
104-
*/
105-
public LeitwegValidator() {
106-
this.formatValidator = DEFAULT_FORMAT;
107-
}
108-
109-
/**
110-
* Retuens the RegexValidator for the format
111-
* @return formatValidator
112-
*/
113-
public RegexValidator getFormatValidator() {
114-
return formatValidator.validator;
115-
}
116-
11791
/**
11892
* Validate a Leitweg-ID
11993
*
12094
* @param id The value validation is being performed on
12195
* @return <code>true</code> if the value is valid
12296
*/
12397
public boolean isValid(String id) {
124-
125-
id = id.trim();
126-
if (id == null || id.length() > MAX_CODE_LEN || id.length() < MIN_CODE_LEN) {
127-
if (LOG.isDebugEnabled()) {
128-
LOG.debug("format length error for " + id);
129-
}
130-
return false;
131-
}
132-
133-
// format check:
134-
// der RegexValidator kann mehrere pattern prüfen!!!
135-
if (!formatValidator.validator.isValid(id)) {
136-
if (LOG.isDebugEnabled()) {
137-
LOG.debug("format " + id + " is NOT valid.");
138-
}
139-
return false;
140-
}
141-
return Modulus97CheckDigit.getInstance().isValid(removeMinus(id));
98+
return VALIDATOR.isValid(id);
14299
}
143100

144101
private String removeMinus(String id) {

0 commit comments

Comments
 (0)