1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.validator.routines.checkdigit;
18
19 import java.io.Serializable;
20
21
22
23
24
25
26
27
28
29
30
31
32
33 public final class VerhoeffCheckDigit implements CheckDigit, Serializable {
34
35 private static final long serialVersionUID = 4138993995483695178L;
36
37
38 public static final CheckDigit VERHOEFF_CHECK_DIGIT = new VerhoeffCheckDigit();
39
40
41 private static final int[][] D_TABLE = {
42 {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
43 {1, 2, 3, 4, 0, 6, 7, 8, 9, 5},
44 {2, 3, 4, 0, 1, 7, 8, 9, 5, 6},
45 {3, 4, 0, 1, 2, 8, 9, 5, 6, 7},
46 {4, 0, 1, 2, 3, 9, 5, 6, 7, 8},
47 {5, 9, 8, 7, 6, 0, 4, 3, 2, 1},
48 {6, 5, 9, 8, 7, 1, 0, 4, 3, 2},
49 {7, 6, 5, 9, 8, 2, 1, 0, 4, 3},
50 {8, 7, 6, 5, 9, 3, 2, 1, 0, 4},
51 {9, 8, 7, 6, 5, 4, 3, 2, 1, 0}};
52
53
54 private static final int[][] P_TABLE = {
55 {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
56 {1, 5, 7, 6, 2, 8, 3, 0, 9, 4},
57 {5, 8, 0, 3, 7, 9, 6, 1, 4, 2},
58 {8, 9, 1, 6, 0, 4, 3, 5, 2, 7},
59 {9, 4, 5, 3, 1, 2, 6, 8, 7, 0},
60 {4, 2, 8, 6, 5, 7, 3, 9, 0, 1},
61 {2, 7, 9, 3, 8, 0, 6, 4, 1, 5},
62 {7, 0, 4, 6, 9, 1, 3, 2, 5, 8}};
63
64
65 private static final int[] INV_TABLE = {0, 4, 3, 2, 1, 5, 6, 7, 8, 9};
66
67
68
69
70
71
72
73
74
75 @Override
76 public String calculate(final String code) throws CheckDigitException {
77 if (code == null || code.isEmpty()) {
78 throw new CheckDigitException("Code is missing");
79 }
80 final int checksum = calculateChecksum(code, false);
81 return Integer.toString(INV_TABLE[checksum]);
82 }
83
84
85
86
87
88
89
90
91
92 private int calculateChecksum(final String code, final boolean includesCheckDigit) throws CheckDigitException {
93 int checksum = 0;
94 for (int i = 0; i < code.length(); i++) {
95 final int idx = code.length() - (i + 1);
96 final int num = Character.getNumericValue(code.charAt(idx));
97 if (num < 0 || num > 9) {
98 throw new CheckDigitException("Invalid Character[" +
99 i + "] = '" + (int)code.charAt(idx) + "'");
100 }
101 final int pos = includesCheckDigit ? i : i + 1;
102 checksum = D_TABLE[checksum][P_TABLE[pos % 8][num]];
103 }
104 return checksum;
105 }
106
107
108
109
110
111
112
113
114 @Override
115 public boolean isValid(final String code) {
116 if (code == null || code.isEmpty()) {
117 return false;
118 }
119 try {
120 return calculateChecksum(code, true) == 0;
121 } catch (final CheckDigitException e) {
122 return false;
123 }
124 }
125
126 }