| 1 | |
|
| 2 | |
|
| 3 | |
|
| 4 | |
|
| 5 | |
|
| 6 | |
|
| 7 | |
|
| 8 | |
|
| 9 | |
|
| 10 | |
|
| 11 | |
|
| 12 | |
|
| 13 | |
|
| 14 | |
|
| 15 | |
|
| 16 | |
|
| 17 | |
package org.apache.commons.validator.routines; |
| 18 | |
|
| 19 | |
import java.util.Arrays; |
| 20 | |
import java.util.Map; |
| 21 | |
import java.util.concurrent.ConcurrentHashMap; |
| 22 | |
|
| 23 | |
import org.apache.commons.validator.routines.RegexValidator; |
| 24 | |
import org.apache.commons.validator.routines.checkdigit.IBANCheckDigit; |
| 25 | |
|
| 26 | |
|
| 27 | |
|
| 28 | |
|
| 29 | |
|
| 30 | |
public class IBANValidator { |
| 31 | |
|
| 32 | |
private final Map<String, Validator> formatValidators; |
| 33 | |
|
| 34 | |
|
| 35 | |
|
| 36 | |
|
| 37 | |
public static class Validator { |
| 38 | |
|
| 39 | |
|
| 40 | |
|
| 41 | |
|
| 42 | |
|
| 43 | |
|
| 44 | |
|
| 45 | |
|
| 46 | |
|
| 47 | |
private static final int MIN_LEN = 8; |
| 48 | |
private static final int MAX_LEN = 34; |
| 49 | |
final String countryCode; |
| 50 | |
final RegexValidator validator; |
| 51 | |
final int lengthOfIBAN; |
| 52 | |
|
| 53 | |
|
| 54 | |
|
| 55 | |
|
| 56 | |
|
| 57 | |
|
| 58 | |
|
| 59 | 77 | public Validator(String cc, int len, String format) { |
| 60 | 77 | if (!(cc.length() == 2 && Character.isUpperCase(cc.charAt(0)) && Character.isUpperCase(cc.charAt(1)))) { |
| 61 | 1 | throw new IllegalArgumentException("Invalid country Code; must be exactly 2 upper-case characters"); |
| 62 | |
} |
| 63 | 76 | if (len > MAX_LEN || len < MIN_LEN) { |
| 64 | 2 | throw new IllegalArgumentException("Invalid length parameter, must be in range "+MIN_LEN+" to "+MAX_LEN+" inclusive: " +len); |
| 65 | |
} |
| 66 | 74 | if (!format.startsWith(cc)) { |
| 67 | 0 | throw new IllegalArgumentException("countryCode '"+cc+"' does not agree with format: " + format); |
| 68 | |
} |
| 69 | 74 | this.countryCode = cc; |
| 70 | 74 | this.lengthOfIBAN = len; |
| 71 | 74 | this.validator = new RegexValidator(format); |
| 72 | 74 | } |
| 73 | |
} |
| 74 | |
|
| 75 | |
|
| 76 | |
|
| 77 | |
|
| 78 | |
|
| 79 | |
|
| 80 | |
|
| 81 | |
|
| 82 | |
|
| 83 | |
|
| 84 | |
|
| 85 | |
|
| 86 | |
|
| 87 | |
|
| 88 | |
|
| 89 | |
|
| 90 | |
|
| 91 | |
|
| 92 | |
|
| 93 | |
|
| 94 | |
|
| 95 | |
|
| 96 | 1 | private static final Validator[] DEFAULT_FORMATS = { |
| 97 | |
new Validator("AD", 24, "AD\\d{10}[A-Z0-9]{12}" ), |
| 98 | |
new Validator("AE", 23, "AE\\d{21}" ), |
| 99 | |
new Validator("AL", 28, "AL\\d{10}[A-Z0-9]{16}" ), |
| 100 | |
new Validator("AT", 20, "AT\\d{18}" ), |
| 101 | |
new Validator("AZ", 28, "AZ\\d{2}[A-Z]{4}[A-Z0-9]{20}" ), |
| 102 | |
new Validator("BA", 20, "BA\\d{18}" ), |
| 103 | |
new Validator("BE", 16, "BE\\d{14}" ), |
| 104 | |
new Validator("BG", 22, "BG\\d{2}[A-Z]{4}\\d{6}[A-Z0-9]{8}" ), |
| 105 | |
new Validator("BH", 22, "BH\\d{2}[A-Z]{4}[A-Z0-9]{14}" ), |
| 106 | |
new Validator("BR", 29, "BR\\d{25}[A-Z]{1}[A-Z0-9]{1}" ), |
| 107 | |
new Validator("BY", 28, "BY\\d{2}[A-Z0-9]{4}\\d{4}[A-Z0-9]{16}" ), |
| 108 | |
new Validator("CH", 21, "CH\\d{7}[A-Z0-9]{12}" ), |
| 109 | |
new Validator("CR", 22, "CR\\d{20}" ), |
| 110 | |
new Validator("CY", 28, "CY\\d{10}[A-Z0-9]{16}" ), |
| 111 | |
new Validator("CZ", 24, "CZ\\d{22}" ), |
| 112 | |
new Validator("DE", 22, "DE\\d{20}" ), |
| 113 | |
new Validator("DK", 18, "DK\\d{16}" ), |
| 114 | |
new Validator("DO", 28, "DO\\d{2}[A-Z0-9]{4}\\d{20}" ), |
| 115 | |
new Validator("EE", 20, "EE\\d{18}" ), |
| 116 | |
new Validator("ES", 24, "ES\\d{22}" ), |
| 117 | |
new Validator("FI", 18, "FI\\d{16}" ), |
| 118 | |
new Validator("FO", 18, "FO\\d{16}" ), |
| 119 | |
new Validator("FR", 27, "FR\\d{12}[A-Z0-9]{11}\\d{2}" ), |
| 120 | |
new Validator("GB", 22, "GB\\d{2}[A-Z]{4}\\d{14}" ), |
| 121 | |
new Validator("GE", 22, "GE\\d{2}[A-Z]{2}\\d{16}" ), |
| 122 | |
new Validator("GI", 23, "GI\\d{2}[A-Z]{4}[A-Z0-9]{15}" ), |
| 123 | |
new Validator("GL", 18, "GL\\d{16}" ), |
| 124 | |
new Validator("GR", 27, "GR\\d{9}[A-Z0-9]{16}" ), |
| 125 | |
new Validator("GT", 28, "GT\\d{2}[A-Z0-9]{24}" ), |
| 126 | |
new Validator("HR", 21, "HR\\d{19}" ), |
| 127 | |
new Validator("HU", 28, "HU\\d{26}" ), |
| 128 | |
new Validator("IE", 22, "IE\\d{2}[A-Z]{4}\\d{14}" ), |
| 129 | |
new Validator("IL", 23, "IL\\d{21}" ), |
| 130 | |
new Validator("IS", 26, "IS\\d{24}" ), |
| 131 | |
new Validator("IT", 27, "IT\\d{2}[A-Z]{1}\\d{10}[A-Z0-9]{12}" ), |
| 132 | |
new Validator("IQ", 23, "IQ\\d{2}[A-Z]{4}\\d{15}" ), |
| 133 | |
new Validator("JO", 30, "JO\\d{2}[A-Z]{4}\\d{4}[A-Z0-9]{18}" ), |
| 134 | |
new Validator("KW", 30, "KW\\d{2}[A-Z]{4}[A-Z0-9]{22}" ), |
| 135 | |
new Validator("KZ", 20, "KZ\\d{5}[A-Z0-9]{13}" ), |
| 136 | |
new Validator("LB", 28, "LB\\d{6}[A-Z0-9]{20}" ), |
| 137 | |
new Validator("LC", 32, "LC\\d{2}[A-Z]{4}[A-Z0-9]{24}" ), |
| 138 | |
new Validator("LI", 21, "LI\\d{7}[A-Z0-9]{12}" ), |
| 139 | |
new Validator("LT", 20, "LT\\d{18}" ), |
| 140 | |
new Validator("LU", 20, "LU\\d{5}[A-Z0-9]{13}" ), |
| 141 | |
new Validator("LV", 21, "LV\\d{2}[A-Z]{4}[A-Z0-9]{13}" ), |
| 142 | |
new Validator("MC", 27, "MC\\d{12}[A-Z0-9]{11}\\d{2}" ), |
| 143 | |
new Validator("MD", 24, "MD\\d{2}[A-Z0-9]{20}" ), |
| 144 | |
new Validator("ME", 22, "ME\\d{20}" ), |
| 145 | |
new Validator("MK", 19, "MK\\d{5}[A-Z0-9]{10}\\d{2}" ), |
| 146 | |
new Validator("MR", 27, "MR\\d{25}" ), |
| 147 | |
new Validator("MT", 31, "MT\\d{2}[A-Z]{4}\\d{5}[A-Z0-9]{18}" ), |
| 148 | |
new Validator("MU", 30, "MU\\d{2}[A-Z]{4}\\d{19}[A-Z]{3}" ), |
| 149 | |
new Validator("NL", 18, "NL\\d{2}[A-Z]{4}\\d{10}" ), |
| 150 | |
new Validator("NO", 15, "NO\\d{13}" ), |
| 151 | |
new Validator("PK", 24, "PK\\d{2}[A-Z]{4}[A-Z0-9]{16}" ), |
| 152 | |
new Validator("PL", 28, "PL\\d{26}" ), |
| 153 | |
new Validator("PS", 29, "PS\\d{2}[A-Z]{4}[A-Z0-9]{21}" ), |
| 154 | |
new Validator("PT", 25, "PT\\d{23}" ), |
| 155 | |
new Validator("QA", 29, "QA\\d{2}[A-Z]{4}[A-Z0-9]{21}" ), |
| 156 | |
new Validator("RO", 24, "RO\\d{2}[A-Z]{4}[A-Z0-9]{16}" ), |
| 157 | |
new Validator("RS", 22, "RS\\d{20}" ), |
| 158 | |
new Validator("SA", 24, "SA\\d{4}[A-Z0-9]{18}" ), |
| 159 | |
new Validator("SC", 31, "SC\\d{2}[A-Z]{4}\\d{20}[A-Z]{3}" ), |
| 160 | |
new Validator("SE", 24, "SE\\d{22}" ), |
| 161 | |
new Validator("SI", 19, "SI\\d{17}" ), |
| 162 | |
new Validator("SK", 24, "SK\\d{22}" ), |
| 163 | |
new Validator("SM", 27, "SM\\d{2}[A-Z]{1}\\d{10}[A-Z0-9]{12}" ), |
| 164 | |
new Validator("ST", 25, "ST\\d{23}" ), |
| 165 | |
new Validator("TL", 23, "TL\\d{21}" ), |
| 166 | |
new Validator("TN", 24, "TN\\d{22}" ), |
| 167 | |
new Validator("TR", 26, "TR\\d{8}[A-Z0-9]{16}" ), |
| 168 | |
new Validator("UA", 29, "UA\\d{8}[A-Z0-9]{19}" ), |
| 169 | |
new Validator("VG", 24, "VG\\d{2}[A-Z]{4}\\d{16}" ), |
| 170 | |
new Validator("XK", 20, "XK\\d{18}" ), |
| 171 | |
}; |
| 172 | |
|
| 173 | |
|
| 174 | 1 | public static final IBANValidator DEFAULT_IBAN_VALIDATOR = new IBANValidator(); |
| 175 | |
|
| 176 | |
|
| 177 | |
|
| 178 | |
|
| 179 | |
|
| 180 | |
|
| 181 | |
public static IBANValidator getInstance() { |
| 182 | 1 | return DEFAULT_IBAN_VALIDATOR; |
| 183 | |
} |
| 184 | |
|
| 185 | |
|
| 186 | |
|
| 187 | |
|
| 188 | |
public IBANValidator() { |
| 189 | 5 | this(DEFAULT_FORMATS); |
| 190 | 5 | } |
| 191 | |
|
| 192 | |
|
| 193 | |
|
| 194 | |
|
| 195 | |
|
| 196 | |
|
| 197 | 5 | public IBANValidator(Validator[] formatMap) { |
| 198 | 5 | this.formatValidators = createValidators(formatMap); |
| 199 | 5 | } |
| 200 | |
|
| 201 | |
private Map<String, Validator> createValidators(Validator[] formatMap) { |
| 202 | 5 | Map<String, Validator> m = new ConcurrentHashMap<String, Validator>(); |
| 203 | 375 | for(Validator v : formatMap) { |
| 204 | 370 | m.put(v.countryCode, v); |
| 205 | |
} |
| 206 | 5 | return m; |
| 207 | |
} |
| 208 | |
|
| 209 | |
|
| 210 | |
|
| 211 | |
|
| 212 | |
|
| 213 | |
|
| 214 | |
|
| 215 | |
public boolean isValid(String code) { |
| 216 | 93 | Validator formatValidator = getValidator(code); |
| 217 | 93 | if (formatValidator == null || code.length() != formatValidator.lengthOfIBAN || !formatValidator.validator.isValid(code)) { |
| 218 | 11 | return false; |
| 219 | |
} |
| 220 | 82 | return IBANCheckDigit.IBAN_CHECK_DIGIT.isValid(code); |
| 221 | |
} |
| 222 | |
|
| 223 | |
|
| 224 | |
|
| 225 | |
|
| 226 | |
|
| 227 | |
|
| 228 | |
|
| 229 | |
public boolean hasValidator(String code) { |
| 230 | 81 | return getValidator(code) != null; |
| 231 | |
} |
| 232 | |
|
| 233 | |
|
| 234 | |
|
| 235 | |
|
| 236 | |
|
| 237 | |
|
| 238 | |
public Validator[] getDefaultValidators() { |
| 239 | 0 | return Arrays.copyOf(DEFAULT_FORMATS, DEFAULT_FORMATS.length); |
| 240 | |
} |
| 241 | |
|
| 242 | |
|
| 243 | |
|
| 244 | |
|
| 245 | |
|
| 246 | |
|
| 247 | |
|
| 248 | |
|
| 249 | |
public Validator getValidator(String code) { |
| 250 | 176 | if (code == null || code.length() < 2) { |
| 251 | 3 | return null; |
| 252 | |
} |
| 253 | 173 | String key = code.substring(0, 2); |
| 254 | 173 | return formatValidators.get(key); |
| 255 | |
} |
| 256 | |
|
| 257 | |
|
| 258 | |
|
| 259 | |
|
| 260 | |
|
| 261 | |
|
| 262 | |
|
| 263 | |
|
| 264 | |
|
| 265 | |
public Validator setValidator(Validator validator) { |
| 266 | 0 | if (this == DEFAULT_IBAN_VALIDATOR) { |
| 267 | 0 | throw new IllegalStateException("The singleton validator cannot be modified"); |
| 268 | |
} |
| 269 | 0 | return formatValidators.put(validator.countryCode, validator); |
| 270 | |
} |
| 271 | |
|
| 272 | |
|
| 273 | |
|
| 274 | |
|
| 275 | |
|
| 276 | |
|
| 277 | |
|
| 278 | |
|
| 279 | |
|
| 280 | |
|
| 281 | |
|
| 282 | |
|
| 283 | |
|
| 284 | |
public Validator setValidator(String countryCode, int length, String format) { |
| 285 | 7 | if (this == DEFAULT_IBAN_VALIDATOR) { |
| 286 | 2 | throw new IllegalStateException("The singleton validator cannot be modified"); |
| 287 | |
} |
| 288 | 5 | if (length < 0) { |
| 289 | 2 | return formatValidators.remove(countryCode); |
| 290 | |
} |
| 291 | 3 | return setValidator(new Validator(countryCode, length, format)); |
| 292 | |
} |
| 293 | |
} |