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