1 /* ---------------------------------------------------------------------------------------
2 * Class: com.cardatechnologies.utils.validators.abaroutevalidator.AbaRouteValidator.java
3 * Date: 2015/02/11
4 * ---------------------------------------------------------------------------------------
5 * Copyright: Daniel Carda
6 * All Rights Reserved
7 * ---------------------------------------------------------------------------------------
8 *
9 * License: MIT license
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
12 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANT ABILITY, FITNESS FOR A
13 * PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
14 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
15 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
16 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17 */
18
19 package com.cardatechnologies.utils.validators.abaroutevalidator;
20
21 //~--- non-JDK imports --------------------------------------------------------
22
23 import com.cardatechnologies.utils.validators.abaroutevalidator.exceptions.AbaRouteValidationException;
24
25 /**
26 * <b>Description:</b><br>
27 * This class is used to validate a ABA Routing Transit Number.
28 *
29 * @author Daniel Carda
30 * <br>
31 * <br><b>Maintenance History:</b>
32 * <br>
33 <pre>
34 yyyy mm dd Who Description
35 ---------- ------------------------ ----------------------------------------------------
36 2015/02/11 Daniel Carda Initial Module Creation...
37 2020/12/23 Daniel Carda Started using and ENum for error content.
38 2020/12/24 Daniel Carda Optimized algorithms.
39 2021/08/01 Daniel Carda Improved Class Javadocs
40 </pre>
41 * <hr>
42 */
43 public class AbaRouteValidator {
44
45 /**
46 * Method description
47 *
48 * @param paramAbaRouteNumber
49 * The incoming ABA Number.
50 *
51 * @return boolean
52 * True if all the characters were digits.
53 * False if there was a problem.
54 *
55 * @throws AbaRouteValidationException
56 * There was a problem somewhere not related to checking the ABA number.
57 */
58 private static boolean breakdownAbaNumber( final String paramAbaRouteNumber )
59 throws AbaRouteValidationException {
60
61 // Check first two digits.
62 validateFedNumber( paramAbaRouteNumber );
63
64 // Alright, now see if the number holds up to scrutiny!
65 boolean returnBool;
66
67 returnBool = validateAbaNumberChecksum( paramAbaRouteNumber );
68
69 // Return the result
70 return( returnBool );
71 }
72
73 /**
74 * This method is the starting point to validate whether a incoming string is
75 * a valid ABA Routing Transit Number.
76 *
77 * @param paramAbaRouteNumber The ABA number to be tested.
78 *
79 * @return boolean
80 * True if all the characters were digits.
81 * False if there was a problem.
82 *
83 * @throws AbaRouteValidationException
84 * A error occurred when parsing the suspect
85 * ABA Routing Transit Number.
86 */
87 static public boolean validate( final String paramAbaRouteNumber )
88 throws AbaRouteValidationException {
89
90 // http://en.wikipedia.org/wiki/Routing_transit_number
91 //
92 // See FRB Regulation CC, Appendix A, which available online here:
93 // <http://www.bankersonline.com/regs/229/a229a.html>
94 // Quick Check
95 // Is the parameter null
96 if( paramAbaRouteNumber == null ) {
97 throw new AbaRouteValidationException( ErrorCodes.ABA_1000.getErrorCode(),
98 ErrorCodes.ABA_1000.getErrorMnemonic() );
99 }
100
101 // Quick Check
102 // Is the parameter empty/blank
103 if( paramAbaRouteNumber.trim().equals( "" ) ) {
104 throw new AbaRouteValidationException( ErrorCodes.ABA_1001.getErrorCode(),
105 ErrorCodes.ABA_1001.getErrorMnemonic() );
106 }
107
108 // Quick Check
109 // Make sure the string length is right
110 int _strLen;
111
112 _strLen = paramAbaRouteNumber.length();
113
114 // See if it's the right length
115 if( _strLen != 9 ) {
116
117 // Is it to short?
118 if( _strLen < 9 ) {
119 throw new AbaRouteValidationException( ErrorCodes.ABA_1002.getErrorCode(),
120 ErrorCodes.ABA_1002.getErrorMnemonic() );
121 }
122 else {
123
124 // Must be to long.
125 throw new AbaRouteValidationException( ErrorCodes.ABA_1003.getErrorCode(),
126 ErrorCodes.ABA_1003.getErrorMnemonic() );
127 }
128 }
129
130 // Quick Check
131 // Finally, let's just do a scan and make sure it's a number
132 if( !isNumeric( paramAbaRouteNumber ) ) {
133 throw new AbaRouteValidationException( ErrorCodes.ABA_1004.getErrorCode(),
134 ErrorCodes.ABA_1004.getErrorMnemonic() );
135 }
136
137 // -----------------------------------------------------------------------------
138 // -----------------------------------------------------------------------------
139 // So we got this far, lets start breaking it down.
140 boolean returnBool;
141
142 returnBool = breakdownAbaNumber( paramAbaRouteNumber );
143
144 // Must be good!
145 return( returnBool );
146 }
147
148 /**
149 * Method: validateAbaNumberChecksum
150 *
151 * @param paramAbaRouteNumber
152 * The target ABA number to test.
153 *
154 * @return boolean
155 * True if all the characters were digits.
156 * False if there was a problem.
157 */
158 static private boolean validateAbaNumberChecksum( final String paramAbaRouteNumber ) {
159
160 // Even thought initializing this to zero is redundant, performance tests
161 // were slightly improved.
162 int checksumTotal = 0;
163
164 // Set up all the ints
165 int i1, i2, i3, i4, i5, i6, i7, i8, i9;
166
167 // Break up the string so we can look at the numbers.
168 // Although this is a bit simplistic, doing it this way (as opposed to
169 // to a loop) produced much faster results.
170 i1 = Character.getNumericValue( paramAbaRouteNumber.charAt( 0 ) );
171 i2 = Character.getNumericValue( paramAbaRouteNumber.charAt( 1 ) );
172 i3 = Character.getNumericValue( paramAbaRouteNumber.charAt( 2 ) );
173 i4 = Character.getNumericValue( paramAbaRouteNumber.charAt( 3 ) );
174 i5 = Character.getNumericValue( paramAbaRouteNumber.charAt( 4 ) );
175 i6 = Character.getNumericValue( paramAbaRouteNumber.charAt( 5 ) );
176 i7 = Character.getNumericValue( paramAbaRouteNumber.charAt( 6 ) );
177 i8 = Character.getNumericValue( paramAbaRouteNumber.charAt( 7 ) );
178 i9 = Character.getNumericValue( paramAbaRouteNumber.charAt( 8 ) );
179
180 // Okay, lets crank it through the formula
181 checksumTotal = ( ( i3 + i6 + i9 ) + ( 3 * ( i1 + i4 + i7 ) ) + ( 7 * ( i2 + i5 + i8 ) ) );
182
183 // Check the modulus and we're done!
184 if( ( checksumTotal % 10 ) != 0 ) {
185
186 // Checksum has failed
187 return( false );
188 }
189 else {
190
191 // Checksum is good!
192 return( true );
193 }
194 }
195
196 /**
197 * This method will test to see if the first two characters, when combined to create a
198 * number, are within an acceptable range.
199 *
200 * @param paramAbaRouteNumber The target string to test.
201 *
202 * @throws AbaRouteValidationException
203 * There was a problem in converting the ABA number.
204 */
205 static private void validateFedNumber( final String paramAbaRouteNumber )
206 throws AbaRouteValidationException {
207
208 // String off the first 2 numbers and see if they validate.
209 String _tempStr;
210
211 _tempStr = paramAbaRouteNumber.substring( 0, 2 );
212
213 // Now, convert the substring to an int
214 int _fedNumb;
215
216 _fedNumb = Integer.parseInt( _tempStr );
217
218 // Okay, let's see if it works!
219 if( !( ( ( _fedNumb >= 0 ) && ( _fedNumb <= 12 ) )
220 || ( ( _fedNumb >= 21 ) && ( _fedNumb <= 32 ) )
221 || ( ( _fedNumb >= 61 ) && ( _fedNumb <= 72 ) )
222 || ( ( _fedNumb == 80 ) ) ) ) {
223 throw new AbaRouteValidationException( ErrorCodes.ABA_1005.getErrorCode(),
224 ErrorCodes.ABA_1005.getErrorMnemonic() );
225 }
226 }
227
228 /**
229 * Method: isNumeric
230 *
231 * Description:
232 * This method will scan the individual numbers in the ABA number
233 * and makre sure they are numeric digits.
234 *
235 * @param paramStr
236 * The string which needs to be made up of all numbers.
237 *
238 * @return boolean
239 * True if all the characters were digits.
240 * False if there was a problem.
241 */
242 static private boolean isNumeric( final String paramStr ) {
243
244 // Cycle through the character array
245 int size = paramStr.length();
246 for( int i = 0; i < size; i++ ) {
247 if( !Character.isDigit( paramStr.charAt(i) ) ) {
248
249 // This is bad!
250 return( false );
251 }
252 }
253
254 // All is well!
255 return( true );
256 }
257 }
258
259 /* ---------------------------------------------------------------------------------------
260 * Class: com.cardatechnologies.utils.validators.abaroutevalidator.AbaRouteValidator.java
261 * Date: 2015/02/11
262 * --------------------------------------------------------------------------------------- */