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 static org.junit.Assert.assertFalse;
20 import static org.junit.Assert.assertNotNull;
21 import static org.junit.Assert.assertNull;
22 import static org.junit.Assert.assertTrue;
23 import static org.junit.Assert.fail;
24
25 import java.io.File;
26 import java.io.FileInputStream;
27 import java.io.InputStreamReader;
28 import java.io.Reader;
29 import java.util.regex.Matcher;
30 import java.util.regex.Pattern;
31
32 import org.apache.commons.csv.CSVFormat;
33 import org.apache.commons.csv.CSVParser;
34 import org.apache.commons.csv.CSVRecord;
35 import org.apache.commons.validator.routines.IBANValidator.Validator;
36 import org.apache.commons.validator.routines.checkdigit.IBANCheckDigit;
37 import org.junit.Test;
38
39
40
41
42
43 public class IBANValidatorTest {
44
45
46
47
48 private final String[] validIBANFormat = new String[] {
49 "AD1200012030200359100100",
50 "AE070331234567890123456",
51 "AL47212110090000000235698741",
52 "AT611904300234573201",
53 "AZ21NABZ00000000137010001944",
54 "BA391290079401028494",
55 "BE68539007547034",
56 "BG80BNBG96611020345678",
57 "BH67BMAG00001299123456",
58 "BR1800000000141455123924100C2",
59 "BR1800360305000010009795493C1",
60 "BR9700360305000010009795493P1",
61 "BY13NBRB3600900000002Z00AB00",
62 "CH9300762011623852957",
63 "CR05015202001026284066",
64 "CY17002001280000001200527600",
65 "CZ6508000000192000145399",
66 "CZ9455000000001011038930",
67 "DE89370400440532013000",
68 "DK5000400440116243",
69 "DO28BAGR00000001212453611324",
70 "EE382200221020145685",
71 "EG380019000500000000263180002",
72 "ES9121000418450200051332",
73 "FI2112345600000785",
74 "FI5542345670000081",
75 "FO6264600001631634",
76 "FR1420041010050500013M02606",
77 "GB29NWBK60161331926819",
78 "GE29NB0000000101904917",
79 "GI75NWBK000000007099453",
80 "GL8964710001000206",
81 "GR1601101250000000012300695",
82 "GT82TRAJ01020000001210029690",
83 "HR1210010051863000160",
84 "HU42117730161111101800000000",
85 "IE29AIBK93115212345678",
86 "IL620108000000099999999",
87 "IQ98NBIQ850123456789012",
88 "IS140159260076545510730339",
89 "IT60X0542811101000000123456",
90 "JO94CBJO0010000000000131000302",
91 "KW81CBKU0000000000001234560101",
92 "KZ86125KZT5004100100",
93 "LB62099900000001001901229114",
94 "LC55HEMM000100010012001200023015",
95 "LI21088100002324013AA",
96 "LT121000011101001000",
97 "LU280019400644750000",
98 "LV80BANK0000435195001",
99 "MC5811222000010123456789030",
100 "MD24AG000225100013104168",
101 "ME25505000012345678951",
102 "MK07250120000058984",
103 "MR1300020001010000123456753",
104 "MT84MALT011000012345MTLCAST001S",
105 "MU17BOMM0101101030300200000MUR",
106 "NL91ABNA0417164300",
107 "NO9386011117947",
108 "PK36SCBL0000001123456702",
109 "PL61109010140000071219812874",
110 "PS92PALS000000000400123456702",
111 "PT50000201231234567890154",
112 "QA58DOHB00001234567890ABCDEFG",
113 "RO49AAAA1B31007593840000",
114 "RS35260005601001611379",
115 "SA0380000000608010167519",
116 "SC18SSCB11010000000000001497USD",
117 "SE4550000000058398257466",
118 "SI56191000000123438",
119 "SI56263300012039086",
120 "SK3112000000198742637541",
121 "SM86U0322509800000000270100",
122 "ST68000100010051845310112",
123 "SV62CENR00000000000000700025",
124 "SV43ACAT00000000000000123123",
125 "TL380080012345678910157",
126 "TN5910006035183598478831",
127 "TR330006100519786457841326",
128 "UA213223130000026007233566001",
129 "UA213996220000026007233566001",
130 "VA59001123000012345678",
131 "VG96VPVG0000012345678901",
132 "XK051212012345678906",
133 };
134
135 private final String[] invalidIBANFormat = new String[] {
136 "",
137 " ",
138 "A",
139 "AB",
140 "FR1420041010050500013m02606",
141 "MT84MALT011000012345mtlcast001s",
142 "LI21088100002324013aa",
143 "QA58DOHB00001234567890abcdefg",
144 "RO49AAAA1b31007593840000",
145 "LC62HEMM000100010012001200023015",
146 "BY00NBRB3600000000000Z00AB00",
147 "ST68000200010192194210112",
148 "SV62CENR0000000000000700025",
149 };
150
151 private static final IBANValidator VALIDATOR = IBANValidator.getInstance();
152
153 @Test
154 public void testValid() {
155 for(String f : validIBANFormat) {
156 assertTrue("Checksum fail: "+f, IBANCheckDigit.IBAN_CHECK_DIGIT.isValid(f));
157 assertTrue("Missing validator: "+f, VALIDATOR.hasValidator(f));
158 assertTrue(f, VALIDATOR.isValid(f));
159 }
160 }
161
162 @Test
163 public void testInValid() {
164 for(String f : invalidIBANFormat) {
165 assertFalse(f, VALIDATOR.isValid(f));
166 }
167 }
168
169 @Test
170 public void testNull() {
171 assertFalse("isValid(null)", VALIDATOR.isValid(null));
172 }
173
174 @Test
175 public void testHasValidator() {
176 assertTrue("GB", VALIDATOR.hasValidator("GB"));
177 assertFalse("gb", VALIDATOR.hasValidator("gb"));
178 }
179
180 @Test
181 public void testGetValidator() {
182 assertNotNull("GB", VALIDATOR.getValidator("GB"));
183 assertNull("gb", VALIDATOR.getValidator("gb"));
184 }
185
186 @Test(expected=IllegalStateException.class)
187 public void testSetDefaultValidator1() {
188 assertNotNull(VALIDATOR.setValidator("GB", 15, "GB"));
189 }
190
191 @Test(expected=IllegalStateException.class)
192 public void testSetDefaultValidator2() {
193 assertNotNull(VALIDATOR.setValidator("GB", -1, "GB"));
194 }
195
196 @Test(expected=IllegalArgumentException.class)
197 public void testSetValidatorLC() {
198 IBANValidator validator = new IBANValidator();
199 assertNotNull(validator.setValidator("gb", 15, "GB"));
200 }
201
202 @Test(expected=IllegalArgumentException.class)
203 public void testSetValidatorLen7() {
204 IBANValidator validator = new IBANValidator();
205 assertNotNull(validator.setValidator("GB", 7, "GB"));
206 }
207
208 @Test(expected=IllegalArgumentException.class)
209 public void testSetValidatorLen35() {
210 IBANValidator validator = new IBANValidator();
211 assertNotNull(validator.setValidator("GB", 35, "GB"));
212 }
213
214 @Test
215 public void testSetValidatorLen_1() {
216 IBANValidator validator = new IBANValidator();
217 assertNotNull("should be present",validator.setValidator("GB", -1, ""));
218 assertNull("no longer present",validator.setValidator("GB", -1, ""));
219 }
220
221 @Test
222 public void testSorted() {
223 IBANValidator validator = new IBANValidator();
224 Validator[] vals = validator.getDefaultValidators();
225 assertNotNull(vals);
226 for(int i=1; i < vals.length; i++) {
227 if (vals[i].countryCode.compareTo(vals[i-1].countryCode) <= 0) {
228 fail("Not sorted: "+vals[i].countryCode+ " <= " + vals[i-1].countryCode);
229 }
230 }
231 }
232
233 private static int checkIBAN(File file, IBANValidator val) throws Exception {
234
235
236 CSVFormat format = CSVFormat.DEFAULT.withDelimiter('\t');
237 Reader rdr = new InputStreamReader(new FileInputStream(file), "ISO_8859_1");
238 CSVParser p = new CSVParser(rdr, format);
239 CSVRecord country = null;
240 CSVRecord cc = null;
241 CSVRecord structure = null;
242 CSVRecord length = null;
243 for (CSVRecord o : p) {
244 String item = o.get(0);
245 if ("Name of country".equals(item)) {
246 country = o;
247 } else if ("IBAN prefix country code (ISO 3166)".equals(item)) {
248 cc = o;
249 } else if ("IBAN structure".equals(item)) {
250 structure = o;
251 } else if ("IBAN length".equals(item)) {
252 length = o;
253 }
254 }
255 for (int i=1; i < country.size(); i++) {
256 try {
257
258 String newLength = length.get(i).split("!")[0];
259 String newRE = fmtRE(structure.get(i), Integer.parseInt(newLength));
260 final Validator valre = val.getValidator(cc.get(i));
261 if (valre == null) {
262 System.out.println("// Missing entry:");
263 printEntry(
264 cc.get(i),
265 newLength,
266 newRE,
267 country.get(i));
268 } else {
269 String currentLength = Integer.toString(valre.lengthOfIBAN);
270 String currentRE = valre.validator.toString()
271 .replaceAll("^.+?\\{(.+)}","$1")
272 .replaceAll("\\\\d","\\\\\\\\d");
273
274 if (currentRE.equals(newRE) && currentLength.equals(newLength)) {
275
276 } else {
277 System.out.println("// Expected: " + newRE + ", " + newLength + " Actual: " + currentRE + ", " + currentLength);
278 printEntry(
279 cc.get(i),
280 newLength,
281 newRE,
282 country.get(i));
283 }
284
285 }
286
287 } catch (IllegalArgumentException e) {
288 e.printStackTrace();
289 }
290 }
291 p.close();
292 return country.size();
293 }
294
295 private static void printEntry(String ccode, String length, String ib, String country) {
296 String fmt = String.format("\"%s\"", ib);
297 System.out.printf(" new Validator(\"%s\", %s, %-40s), // %s\n",
298 ccode,
299 length,
300 fmt,
301 country);
302 }
303
304
305
306 private static final String IBAN_PART = "(?:(\\d+)!([acn]))";
307 private static final Pattern IBAN_PAT = Pattern.compile(
308 "([A-Z]{2})"+IBAN_PART+IBAN_PART+IBAN_PART+IBAN_PART+"?"+IBAN_PART+"?"+IBAN_PART+"?"+IBAN_PART+"?");
309
310
311 private static String formatToRE(String type, int len) {
312 char ctype = type.charAt(0);
313 switch(ctype) {
314 case 'n':
315 return String.format("\\\\d{%d}",len);
316 case 'a':
317 return String.format("[A-Z]{%d}",len);
318 case 'c':
319 return String.format("[A-Z0-9]{%d}",len);
320 default:
321 throw new IllegalArgumentException("Unexpected type " + type);
322 }
323 }
324
325 private static String fmtRE(String iban_pat, int iban_len) {
326 Matcher m = IBAN_PAT.matcher(iban_pat);
327 if (m.matches()) {
328 StringBuilder sb = new StringBuilder();
329 String cc = m.group(1);
330 int totalLen = cc.length();
331 sb.append(cc);
332 int len = Integer.parseInt(m.group(2));
333 String curType = m.group(3);
334 for (int i = 4; i <= m.groupCount(); i += 2) {
335 if (m.group(i) == null) {
336 break;
337 }
338 int count = Integer.parseInt(m.group(i));
339 String type = m.group(i+1);
340 if (type.equals(curType)) {
341 len += count;
342 } else {
343 sb.append(formatToRE(curType,len));
344 totalLen += len;
345 curType = type;
346 len = count;
347 }
348 }
349 sb.append(formatToRE(curType,len));
350 totalLen += len;
351 if (iban_len != totalLen) {
352 throw new IllegalArgumentException("IBAN pattern " + iban_pat + " does not match length " + iban_len);
353 }
354 return sb.toString();
355 } else {
356 throw new IllegalArgumentException("Unexpected IBAN pattern " + iban_pat);
357 }
358 }
359
360 public static void main(String [] a) throws Exception {
361 IBANValidator validator = new IBANValidator();
362 File iban_tsv = new File("target","iban-registry.tsv");
363 int countries = 0;
364 if (iban_tsv.canRead()) {
365 countries = checkIBAN(iban_tsv, validator);
366 } else {
367 System.out.println("Please load the file " + iban_tsv.getCanonicalPath() + " from https://www.swift.com/standards/data-standards/iban");
368 }
369 System.out.println("Processed " + countries + " countries.");
370 }
371 }