AbaRouteValidator.java
/* ---------------------------------------------------------------------------------------
* Class: com.cardatechnologies.utils.validators.abaroutevalidator.AbaRouteValidator.java
* Date: 2026/11/07
* ---------------------------------------------------------------------------------------
* Copyright (c) 2026 Carda Technologies, LLC.
*
* SPDX-License-Identifier: Apache-2.0
* ---------------------------------------------------------------------------------------
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ---------------------------------------------------------------------------------------
*/
package com.cardatechnologies.utils.validators.abaroutevalidator;
//~--- non-JDK imports --------------------------------------------------------
import com.cardatechnologies.utils.validators.abaroutevalidator.exceptions.AbaRouteValidationException;
/**
* <b>Description:</b><br>
* This class is used to validate a ABA Routing Transit Number.
*
* @author Daniel Carda
* <br>
* <br><b>Maintenance History:</b>
* <br>
<pre>
yyyy mm dd Who Description
---------- ------------------------ ----------------------------------------------------
2015/02/11 Daniel Carda Initial Module Creation...
2020/12/23 Daniel Carda Started using and ENum for error content.
2020/12/24 Daniel Carda Optimized algorithms.
2021/08/01 Daniel Carda Improved Class Javadocs
2026/09/17 Daniel Carda Updated code. New Class Header.
</pre>
* <hr>
*/
public final class AbaRouteValidator {
private static final int FED_NUMBER_GOV_0 = 0;
private static final int FED_NUMBER_GOV_12 = 12;
private static final int FED_NUMBER_THRIFT_21 = 21;
private static final int FED_NUMBER_THRIFT_32 = 32;
private static final int FED_NUMBER_CLEARING_61 = 61;
private static final int FED_NUMBER_CLEARNING_72 = 72;
private static final int FED_NUMBER_TRAVELERS_80 = 80;
private static final int ABA_LENGTH = 9;
private static final int[] CHECKSUM_WEIGHTS = { 3, 7, 1 };
private AbaRouteValidator() {
// Utility class.
}
/**
* Method: validate
* <p>
* <b>Description:</b><br>
* Validates an ABA Routing Transit Number.
*
* @param routingNumber
* The ABA Routing Transit Number to validate.
*
* @return true if the routing number passes all validation checks;
* false if the checksum is invalid.
*
* @throws AbaRouteValidationException
* If the routing number is null, blank, has an invalid length,
* contains non-ASCII digits, or has an invalid Federal Reserve prefix.
*/
public static boolean validate(final String routingNumber)
throws AbaRouteValidationException {
if (routingNumber == null) {
throw new AbaRouteValidationException(
ErrorCodes.ABA_1000);
}
if (routingNumber.isBlank()) {
throw new AbaRouteValidationException(ErrorCodes.ABA_1001);
}
if (routingNumber.length() != ABA_LENGTH) {
if (routingNumber.length() < ABA_LENGTH) {
throw new AbaRouteValidationException(ErrorCodes.ABA_1002);
}
throw new AbaRouteValidationException(ErrorCodes.ABA_1003);
}
if (!isAsciiDigits(routingNumber)) {
throw new AbaRouteValidationException(ErrorCodes.ABA_1004);
}
validateFedNumber(routingNumber);
return validateAbaNumberChecksum(routingNumber);
}
/**
* Method: validateAbaNumberChecksum
* <p>
* <b>Description:</b><br>
* Validates the ABA routing number checksum.
*
* @param routingNumber
* The nine-digit ABA Routing Transit Number to check.
*
* @return true if the checksum is valid; false otherwise.
*/
private static boolean validateAbaNumberChecksum(
final String routingNumber) {
int checksum = 0;
for (int i = 0; i < ABA_LENGTH; i++) {
checksum +=
(routingNumber.charAt(i) - '0')
* CHECKSUM_WEIGHTS[i % CHECKSUM_WEIGHTS.length];
}
return checksum % 10 == 0;
}
/**
* Method: isAsciiDigits
* <p>
* <b>Description:</b><br>
* Determines whether every character in the supplied string
* is an ASCII digit.
*
* @param value
* The string to examine.
*
* @return true if every character is an ASCII digit; false otherwise.
*/
private static boolean isAsciiDigits(final String value) {
for (int i = 0; i < value.length(); i++) {
final char c = value.charAt(i);
if (c < '0' || c > '9') {
return false;
}
}
return true;
}
/**
* Method: validateFedNumber
* <p>
* <b>Description:</b><br>
* Validates the Federal Reserve routing prefix represented
* by the first two digits of the routing number.
*
* @param routingNumber
* The nine-digit ABA Routing Transit Number.
*
* @throws AbaRouteValidationException
* If the Federal Reserve prefix is invalid.
*/
private static void validateFedNumber(final String routingNumber)
throws AbaRouteValidationException {
final int fedNumber =
(routingNumber.charAt(0) - '0') * 10
+ (routingNumber.charAt(1) - '0');
// https://en.wikipedia.org/wiki/Routing_transit_number
// The first two digits of the nine digit RTN must be in the ranges
// 00 through 12, 00=Gobermint, and 12 Federal Reserve Banks
// 21 through 32, Thrift Institutions
// 61 through 72, Payment Processors and Clearing Houses
// 80. Traveler's checks
if (!((fedNumber >= FED_NUMBER_GOV_0 && fedNumber <= FED_NUMBER_GOV_12)
|| (fedNumber >= FED_NUMBER_THRIFT_21 && fedNumber <= FED_NUMBER_THRIFT_32)
|| (fedNumber >= FED_NUMBER_CLEARING_61 && fedNumber <= FED_NUMBER_CLEARNING_72)
|| fedNumber == FED_NUMBER_TRAVELERS_80)) {
throw new AbaRouteValidationException(ErrorCodes.ABA_1005);
}
}
}
/* ---------------------------------------------------------------------------------------
* Class: com.cardatechnologies.utils.validators.abaroutevalidator.AbaRouteValidator.java
* Date: 2026/11/07
* --------------------------------------------------------------------------------------- */