com.prowidesoftware.swift.model.IBAN.java Source code

Java tutorial

Introduction

Here is the source code for com.prowidesoftware.swift.model.IBAN.java

Source

/*******************************************************************************
 * Copyright (c) 2016 Prowide Inc.
 *
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as
 *     published by the Free Software Foundation, either version 3 of the
 *     License, or (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 *     Check the LGPL at <http://www.gnu.org/licenses/> for more details.
 *******************************************************************************/
package com.prowidesoftware.swift.model;

import com.prowidesoftware.deprecation.DeprecationUtils;
import com.prowidesoftware.deprecation.ProwideDeprecated;
import com.prowidesoftware.deprecation.TargetYear;
import org.apache.commons.lang.StringUtils;

import java.util.logging.Level;

/**
 * Utility class to validate IBAN codes.
 *
 * The IBAN consists of a ISO 3166-1 alpha-2 country code, followed by two check
 * digits (represented by kk in the examples below), and up to thirty alphanumeric
 * characters for the domestic bank account number, called the BBAN (Basic Bank
 * Account Number).
 *
 * <h1>Exampe usage scenario</h1>
 * <code><pre>IBAN iban = new IBAN("ES2153893489");
 * if (iban.isValid())
 *       System.out.println("ok");
 * else
 *       System.out.println("problem with iban: "+iban.getInvalidCause());
 * </pre></code>
 *
 * @since 3.3
 */
public class IBAN {
    private static transient final java.util.logging.Logger log = java.util.logging.Logger
            .getLogger(IBAN.class.getName());

    @Deprecated
    @ProwideDeprecated(phase2 = TargetYear._2019)
    private String invalidCause = null;

    private String iban;

    private static final int COUNTRY_CODE_INDEX = 0;
    static final int COUNTRY_CODE_LENGTH = 2;
    private static final int CHECK_DIGIT_INDEX = COUNTRY_CODE_LENGTH;
    static final int CHECK_DIGIT_LENGTH = 2;
    private static final int BBAN_INDEX = CHECK_DIGIT_INDEX + CHECK_DIGIT_LENGTH;

    /**
     * Get the IBAN
     * @return a string with the IBAN
     */
    public String getIban() {
        return iban;
    }

    /**
     * Set the IBAN
     * @param iban the IBAN to set
     */
    public void setIban(String iban) {
        this.iban = iban;
    }

    /**
     * Create an IBAN object with the given iban code.
     * This constructor does not perform any validation on the iban, only
     * @param iban
     */
    public IBAN(String iban) {
        this.iban = iban;
    }

    /**
     * Checks if the IBAN number is valid.
     * <p>If the number is not valid a text description of the invalid cause is set in {@link #getInvalidCause()}</p>
     *
     * @see #validate() for details regarding the validation checks or if you need structured details of the validation
     * problem found.
     *
     * @return <code>true</code> if the IBAN is valid and <code>false</code> in other case
     */
    public boolean isValid() {
        IbanValidationResult result = validate();
        if (result == IbanValidationResult.OK) {
            return true;
        } else {
            this.invalidCause = result.message();
            return false;
        }
    }

    /**
     * Check an IBAN number throwing an exception with validation details if it is not valid.
     *
     * <p>Validates that the length is at least 5 chars: composed by a valid 2 letters ISO country code,
     * 2 verifying digits, and 1 BBAN. The verification digits are also computed and verified.
     * For the BBAN validation the specific per country structure must be defined either in the
     * BbanStructureValidations.json file or by API in the {@link BbanStructureValidations} instance.</p>
     *
     * @return IbanFormatStatus with detailed information of the validation problem found
     */
    public IbanValidationResult validate() {
        if (iban == null) {
            return IbanValidationResult.IBAN_IS_NULL;
        }
        if (iban.length() == 0) {
            return IbanValidationResult.IBAN_IS_EMPTY;
        }

        IbanValidationResult result = null;
        try {
            final String code = removeNonAlpha(this.iban);

            result = IbanValidationUtils.validateCountryCode(code);

            if (result == null) {
                result = IbanValidationUtils.validateCheckDigitPresence(code);
            }

            if (result == null) {
                result = IbanValidationUtils.validateBbanPresence(code);
            }

            if (result == null) {
                final String bban = getBban(code);

                result = IbanValidationUtils.validateBbanMaxLength(bban);

                if (result == null) {
                    /*
                     * load specific structure for country
                     */
                    final String country = getCountryCode(code);
                    final BbanStructureDTO structure = BbanStructureValidations.getInstance().forCountry(country);
                    if (structure == null) {
                        result = IbanValidationResult.MISSING_BBAN_CONFIGURATION;
                        result.setFound(country);
                    } else {
                        result = IbanValidationUtils.validateBban(bban, structure);
                    }
                }
            }

            if (result == null) {
                result = IbanValidationUtils.validateCharacters(code);
            }
            if (result == null) {
                result = IbanValidationUtils.validateCheckDigit(code);
            }

        } catch (RuntimeException e) {
            return IbanValidationResult.UNKNOWN;
        }

        if (result != null) {
            return result;
        } else {
            return IbanValidationResult.OK;
        }
    }

