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, 088 * validator and {@link CheckDigit} validation. 089 * 090 * @param regexValidator The format regular expression validator 091 * @param checkdigit The check digit validation routine. 092 */ 093 public CodeValidator(final RegexValidator regexValidator, final CheckDigit checkdigit) { 094 this(regexValidator, -1, -1, checkdigit); 095 } 096 097 /** 098 * Constructs a code validator with a specified regular expression, 099 * 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}