// ============================================================================
//
// Copyright (C) 2006-2016 Talend Inc. - www.talend.com
//
// This source code is available under agreement available at
// %InstallDIR%\features\org.talend.rcp.branding.%PRODUCTNAME%\%PRODUCTNAME%license.txt
//
// You should have received a copy of the agreement
// along with this program; if not, write to Talend SA
// 9 rue Pages 92150 Suresnes, France
//
// ============================================================================
package org.talend.dataquality.datamasking.functions;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* created by jgonzalez on 24 juin 2015. This class holds all the functions required by GenerateAccountNumberFormat and
* GenerateAccountNumberFormat.
*
*/
public abstract class GenerateAccountNumber extends Function<String> {
private static final long serialVersionUID = -631455182627735683L;
private static final BigInteger MOD97 = new BigInteger("97"); //$NON-NLS-1$
private static final Map<Integer, Country> decryptionCode = new HashMap<Integer, Country>();
static {
decryptionCode.put(15, Country.CODE_15);
decryptionCode.put(18, Country.CODE_18);
decryptionCode.put(19, Country.CODE_19);
decryptionCode.put(20, Country.CODE_20);
decryptionCode.put(21, Country.CODE_21);
decryptionCode.put(22, Country.CODE_22);
decryptionCode.put(23, Country.CODE_23);
decryptionCode.put(24, Country.CODE_24);
decryptionCode.put(25, Country.CODE_25);
decryptionCode.put(26, Country.CODE_26);
decryptionCode.put(27, Country.CODE_27);
decryptionCode.put(28, Country.CODE_28);
decryptionCode.put(29, Country.CODE_29);
decryptionCode.put(30, Country.CODE_30);
decryptionCode.put(31, Country.CODE_31);
}
enum Country {
CODE_15(Arrays.asList("NO")),
CODE_16(Arrays.asList("BE", "BI")),
CODE_18(Arrays.asList("DK", "FO", "FI", "GL", "NL")),
CODE_19(Arrays.asList("MK", "SI")),
CODE_20(Arrays.asList("AT", "BA", "EE", "KZ", "XK", "LT", "LU")),
CODE_21(Arrays.asList("CR", "HR", "LV", "LI", "CH")),
CODE_22(Arrays.asList("BH", "BG", "GE", "DE", "IE", "ME", "RS", "GB")),
CODE_23(Arrays.asList("GI", "IL", "AE")),
CODE_24(Arrays.asList("AD", "CZ", "MD", "PK", "RO", "SA", "SK", "ES", "SE", "DZ")),
CODE_25(Arrays.asList("PT", "AO", "CV", "MZ")),
CODE_26(Arrays.asList("IS", "TR", "IR")),
CODE_27(Arrays.asList("FR", "GR", "IT", "MR", "MC", "SM", "CM", "MG", "BF")),
CODE_28(Arrays.asList("AL", "AZ", "CY", "DO", "GT", "HU", "LB", "PL", "BJ", "CI", "ML", "SN")),
CODE_29(Arrays.asList("BR", "PS", "QA", "UA")),
CODE_30(Arrays.asList("JO", "KW", "MU")),
CODE_31(Arrays.asList("MT"));
private final List<String> countries;
Country(List<String> list) {
this.countries = list;
}
List<String> getCountries() {
return countries;
}
boolean containCountryCode(String country) {
return getCountries().contains(country);
}
}
/**
* This functions checks if the account number (ie, FR043568953359583693) is correct according to its size and
* country.
*
* @param accountNumber The account number given in input.
* @return A boolean holding if the account number is correct or not.
*/
private static boolean sizeOk(String accountNumber) {
String str = accountNumber.substring(0, 2);
Country code = decryptionCode.get(accountNumber.length());
if (code == null)
return false;
else
return code.containCountryCode(str);
}
/**
* This function generates an american account number.
*
* @param number The account number given in input.
* @return An american account number, the nine first digits belonging to the original input and the others randomly
* generated.
*/
protected StringBuilder generateAmericanAccountNumber(String number) {
StringBuilder sb = new StringBuilder(number.substring(0, 9)); // $NON-NLS-1$ //
// //$NON-NLS-2$
for (int i = 0; i < 10; ++i) {
sb.append(String.valueOf(rnd.nextInt(10)));
}
return sb;
}
/**
* This function checks if the account number is an American number account.
*
* @param number The account number given in input.
* @return A boolean holding if the account number is correct or not.
*/
protected boolean isAmericanAccount(String number) {
StringBuilder accountNumber = new StringBuilder(number.substring(0, 9));
for (int i = 0; i < 9; i++) {
if (!Character.isDigit(accountNumber.charAt(i))) {
return false;
}
}
return (3
* (Integer.parseInt(String.valueOf(accountNumber.charAt(0)))
+ Integer.parseInt(String.valueOf(accountNumber.charAt(3)))
+ Integer.parseInt(String.valueOf(accountNumber.charAt(6))))
+ 7 * (Integer.parseInt(String.valueOf(accountNumber.charAt(1)))
+ Integer.parseInt(String.valueOf(accountNumber.charAt(4)))
+ Integer.parseInt(String.valueOf(accountNumber.charAt(7))))
+ (Integer.parseInt(String.valueOf(accountNumber.charAt(2)))
+ Integer.parseInt(String.valueOf(accountNumber.charAt(5)))
+ Integer.parseInt(String.valueOf(accountNumber.charAt(8)))))
% 10 == 0;
}
/**
* This function generates an account number. If the parameter is a correct iban number, this function will generate
* an iban number, keeping the original country of where it's from (if the iban it's incorrect, the countru will be
* France (see generateIban())). If the parameter is an correct american account number, this function will generate
* a american number (see generateAmericanAccount).
*
* @param number The account number given in input.
* @return A correct account number.
*/
public StringBuilder generateIban(String number) {
if (!sizeOk(number)) {
return generateIban();
}
StringBuilder sb = new StringBuilder(number.substring(0, 2));
sb.append("00"); //$NON-NLS-1$
for (int i = 0; i < number.length() - 4; ++i) {
sb.append(rnd.nextInt(10));
}
return generateIbanLastPart(sb);
}
/**
* This function is called when generating a French Iban number. The last two digits must be generated using an
* algorithm.
*
* @param sb The French Iban number missing its two last digits.
* @return An int holding the two digits needed to complete the iban.
*/
private static int generateRib(String sb) {
Long b = Long.valueOf(sb.substring(0, 5));
Long g = Long.valueOf(sb.substring(5, 10));
StringBuilder csb = new StringBuilder(""); //$NON-NLS-1$
for (int i = 10; i < 16; ++i) {
csb.append(Character.getNumericValue(sb.charAt(i)));
}
Long d = Long.valueOf(csb.toString());
csb = new StringBuilder(""); //$NON-NLS-1$
for (int i = 16; i < 21; ++i) {
csb.append(Character.getNumericValue(sb.charAt(i)));
}
Long c = Long.valueOf(csb.toString());
return (int) (97 - ((89 * b + 15 * g + 76 * d + 3 * c) % 97));
}
/**
* This function generates a correct French Iban number.
*
* @return A string holding a correct French Iban number.
*/
public StringBuilder generateIban() {
StringBuilder sb = new StringBuilder("FR00"); //$NON-NLS-1$
for (int i = 0; i < 10; ++i) {
sb.append(String.valueOf(rnd.nextInt(10)));
}
for (int i = 0; i < 11; ++i) {
int choice = rnd.nextInt(2);
if (choice == 0) {
sb.append(String.valueOf(rnd.nextInt(10)));
} else {
sb.append((char) (rnd.nextInt(26) + 'A'));
}
}
int rib = generateRib(sb.substring(4));
sb.append(rib / 10);
sb.append(rib % 10);
return generateIbanLastPart(sb);
}
private StringBuilder generateIbanLastPart(StringBuilder sb) {
String sb2 = new String(sb);
sb2 = sb2.substring(4) + sb2.substring(0, 4);
StringBuilder numericAccountNumber = new StringBuilder();
for (int i = 0; i < sb2.length(); i++) {
numericAccountNumber.append(Character.getNumericValue(sb2.charAt(i)));
}
BigInteger ibanNumber = new BigInteger(numericAccountNumber.toString());
int checkDigits = 98 - ibanNumber.mod(MOD97).intValue();
sb.setCharAt(2, Character.forDigit(checkDigits / 10, 10));
sb.setCharAt(3, Character.forDigit(checkDigits % 10, 10));
return sb;
}
}