    /**
     * @deprecated use {@link IBAN#translateChars(StringBuilder)}
     */
    @Deprecated
    @ProwideDeprecated(phase3 = TargetYear._2018)
    public String translateChars(final StringBuffer bban) {
        DeprecationUtils.phase2(getClass(), "translateChars(StringBuffer)",
                "Use translateChars(StringBuilder) instead.");
        return translateChars(new StringBuilder(bban));
    }

    /**
     * Translate letters to numbers, also ignoring non alphanumeric characters
     *
     * @param bban
     * @return the translated value
     */
    public String translateChars(final StringBuilder bban) {
        final StringBuilder result = new StringBuilder();
        for (int i = 0; i < bban.length(); i++) {
            char c = bban.charAt(i);
            if (Character.isLetter(c)) {
                result.append(Character.getNumericValue(c));
            } else {
                result.append((char) c);
            }
        }
        return result.toString();
    }

    /**
     *
     * @param iban
     * @return the resulting IBAN
     */
    public String removeNonAlpha(final String iban) {
        final StringBuilder result = new StringBuilder();
        for (int i = 0; i < iban.length(); i++) {
            char c = iban.charAt(i);
            if (Character.isLetter(c) || Character.isDigit(c)) {
                result.append((char) c);
            }
        }
        return result.toString();
    }

    /**
     * Get a string with information about why the IBAN was found invalid
     * @return a human readable (english) string
     * @deprecated use the {@link #validate()} method to get a detailed result of the validation problem found
     */
    @Deprecated
    @ProwideDeprecated(phase2 = TargetYear._2019)
    public String getInvalidCause() {
        return invalidCause;
    }

    /**
     * Gets the BBAN (custom account number) part of the IBAN
     * @return the custom account part of the IBAN or null if the IBAN has an invalid length
     * @since 7.9.7
     * @author psantamarina
     */
    public String getBban() {
        if (StringUtils.isNotEmpty(this.iban)) {
            try {
                return getBban(this.iban);
            } catch (IndexOutOfBoundsException e) {
                log.log(Level.FINER, "Invalid IBAN length in " + this.iban, e);
            }
        }
        return null;
    }

    /**
     * Gets the BBAN (custom account number) part of the given IBAN
     *
     * @param iban a well-formed IBAN
     * @return the custom account part of the IBAN
     * @throws {@link IndexOutOfBoundsException} if the IBAN length is wrong
     * @since 7.9.7
     * @author psantamarina
     */
    public static String getBban(final String iban) throws IndexOutOfBoundsException {
        return iban.substring(BBAN_INDEX);
    }

    /**
     * Gets the check digits part of the IBAN
     * @return the check digits (two digits as String) of the IBAN or null if the IBAN has an invalid length
     * @since 7.9.7
     * @author psantamarina
     */
    public String getCheckDigits() {
        if (StringUtils.isNotEmpty(this.iban)) {
            try {
                return getCheckDigits(this.iban);
            } catch (IndexOutOfBoundsException e) {
                log.log(Level.FINER, "Invalid IBAN length in " + this.iban, e);
            }
        }
        return null;
    }

    /**
     * Gets the check digits part of the given IBAN.
     *
     * @param iban a well-formed IBAN
     * @return the check digits (two digits as String)
     * @throws {@link IndexOutOfBoundsException} if the IBAN length is wrong
     * @since 7.9.7
     * @author psantamarina
     */
    public static String getCheckDigits(final String iban) throws IndexOutOfBoundsException {
        return iban.substring(CHECK_DIGIT_INDEX, CHECK_DIGIT_INDEX + CHECK_DIGIT_LENGTH);
    }

    /**
     * Gets the country code part of the IBAN
     * @return the two letters ISO country code of the IBAN or null if the IBAN has an invalid length
     * @since 7.9.7
     * @author psantamarina
     */
    public String getCountryCode() {
        if (StringUtils.isNotEmpty(this.iban)) {
            try {
                return getCountryCode(this.iban);
            } catch (IndexOutOfBoundsException e) {
                log.log(Level.FINER, "Invalid IBAN length in " + this.iban, e);
            }
        }
        return null;
    }

    /**
     * Gets the country code part of the given IBAN.
     *
     * @param iban a well-formed IBAN
     * @return the two letters ISO country code
     * @throws {@link IndexOutOfBoundsException} if the IBAN length is wrong
     * @since 7.9.7
     * @author psantamarina
     */
    public static String getCountryCode(final String iban) throws IndexOutOfBoundsException {
        return iban.substring(COUNTRY_CODE_INDEX, COUNTRY_CODE_INDEX + COUNTRY_CODE_LENGTH);
    }

}