diff --git a/res/BankCSV_etwurf.csv b/res/BankCSV_etwurf.csv new file mode 100644 index 0000000..f642b78 --- /dev/null +++ b/res/BankCSV_etwurf.csv @@ -0,0 +1,6 @@ +Bank;BLZ;Kontonummer;PIN;Kontostand;Kontoart;Zins %;Ueberziehungsbetrag;Kundennummer;Name;Vorname;Kunde Straße;Kunde PLZ;Kunde Ort +VR Bank Rhein-Neckar;MA2424;4711;1234;50,14 €;Sparkonto;3%;;123456;Mustermann;Max;Bahnhofstraße 1;68159;Mannheim +VR Bank Rhein-Neckar;MA2424;8321;1234;500,14;Girokonto;;1.000,27 €;123456;Mustermann;Max;Bahnhofstraße 1;68159;Mannheim +Berliner Bank;19087;1717;1234;500,14;Sparkonto;1,003;;717171;Mustermann;Max;Bahnhofstraße 1;68159;Mannheim +Berliner Bank;19087;1919;1234;540,2;Sparkonto;1,1;;717171;Mustermann;Max;Bahnhofstraße 1;68159;Mannheim +Zocker Bank;Zock7777;4444;7777;-8000,5;Girokonto;;50000,5;31124;Hogo;Gruber;Berlinestraß 33 / 1;7899;Berlin diff --git a/src/me/teridax/jcash/banking/Account.java b/src/me/teridax/jcash/banking/Account.java index d3055f3..7ef9445 100644 --- a/src/me/teridax/jcash/banking/Account.java +++ b/src/me/teridax/jcash/banking/Account.java @@ -1,5 +1,7 @@ package me.teridax.jcash.banking; +import me.teridax.jcash.decode.Decoder; + import java.util.Objects; @SuppressWarnings("unused") @@ -9,10 +11,33 @@ public abstract class Account { private final int pin; private final double balance; - public Account(int iban, int pin) { + public Account(int iban, int pin, double balance) { this.iban = iban; this.pin = pin; - this.balance = 0.0d; + this.balance = balance; + } + + public static Account fromColumns(String[] columns) { + Objects.requireNonNull(columns); + + var iban = Decoder.decodeUniqueIdentificationNumber(columns[0]); + var pin = Decoder.decodeUniqueIdentificationNumber(columns[1]); + var balance = Decoder.decodeCurrency(columns[2]); + var type = Decoder.decodeName(columns[3]); + + try { + if (type.equals("Sparkonto")) { + var interest = Decoder.decodePercent(columns[4]); + return new SavingsAccount(iban, pin, balance, interest); + } else if (type.equals("Girokonto")) { + var overdraft = Decoder.decodeCurrency(columns[5]); + return new Girokonto(iban, pin, balance, overdraft); + } else { + throw new IllegalArgumentException("Invalid account type: " + type); + } + } catch (IllegalArgumentException | NullPointerException e) { + throw new IllegalArgumentException("Account format: ", e); + } } public int getIban() { diff --git a/src/me/teridax/jcash/banking/Bank.java b/src/me/teridax/jcash/banking/Bank.java index d4f8564..13bad12 100644 --- a/src/me/teridax/jcash/banking/Bank.java +++ b/src/me/teridax/jcash/banking/Bank.java @@ -8,7 +8,7 @@ public final class Bank { private final String blz; private final Map> accounts; - private Bank(String blz, String name) { + Bank(String blz, String name) { this.blz = blz; this.name = name; this.accounts = new HashMap<>(); @@ -22,6 +22,12 @@ public final class Bank { return name; } + public void addAccount(Owner owner, Account account) { + var set = this.accounts.getOrDefault(owner, new HashSet<>()); + set.add(account); + this.accounts.put(owner, set); + } + @Override public boolean equals(Object obj) { if (obj instanceof Bank) { @@ -39,7 +45,7 @@ public final class Bank { return accounts.get(owner).toArray(Account[]::new); } - private static String validateBlz(String maybeBlz) { + public static String validateBlz(String maybeBlz) { var builder = new StringBuilder(); maybeBlz.trim().chars().filter(ch -> Character.isDigit(ch) || Character.isLetter(ch)).forEach(builder::append); var blz = builder.toString(); diff --git a/src/me/teridax/jcash/banking/BankingManagementSystem.java b/src/me/teridax/jcash/banking/BankingManagementSystem.java index 49c6dfb..35d8839 100644 --- a/src/me/teridax/jcash/banking/BankingManagementSystem.java +++ b/src/me/teridax/jcash/banking/BankingManagementSystem.java @@ -1,12 +1,11 @@ package me.teridax.jcash.banking; -import java.io.File; +import me.teridax.jcash.decode.Decoder; + import java.io.IOException; import java.nio.file.Files; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.nio.file.Path; +import java.util.*; public final class BankingManagementSystem { @@ -16,22 +15,43 @@ public final class BankingManagementSystem { this.banks = new HashSet<>(); } - public static BankingManagementSystem loadFromCsv(File file) { + private static String[] tail(String[] array, int index) { + return Arrays.stream(array).skip(index).toArray(String[]::new); + } + public static BankingManagementSystem loadFromCsv(Path file) { try { - var content = Files.readString(file.toPath()); + var bms = new BankingManagementSystem(); + var content = Files.readString(file); - var banks = new ArrayList(); + content.lines().skip(1).forEach(line -> { + var columns = line.split(";"); - content.lines().forEach(line -> { - // load bank and account from line. - // transfer accounts to existing bank if already created + if (columns.length != 14) + throw new IllegalArgumentException("invalid column count: " + columns.length); + + var owner = Owner.fromColumns(tail(columns, 8)); + var account = Account.fromColumns(tail(columns, 2)); + + var blz = Bank.validateBlz(columns[1]); + var name = Decoder.decodeName(columns[0]); + var bankOfLine = new Bank(blz, name); + + var bankOfSet = bms.banks.stream().filter(b -> b.equals(bankOfLine)).findFirst(); + if (bankOfSet.isPresent()) { + bankOfSet.get().addAccount(owner, account); + } else { + bankOfLine.addAccount(owner, account); + bms.banks.add(bankOfLine); + } }); - } catch (IOException e) { - throw new IllegalArgumentException("Could not read file " + file); - } + return bms; - return null; + } catch (IOException e) { + throw new IllegalArgumentException("Could not read file " + file, e); + } catch (IllegalArgumentException | NullPointerException e) { + throw new IllegalArgumentException("Could not parse file " + file, e); + } } } diff --git a/src/me/teridax/jcash/banking/Girokonto.java b/src/me/teridax/jcash/banking/Girokonto.java index 5fbea62..b277057 100644 --- a/src/me/teridax/jcash/banking/Girokonto.java +++ b/src/me/teridax/jcash/banking/Girokonto.java @@ -4,8 +4,8 @@ public class Girokonto extends Account { private final double overdraft; - public Girokonto(int iban, int pin, double overdraft) { - super(iban, pin); + public Girokonto(int iban, int pin, double balance, double overdraft) { + super(iban, pin, balance); this.overdraft = overdraft; } diff --git a/src/me/teridax/jcash/banking/Owner.java b/src/me/teridax/jcash/banking/Owner.java index dcc06d4..07b1bab 100644 --- a/src/me/teridax/jcash/banking/Owner.java +++ b/src/me/teridax/jcash/banking/Owner.java @@ -1,5 +1,7 @@ package me.teridax.jcash.banking; +import me.teridax.jcash.decode.Decoder; + import java.util.Objects; @SuppressWarnings("unused") @@ -11,9 +13,7 @@ public final class Owner { private int zip; private String city; - private Owner() { } - - public Owner(int uid, String familyName, String name, int zip, String city, String street) { + private Owner(int uid, String familyName, String name, int zip, String city, String street) { this.uid = uid; this.familyName = familyName; this.name = name; @@ -22,6 +22,22 @@ public final class Owner { this.street = street; } + public static Owner fromColumns(String... columns) throws IllegalArgumentException, NullPointerException { + Objects.requireNonNull(columns); + + if (columns.length != 6) + throw new IllegalArgumentException("Invalid number of columns: " + columns.length); + + var uid = Decoder.decodeUniqueIdentificationNumber(columns[0]); + var familyName = Decoder.decodeName(columns[1]); + var name = Decoder.decodeName(columns[2]); + var street = Decoder.decodeStreet(columns[3]); + var zip = Decoder.decodeUniqueIdentificationNumber(columns[4]); + var city = Decoder.decodeName(columns[5]); + + return new Owner(uid, familyName, name, zip, city, street); + } + public long getUid() { return uid; } diff --git a/src/me/teridax/jcash/banking/SavingsAccount.java b/src/me/teridax/jcash/banking/SavingsAccount.java index 0e0570c..04a056e 100644 --- a/src/me/teridax/jcash/banking/SavingsAccount.java +++ b/src/me/teridax/jcash/banking/SavingsAccount.java @@ -5,8 +5,8 @@ public class SavingsAccount extends Account { private final double interest; - public SavingsAccount(int iban, int pin, double interest) { - super(iban, pin); + public SavingsAccount(int iban, int pin, double balance, double interest) { + super(iban, pin, balance); this.interest = interest; } diff --git a/src/me/teridax/jcash/banking/Testing.java b/src/me/teridax/jcash/banking/Testing.java new file mode 100644 index 0000000..0154cfc --- /dev/null +++ b/src/me/teridax/jcash/banking/Testing.java @@ -0,0 +1,13 @@ +package me.teridax.jcash.banking; + +import org.junit.Test; + +import java.nio.file.Paths; + +public class Testing { + + @Test + public void test() { + BankingManagementSystem.loadFromCsv(Paths.get("res/BankCSV_etwurf.csv")); + } +} diff --git a/src/me/teridax/jcash/decode/Decoder.java b/src/me/teridax/jcash/decode/Decoder.java index 66542f1..b0e805b 100644 --- a/src/me/teridax/jcash/decode/Decoder.java +++ b/src/me/teridax/jcash/decode/Decoder.java @@ -38,7 +38,7 @@ public class Decoder { } } - public static int decodeSerialNumber(String number) throws IllegalArgumentException, NullPointerException { + public static int decodeUniqueIdentificationNumber(String number) throws IllegalArgumentException, NullPointerException { Objects.requireNonNull(number); // check if the string is a valid unsigned number @@ -57,11 +57,13 @@ public class Decoder { var trimmed = name.trim(); - // check if the string is a valid unsigned number - if (!trimmed.trim().chars().allMatch(Character::isLetter)) - throw new IllegalArgumentException("Not a valid name: " + name); - - return trimmed; + var pattern = Pattern.compile("[\\w-\\s]+", Pattern.CASE_INSENSITIVE); + var matcher = pattern.matcher(trimmed); + if (matcher.find()) { + return matcher.group(); + } else { + throw new IllegalArgumentException("not a void name"); + } } public static String decodeStreet(String street) throws IllegalArgumentException, NullPointerException { @@ -78,9 +80,9 @@ public class Decoder { @Test public void testDecodeSuccessfulFunctions() { - decodeSerialNumber("95786978625347895"); - decodeSerialNumber(" 927856347 "); - decodeSerialNumber("0"); + decodeUniqueIdentificationNumber("95786978625347895"); + decodeUniqueIdentificationNumber(" 927856347 "); + decodeUniqueIdentificationNumber("0"); decodeName("Adolf"); decodeName("Günther"); @@ -101,10 +103,10 @@ public class Decoder { @Test(expected = IllegalArgumentException.class) public void testDecodeInvalidFunctions() { - decodeSerialNumber("q0948tvb6q047t 740 t74z0tz 784"); - decodeSerialNumber("-39867.8475"); - decodeSerialNumber("-398678475"); - decodeSerialNumber(" ß9qu908t76q34798t6q734vb9843"); + decodeUniqueIdentificationNumber("q0948tvb6q047t 740 t74z0tz 784"); + decodeUniqueIdentificationNumber("-39867.8475"); + decodeUniqueIdentificationNumber("-398678475"); + decodeUniqueIdentificationNumber(" ß9qu908t76q34798t6q734vb9843"); decodeName("John Doe"); decodeName("3490qt67v 0b34");