diff --git a/src/me/teridax/jcash/Main.java b/src/me/teridax/jcash/Main.java index 75463e8..35d9c15 100644 --- a/src/me/teridax/jcash/Main.java +++ b/src/me/teridax/jcash/Main.java @@ -65,7 +65,7 @@ public final class Main { // when we have logged in set the account viewer as window content login.addAccountSelectionListener(account -> { - var profileCont = new AccountController(account); + var profileCont = new AccountController(account, login.getData().getBms()); this.window.setContentPane(profileCont.getView()); this.window.revalidate(); this.window.repaint(); @@ -78,6 +78,7 @@ public final class Main { } catch (IllegalStateException e) { System.out.println("no file selected. goodbye"); + System.exit(0); } }); } diff --git a/src/me/teridax/jcash/banking/Account.java b/src/me/teridax/jcash/banking/Account.java index c9a8a49..fb02c06 100644 --- a/src/me/teridax/jcash/banking/Account.java +++ b/src/me/teridax/jcash/banking/Account.java @@ -1,14 +1,27 @@ package me.teridax.jcash.banking; -import me.teridax.jcash.decode.StringUtils; +import me.teridax.jcash.decode.StringDecoder; import java.util.Objects; +/** + * Base class for bank accounts. + * Stores the iban, pin and balance. + */ @SuppressWarnings("unused") public abstract class Account { + /** + * International bank account number + */ private final int iban; + /** + * Personal identification number + */ private final int pin; + /** + * Balance of this account + */ private double balance; public Account(int iban, int pin, double balance) { @@ -17,20 +30,30 @@ public abstract class Account { this.balance = balance; } - public static Account fromColumns(String[] columns) { + /** + * Parses a row of a fixed amount of columns into an account. + * This function will attempt to create an instance of two classes which inherit from Account. + * @param columns array of 6 strings + * @return either an instance of {@link me.teridax.jcash.banking.SavingsAccount} or an instance of {@link me.teridax.jcash.banking.CurrentAccount} + * @throws IllegalArgumentException if the account type cannot be determined or the provided data is invalid + * @throws NullPointerException if columns is null + */ + public static Account fromColumns(String[] columns) throws IllegalArgumentException, NullPointerException { Objects.requireNonNull(columns); - var iban = StringUtils.decodeUniqueIdentificationNumber(columns[0]); - var pin = StringUtils.decodeUniqueIdentificationNumber(columns[1]); - var balance = StringUtils.decodeCurrency(columns[2]); - var type = StringUtils.decodeName(columns[3]); + // deserialize fields + var iban = StringDecoder.decodeUniqueIdentificationNumber(columns[0]); + var pin = StringDecoder.decodeUniqueIdentificationNumber(columns[1]); + var balance = StringDecoder.decodeCurrency(columns[2]); + var type = StringDecoder.decodeName(columns[3]); + // try to detect the specific runtime class to deserialize try { if (type.equals("Sparkonto")) { - var interest = StringUtils.decodePercent(columns[4]); + var interest = StringDecoder.decodePercent(columns[4]); return new SavingsAccount(iban, pin, balance, interest); } else if (type.equals("Girokonto")) { - var overdraft = StringUtils.decodeCurrency(columns[5]); + var overdraft = StringDecoder.decodeCurrency(columns[5]); return new CurrentAccount(iban, pin, balance, overdraft); } else { throw new IllegalArgumentException("Invalid account type: " + type); @@ -65,6 +88,12 @@ public abstract class Account { return false; } + /** + * Returns a description of the account in form a string. + * This method is not equal to {@link #toString()} but is intended to be + * a method used to retrieve a formatted representation for guis. + * @return a basic description of the account in form a string + */ public String getDescription() { return String.format("%s (%s)", iban, getClass().getSimpleName()); } @@ -78,6 +107,11 @@ public abstract class Account { return String.format("@Account [iban=%d, pin=%d, balance=%.2f]", iban, pin, balance); } + /** + * Takeoff a certain amount of money from this accounts balance. + * Saturates the result if the value to subtract is greater than the balance present. + * @param amount the amount of money to subtract from the accounts balance + */ public void takeoff(double amount) { this.balance = Math.max(0, this.balance - amount); } diff --git a/src/me/teridax/jcash/banking/Bank.java b/src/me/teridax/jcash/banking/Bank.java index 03b347b..f2e59e4 100644 --- a/src/me/teridax/jcash/banking/Bank.java +++ b/src/me/teridax/jcash/banking/Bank.java @@ -3,10 +3,24 @@ package me.teridax.jcash.banking; import java.util.*; import java.util.regex.Pattern; +/** + * Bank storing a name, blz and a list of owners and their accounts for this bank. + */ @SuppressWarnings("unused") public final class Bank { + /** + * The name of the bank + */ private final String name; + + /** + * Bankleitzahl. Identification number of banks in germany + */ private final String blz; + + /** + * A map of banking accounts associated with their respective owner + */ private final Map> accounts; Bank(String blz, String name) { @@ -23,6 +37,13 @@ public final class Bank { return name; } + /** + * Add a new account and its associated owner to this bank. + * If the owner is already present the account is added to the existing owner. + * If the account is already present for the owner the old account gets overwritten. + * @param owner the owner of the account + * @param account the account of the owner + */ public void addAccount(Owner owner, Account account) { var set = this.accounts.getOrDefault(owner, new HashSet<>()); set.add(account); @@ -42,12 +63,23 @@ public final class Bank { return Objects.hash(blz); } + /** + * Retrieve all accounts owned by the specific owner. + * @param owner the owner for which accounts are to be retrieved + * @return all accounts owned by the owner and managed by this bank + */ public Account[] getAccountsOfOwner(Owner owner) { return accounts.get(owner).toArray(Account[]::new); } - public static String validateBlz(String maybeBlz) { - var pattern = Pattern.compile("[\\w\\d-_]+"); + /** + * Validates the given BLZ. If the blz is not valid an exception is thrown. + * @param maybeBlz a string to be tested for being a blz + * @return returns the correctly formatted blz + * @throws IllegalArgumentException if the supplied argument is not a valid blz + */ + public static String validateBlz(String maybeBlz) throws IllegalArgumentException { + var pattern = Pattern.compile("[\\w-_]+"); var matcher = pattern.matcher(maybeBlz); if (matcher.find()) @@ -56,11 +88,17 @@ public final class Bank { throw new IllegalArgumentException("not a valid BLZ: " + maybeBlz); } + /** + * Return a profile of the specified international bank account number. + * @param iban the number to create a profile for + * @return the profile for the iban account + */ public Optional getAccount(int iban) { + // find the account which has the supplied iban + for (var owner : this.accounts.entrySet()) { for (var account : owner.getValue()) { - var tmp = account.getIban(); - if (tmp == iban) { + if (iban == account.getIban()) { return Optional.of(new Profile(owner.getKey(), this, account, getAccountsOfOwner(owner.getKey()))); } } diff --git a/src/me/teridax/jcash/banking/BankingManagementSystem.java b/src/me/teridax/jcash/banking/BankingManagementSystem.java index 57fe941..2192ec6 100644 --- a/src/me/teridax/jcash/banking/BankingManagementSystem.java +++ b/src/me/teridax/jcash/banking/BankingManagementSystem.java @@ -1,42 +1,90 @@ package me.teridax.jcash.banking; -import me.teridax.jcash.decode.StringUtils; +import me.teridax.jcash.decode.StringDecoder; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; +/** + * Management system for banks and all accounts provided by these banks and their respective account owners. + * This class serves a read only database which can only modify runtime data without any respect to CRUD or the ACID + * principles. + */ public final class BankingManagementSystem { + /** + * A set of banks + */ private final Set banks; private BankingManagementSystem() { this.banks = new HashSet<>(); } + /** + * Utility method for retrieving the tail of a string array. + * This method return all items which follow the n-th index denoted by index. + * @param array the array to take the tail of + * @param index the amount trailing indices to skip + * @return an array containing the last elements of the supplied array + */ private static String[] tail(String[] array, int index) { return Arrays.stream(array).skip(index).toArray(String[]::new); } - public static BankingManagementSystem loadFromCsv(Path file) { + /** + * Loads an instance of this class from a csv file. + * The csv file must comply to the formats of {@link StringDecoder} and must contain + * the following columns in this exact order: + *
    + *
  1. Bank name
  2. + *
  3. Bank BLZ
  4. + *
  5. Account IBAN
  6. + *
  7. Account PIN
  8. + *
  9. Account Balance
  10. + *
  11. Account type
  12. + *
  13. Account Interest rate
  14. + *
  15. Account Overdraft
  16. + *
  17. Owner uuid
  18. + *
  19. Owner family name
  20. + *
  21. Owner name
  22. + *
  23. Owner street
  24. + *
  25. Owner plz
  26. + *
  27. Owner city
  28. + *
+ * The file can contain a header line which gets skipped when reading. + * Note that the first line is always skipped and the name of every column is not checked in any way. + * @param file the file to parse + * @throws IllegalArgumentException if the file cannot be read or the containing data is invalid + * @return a valid BMS + */ + public static BankingManagementSystem loadFromCsv(Path file) throws IllegalArgumentException { try { var bms = new BankingManagementSystem(); var content = Files.readString(file); + // read line by line + // and skip the first line content.lines().skip(1).forEach(line -> { + // split the line into columns var columns = line.split(";"); + // one line must contain exactly 14 columns if (columns.length != 14) throw new IllegalArgumentException("invalid column count: " + columns.length); + // read basic fields var owner = Owner.fromColumns(tail(columns, 8)); var account = Account.fromColumns(tail(columns, 2)); var blz = Bank.validateBlz(columns[1]); - var name = StringUtils.decodeName(columns[0]); + var name = StringDecoder.decodeName(columns[0]); var bankOfLine = new Bank(blz, name); + // add banks to bms + var bankOfSet = bms.banks.stream().filter(b -> b.equals(bankOfLine)).findFirst(); if (bankOfSet.isPresent()) { bankOfSet.get().addAccount(owner, account); @@ -55,6 +103,11 @@ public final class BankingManagementSystem { } } + /** + * Return a bank with the given blz. + * @param blz the blz to search bank of + * @return the bank with this blz or none + */ public Optional getBank(String blz) { return this.banks.stream().filter(b -> b.getBlz().equals(blz)).findFirst(); } diff --git a/src/me/teridax/jcash/banking/CurrentAccount.java b/src/me/teridax/jcash/banking/CurrentAccount.java index dc559bf..5346226 100644 --- a/src/me/teridax/jcash/banking/CurrentAccount.java +++ b/src/me/teridax/jcash/banking/CurrentAccount.java @@ -1,7 +1,14 @@ package me.teridax.jcash.banking; -public class CurrentAccount extends Account { +/** + * Immutable currency account storing only overdraft. + * English equivalent to "Girokonto" + */ +public final class CurrentAccount extends Account { + /** + * Overdraft amount in currency. + */ private final double overdraft; public CurrentAccount(int iban, int pin, double balance, double overdraft) { diff --git a/src/me/teridax/jcash/banking/Tests.java b/src/me/teridax/jcash/banking/DataClassTests.java similarity index 94% rename from src/me/teridax/jcash/banking/Tests.java rename to src/me/teridax/jcash/banking/DataClassTests.java index ab644fb..eed1dfa 100644 --- a/src/me/teridax/jcash/banking/Tests.java +++ b/src/me/teridax/jcash/banking/DataClassTests.java @@ -4,7 +4,7 @@ import org.junit.Test; import java.nio.file.Paths; -public class Tests { +public class DataClassTests { @Test public void test() { diff --git a/src/me/teridax/jcash/banking/Owner.java b/src/me/teridax/jcash/banking/Owner.java index 5497df1..3822b62 100644 --- a/src/me/teridax/jcash/banking/Owner.java +++ b/src/me/teridax/jcash/banking/Owner.java @@ -1,17 +1,26 @@ package me.teridax.jcash.banking; -import me.teridax.jcash.decode.StringUtils; +import me.teridax.jcash.decode.StringDecoder; import java.util.Objects; +/** + * Represents a person owning an account. + */ @SuppressWarnings("unused") public final class Owner { - private int uid; - private String familyName; - private String name; - private String street; - private int zip; - private String city; + /** + * unique identifier + */ + private final int uid; + private final String familyName; + private final String name; + private final String street; + /** + * postal code + */ + private final int zip; + private final String city; private Owner(int uid, String familyName, String name, int zip, String city, String street) { this.uid = uid; @@ -22,18 +31,26 @@ public final class Owner { this.street = street; } + /** + * Create a new instance of this class parsed from the columns. + * @param columns the fields of this class as strings + * @return an instance of this class + * @throws IllegalArgumentException if the supplied columns is invalid + * @throws NullPointerException if columns is null + */ 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 = StringUtils.decodeUniqueIdentificationNumber(columns[0]); - var familyName = StringUtils.decodeName(columns[1]); - var name = StringUtils.decodeName(columns[2]); - var street = StringUtils.decodeStreet(columns[3]); - var zip = StringUtils.decodeUniqueIdentificationNumber(columns[4]); - var city = StringUtils.decodeName(columns[5]); + // decode fields + var uid = StringDecoder.decodeUniqueIdentificationNumber(columns[0]); + var familyName = StringDecoder.decodeName(columns[1]); + var name = StringDecoder.decodeName(columns[2]); + var street = StringDecoder.decodeStreet(columns[3]); + var zip = StringDecoder.decodeUniqueIdentificationNumber(columns[4]); + var city = StringDecoder.decodeName(columns[5]); return new Owner(uid, familyName, name, zip, city, street); } diff --git a/src/me/teridax/jcash/banking/Profile.java b/src/me/teridax/jcash/banking/Profile.java index 9ac33a6..3e35ef9 100644 --- a/src/me/teridax/jcash/banking/Profile.java +++ b/src/me/teridax/jcash/banking/Profile.java @@ -1,11 +1,17 @@ package me.teridax.jcash.banking; +/** + * Groups an owner and all of its accounts registered at a specific bank together. + * The profile is oriented around a primary account of the owner. + * This class is meant to be a read only reference for easier runtime processing of + * data packed into nested structure of objects. + */ public class Profile { - private Owner owner; - private Bank bank; + private final Owner owner; + private final Bank bank; private Account primaryAccount; - private Account[] accounts; + private final Account[] accounts; public Profile(Owner owner, Bank bank, Account account, Account[] accounts) { this.owner = owner; @@ -30,6 +36,11 @@ public class Profile { return accounts; } + /** + * Set the primary account of this profile based on a descriptive text. + * This method may not change anything if no account can be found with a matching description + * @param description the description to find a matching account for + */ public void setPrimaryAccount(String description) { for (Account account : accounts) { if (account.getDescription().equals(description)) { diff --git a/src/me/teridax/jcash/banking/SavingsAccount.java b/src/me/teridax/jcash/banking/SavingsAccount.java index 04a056e..6f7beda 100644 --- a/src/me/teridax/jcash/banking/SavingsAccount.java +++ b/src/me/teridax/jcash/banking/SavingsAccount.java @@ -1,8 +1,15 @@ package me.teridax.jcash.banking; +/** + * Savings account representing a german "Sparkonto". + * To the balance a certain interest rate is added. + */ @SuppressWarnings("unused") -public class SavingsAccount extends Account { +public final class SavingsAccount extends Account { + /** + * interest rate applied to the balance of the super class + */ private final double interest; public SavingsAccount(int iban, int pin, double balance, double interest) { diff --git a/src/me/teridax/jcash/decode/StringUtils.java b/src/me/teridax/jcash/decode/StringDecoder.java similarity index 99% rename from src/me/teridax/jcash/decode/StringUtils.java rename to src/me/teridax/jcash/decode/StringDecoder.java index 5145466..fd8fa63 100644 --- a/src/me/teridax/jcash/decode/StringUtils.java +++ b/src/me/teridax/jcash/decode/StringDecoder.java @@ -14,7 +14,7 @@ import static junit.framework.TestCase.assertEquals; * Utility class for converting various single line strings into a specific data type according * to a format mostly dictated by a locale. */ -public class StringUtils { +public class StringDecoder { /** * Locale to use when converting strings diff --git a/src/me/teridax/jcash/gui/account/AccountController.java b/src/me/teridax/jcash/gui/account/AccountController.java index ab849b1..07c6f67 100644 --- a/src/me/teridax/jcash/gui/account/AccountController.java +++ b/src/me/teridax/jcash/gui/account/AccountController.java @@ -1,18 +1,26 @@ package me.teridax.jcash.gui.account; import me.teridax.jcash.Main; +import me.teridax.jcash.banking.BankingManagementSystem; import me.teridax.jcash.banking.Profile; import me.teridax.jcash.gui.deposit.DepositDialog; import me.teridax.jcash.gui.takeoff.TakeoffDialog; import me.teridax.jcash.gui.transfer.TransferDialog; +/** + * Controller for controlling the gui of an account. + */ public class AccountController { - + private final AccountData data; + /** + * GUI if an account + */ private final AccountView view; - public AccountController(Profile profile) { + public AccountController(Profile profile, BankingManagementSystem bms) { this.view = new AccountView(); this.view.setProfile(profile); + this.data = new AccountData(bms); createListeners(profile); } @@ -29,17 +37,11 @@ public class AccountController { Main.getInstance().showLoginScreen(); }); - this.view.getDeposit().addActionListener(e -> { - new DepositDialog(profile.getPrimaryAccount(), () -> this.view.setProfile(profile)); - }); + this.view.getDeposit().addActionListener(e -> new DepositDialog(profile.getPrimaryAccount(), () -> this.view.setProfile(profile))); - this.view.getTakeoff().addActionListener(e -> { - new TakeoffDialog(profile.getPrimaryAccount(), () -> this.view.setProfile(profile)); - }); + this.view.getTakeoff().addActionListener(e -> new TakeoffDialog(profile.getPrimaryAccount(), () -> this.view.setProfile(profile))); - this.view.getTransfer().addActionListener(e -> { - new TransferDialog(profile.getPrimaryAccount(), () -> this.view.setProfile(profile)); - }); + this.view.getTransfer().addActionListener(e -> new TransferDialog(profile.getPrimaryAccount(), data.getBms(), () -> this.view.setProfile(profile))); } public AccountView getView() { diff --git a/src/me/teridax/jcash/gui/account/AccountData.java b/src/me/teridax/jcash/gui/account/AccountData.java new file mode 100644 index 0000000..37e02d3 --- /dev/null +++ b/src/me/teridax/jcash/gui/account/AccountData.java @@ -0,0 +1,16 @@ +package me.teridax.jcash.gui.account; + +import me.teridax.jcash.banking.BankingManagementSystem; + +public class AccountData { + + private final BankingManagementSystem bms; + + public AccountData(BankingManagementSystem bms) { + this.bms = bms; + } + + public BankingManagementSystem getBms() { + return bms; + } +} diff --git a/src/me/teridax/jcash/gui/account/AccountView.java b/src/me/teridax/jcash/gui/account/AccountView.java index 781533c..e716a2d 100644 --- a/src/me/teridax/jcash/gui/account/AccountView.java +++ b/src/me/teridax/jcash/gui/account/AccountView.java @@ -1,11 +1,13 @@ package me.teridax.jcash.gui.account; import me.teridax.jcash.banking.*; -import me.teridax.jcash.decode.StringUtils; +import me.teridax.jcash.decode.StringDecoder; import javax.swing.*; import java.awt.*; +import static javax.swing.SwingConstants.RIGHT; + public class AccountView extends JPanel { private JTextField iban; @@ -16,13 +18,8 @@ public class AccountView extends JPanel { private JTextField type; private JTextField typeSpecialProperty; private JTextField balance; - private JLabel typeSpecialLabel; - private JComboBox accountSelection; - - private JPanel content; - private JButton logout; private JButton transfer; private JButton deposit; @@ -47,12 +44,12 @@ public class AccountView extends JPanel { this.name.setText(owner.getName() + " " + owner.getFamilyName()); this.address.setText(owner.getStreet() + " " + owner.getCity()); - this.balance.setText(StringUtils.LOCAL_NUMBER_FORMAT.format(account.getBalance()) + " €"); + this.balance.setText(StringDecoder.LOCAL_NUMBER_FORMAT.format(account.getBalance()) + " €"); this.type.setText(account.getClass().getSimpleName()); if (account instanceof CurrentAccount) { this.typeSpecialLabel.setText("Overdraft"); - this.typeSpecialProperty.setText( StringUtils.LOCAL_NUMBER_FORMAT.format(((CurrentAccount) account).getOverdraft()) + " €"); + this.typeSpecialProperty.setText( StringDecoder.LOCAL_NUMBER_FORMAT.format(((CurrentAccount) account).getOverdraft()) + " €"); } else if (account instanceof SavingsAccount) { this.typeSpecialLabel.setText("Interest rate"); this.typeSpecialProperty.setText( ((SavingsAccount) account).getInterest() + " %" ); @@ -67,10 +64,11 @@ public class AccountView extends JPanel { } private void createLayout() { - setLayout(new BorderLayout(16, 16)); - add(new JScrollPane(content), BorderLayout.CENTER); + var content = new JPanel(new GridBagLayout()); + + this.setLayout(new BorderLayout(16, 16)); + this.add(new JScrollPane(content), BorderLayout.CENTER); - content.setLayout(new GridBagLayout()); var constraints = new GridBagConstraints(); constraints.gridwidth = 4; @@ -80,14 +78,14 @@ public class AccountView extends JPanel { accountSelectionPanel.add(iban, BorderLayout.CENTER); accountSelectionPanel.add(accountSelection, BorderLayout.EAST); - addInputRow(constraints, content, accountSelectionPanel, 1, new JLabel("IBAN", SwingConstants.RIGHT)); - addInputRow(constraints, content, name, 2, new JLabel("Name/Family-name", SwingConstants.RIGHT)); - addInputRow(constraints, content, address, 3, new JLabel("Address", SwingConstants.RIGHT)); - addInputRow(constraints, content, bankName, 4, new JLabel("Bank", SwingConstants.RIGHT)); - addInputRow(constraints, content, blz, 5, new JLabel("BLZ", SwingConstants.RIGHT)); - addInputRow(constraints, content, type, 6, new JLabel("Account", SwingConstants.RIGHT)); + addInputRow(constraints, content, accountSelectionPanel, 1, new JLabel("IBAN", RIGHT)); + addInputRow(constraints, content, name, 2, new JLabel("Name/Family-name", RIGHT)); + addInputRow(constraints, content, address, 3, new JLabel("Address", RIGHT)); + addInputRow(constraints, content, bankName, 4, new JLabel("Bank", RIGHT)); + addInputRow(constraints, content, blz, 5, new JLabel("BLZ", RIGHT)); + addInputRow(constraints, content, type, 6, new JLabel("Account", RIGHT)); addInputRow(constraints, content, typeSpecialProperty, 7, typeSpecialLabel); - addInputRow(constraints, content, balance, 8, new JLabel("Balance", SwingConstants.RIGHT)); + addInputRow(constraints, content, balance, 8, new JLabel("Balance", RIGHT)); var buttonPanel = Box.createHorizontalBox(); buttonPanel.add(Box.createHorizontalStrut(4)); @@ -121,18 +119,25 @@ public class AccountView extends JPanel { this.balance.setEditable(false); this.typeSpecialProperty.setEditable(false); - this.typeSpecialLabel = new JLabel("", SwingConstants.RIGHT); + this.typeSpecialLabel = new JLabel("", RIGHT); this.accountSelection = new JComboBox<>(); - this.content = new JPanel(); - this.logout = new JButton("Logout"); this.transfer = new JButton("Transfer"); this.deposit = new JButton("Deposit"); this.takeoff = new JButton("Takeoff"); } + /** + * Add a new row of components to the specified target component. + * This will add label to the right side of the next row and the specified component to the left. + * @param constraints the constraint to use. Must be non-null + * @param target the base component to add a row to + * @param comp the component to add to the left side + * @param row the row to add the components to + * @param label the label to add to the left side + */ private void addInputRow(GridBagConstraints constraints, JComponent target, JComponent comp, int row, JLabel label) { constraints.gridwidth = 1; constraints.gridx = 1; diff --git a/src/me/teridax/jcash/gui/deposit/DepositDialog.java b/src/me/teridax/jcash/gui/deposit/DepositDialog.java index 00e9e5b..67ae7ef 100644 --- a/src/me/teridax/jcash/gui/deposit/DepositDialog.java +++ b/src/me/teridax/jcash/gui/deposit/DepositDialog.java @@ -5,7 +5,7 @@ import me.teridax.jcash.banking.Account; public class DepositDialog { public DepositDialog(Account account, Runnable onDeposit) { - var view = new DepositView(account); + var view = new DepositView(); view.getDeposit().addActionListener(e -> { account.deposit(view.getAmount()); onDeposit.run(); diff --git a/src/me/teridax/jcash/gui/deposit/DepositView.java b/src/me/teridax/jcash/gui/deposit/DepositView.java index 06529cc..f48cdc0 100644 --- a/src/me/teridax/jcash/gui/deposit/DepositView.java +++ b/src/me/teridax/jcash/gui/deposit/DepositView.java @@ -1,7 +1,7 @@ package me.teridax.jcash.gui.deposit; import me.teridax.jcash.banking.Account; -import me.teridax.jcash.decode.StringUtils; +import me.teridax.jcash.decode.StringDecoder; import javax.swing.*; import java.awt.*; @@ -15,8 +15,8 @@ public class DepositView { private JButton deposit; private JFormattedTextField value; - public DepositView(Account account) { - createComponents(account); + public DepositView() { + createComponents(); layoutComponents(); } @@ -80,12 +80,12 @@ public class DepositView { dialog.getContentPane().add(buttonPanel, c); } - private void createComponents(Account account) { + private void createComponents() { this.dialog = new JDialog(); this.cancel = new JButton("Cancel"); this.deposit = new JButton("Deposit"); - this.value = new JFormattedTextField(StringUtils.LOCAL_NUMBER_FORMAT); + this.value = new JFormattedTextField(StringDecoder.LOCAL_NUMBER_FORMAT); this.dialog.setContentPane(new JPanel(new GridBagLayout())); } diff --git a/src/me/teridax/jcash/gui/login/AccountSelectionListener.java b/src/me/teridax/jcash/gui/login/AccountSelectionListener.java index abeac4f..5e5de2c 100644 --- a/src/me/teridax/jcash/gui/login/AccountSelectionListener.java +++ b/src/me/teridax/jcash/gui/login/AccountSelectionListener.java @@ -2,7 +2,15 @@ package me.teridax.jcash.gui.login; import me.teridax.jcash.banking.Profile; +/** + * Listens for changes in a selected account. + */ public interface AccountSelectionListener { + /** + * Run when a new account is selected. + * The selected account is set as the primary account of the profile + * @param account the profile for the selected account + */ void onAccountSelected(Profile account); } diff --git a/src/me/teridax/jcash/gui/login/LoginController.java b/src/me/teridax/jcash/gui/login/LoginController.java index 06a614f..88c1566 100644 --- a/src/me/teridax/jcash/gui/login/LoginController.java +++ b/src/me/teridax/jcash/gui/login/LoginController.java @@ -6,6 +6,9 @@ import javax.swing.*; import java.awt.event.ActionEvent; import java.util.Optional; +import static javax.swing.JOptionPane.ERROR_MESSAGE; +import static javax.swing.JOptionPane.showMessageDialog; + public class LoginController { private final LoginView view; @@ -46,12 +49,13 @@ public class LoginController { var blz = this.view.getBlz().getText(); var iban = this.getIban(); if (iban.isEmpty()) { - JOptionPane.showMessageDialog(null, "invalid IBAN", "Faulty login attempt", JOptionPane.ERROR_MESSAGE); + showMessageDialog(null, "invalid IBAN", "Faulty login attempt", ERROR_MESSAGE); return; } + var pin = this.getPin(); if (pin.isEmpty()) { - JOptionPane.showMessageDialog(null, "invalid pin", "Faulty login attempt", JOptionPane.ERROR_MESSAGE); + showMessageDialog(null, "invalid pin", "Faulty login attempt", ERROR_MESSAGE); return; } @@ -59,7 +63,7 @@ public class LoginController { if (account.isPresent()) { this.listener.onAccountSelected(account.get()); } else { - JOptionPane.showMessageDialog(null, "invalid login credentials", "Faulty login attempt", JOptionPane.ERROR_MESSAGE); + showMessageDialog(null, "invalid login credentials", "Faulty login attempt", ERROR_MESSAGE); } } @@ -70,4 +74,8 @@ public class LoginController { public LoginView getView() { return view; } + + public LoginData getData() { + return data; + } } diff --git a/src/me/teridax/jcash/gui/login/LoginData.java b/src/me/teridax/jcash/gui/login/LoginData.java index 3183c99..bb37955 100644 --- a/src/me/teridax/jcash/gui/login/LoginData.java +++ b/src/me/teridax/jcash/gui/login/LoginData.java @@ -5,6 +5,9 @@ import me.teridax.jcash.banking.Profile; import java.util.Optional; +/** + * Wrapper class for a {@link me.teridax.jcash.banking.BankingManagementSystem} + */ public class LoginData { private final BankingManagementSystem bms; @@ -13,6 +16,13 @@ public class LoginData { this.bms = bms; } + /** + * authenticate the specified account with the provided pin. + * @param blz the bank identifier + * @param iban the account identifier + * @param pin the pin for the account to authenticate with + * @return an optional wrapping the specified account if authentication was successful + */ public Optional authenticateAccount(String blz, int iban, int pin) { var optionalBank = bms.getBank(blz); if (optionalBank.isEmpty()) @@ -21,4 +31,8 @@ public class LoginData { var profile = optionalBank.get().getAccount(iban); return profile.filter(value -> value.getPrimaryAccount().getPin() == pin); } + + public BankingManagementSystem getBms() { + return bms; + } } diff --git a/src/me/teridax/jcash/gui/login/LoginView.java b/src/me/teridax/jcash/gui/login/LoginView.java index 6d869c0..cd2d17f 100644 --- a/src/me/teridax/jcash/gui/login/LoginView.java +++ b/src/me/teridax/jcash/gui/login/LoginView.java @@ -7,34 +7,32 @@ import java.text.NumberFormat; public class LoginView extends JPanel { - private final JFormattedTextField blz; - private final JFormattedTextField iban; - private final JPasswordField pin; - private final JButton login; + private JFormattedTextField blz; + private JFormattedTextField iban; + private JPasswordField pin; + private JButton login; public LoginView() { - this.blz = new JFormattedTextField("MA2424"); - this.iban = new JFormattedTextField(getNumberFormat()); - this.iban.setText("4711"); - this.pin = new JPasswordField("1234"); - this.login = new JButton("Login"); + createComponents(); + layoutComponents(); + } + + private void layoutComponents() { + var content = new JPanel(new GridBagLayout()); - setLayout(new BorderLayout(16, 16)); - var content = new JPanel(); this.setBorder(BorderFactory.createEmptyBorder(8,8,8,8)); - add(new JScrollPane(content), BorderLayout.CENTER); - add(new JLabel("Bankautomat"), BorderLayout.NORTH); + this.setLayout(new BorderLayout(16, 16)); + this.add(new JScrollPane(content), BorderLayout.CENTER); + this.add(new JLabel("Bankautomat"), BorderLayout.NORTH); - var layout = new GridBagLayout(); var constraints = new GridBagConstraints(); - content.setLayout(layout); constraints.gridwidth = 4; constraints.insets = new Insets(12,12,12,12); addInputRow(constraints, content, blz, 1, "BLZ"); - addInputRow(constraints, content, iban, 2, "Kontonummer"); - addInputRow(constraints, content, pin, 3, "Passwort"); + addInputRow(constraints, content, iban, 2, "IBAN"); + addInputRow(constraints, content, pin, 3, "PIN"); constraints.gridy = 4; constraints.anchor = GridBagConstraints.PAGE_END; @@ -44,6 +42,14 @@ public class LoginView extends JPanel { content.add(login, constraints); } + private void createComponents() { + this.blz = new JFormattedTextField("MA2424"); + this.iban = new JFormattedTextField(getNumberFormat()); + this.iban.setText("4711"); + this.pin = new JPasswordField("1234"); + this.login = new JButton("Login"); + } + private NumberFormatter getNumberFormat() { var format = NumberFormat.getIntegerInstance(); format.setGroupingUsed(false); diff --git a/src/me/teridax/jcash/gui/takeoff/TakeoffDialog.java b/src/me/teridax/jcash/gui/takeoff/TakeoffDialog.java index 44e05c6..686d14f 100644 --- a/src/me/teridax/jcash/gui/takeoff/TakeoffDialog.java +++ b/src/me/teridax/jcash/gui/takeoff/TakeoffDialog.java @@ -5,7 +5,7 @@ import me.teridax.jcash.banking.Account; public class TakeoffDialog { public TakeoffDialog(Account account, Runnable onTakeoff) { - var view = new TakeoffView(account); + var view = new TakeoffView(); view.getTakeoff().addActionListener(e -> { account.takeoff(view.getAmount()); onTakeoff.run(); diff --git a/src/me/teridax/jcash/gui/takeoff/TakeoffView.java b/src/me/teridax/jcash/gui/takeoff/TakeoffView.java index 4eed846..33a9c20 100644 --- a/src/me/teridax/jcash/gui/takeoff/TakeoffView.java +++ b/src/me/teridax/jcash/gui/takeoff/TakeoffView.java @@ -1,7 +1,6 @@ package me.teridax.jcash.gui.takeoff; -import me.teridax.jcash.banking.Account; -import me.teridax.jcash.decode.StringUtils; +import me.teridax.jcash.decode.StringDecoder; import javax.swing.*; import java.awt.*; @@ -18,8 +17,8 @@ public class TakeoffView { private JButton takeoff; private JFormattedTextField value; - public TakeoffView(Account account) { - createComponents(account); + public TakeoffView() { + createComponents(); layoutComponents(); } @@ -81,12 +80,12 @@ public class TakeoffView { dialog.getContentPane().add(buttonPanel, c); } - private void createComponents(Account account) { + private void createComponents() { this.dialog = new JDialog(); this.cancel = new JButton("Cancel"); this.takeoff = new JButton("Takeoff"); - this.value = new JFormattedTextField(StringUtils.LOCAL_NUMBER_FORMAT); + this.value = new JFormattedTextField(StringDecoder.LOCAL_NUMBER_FORMAT); this.dialog.setContentPane(new JPanel(new GridBagLayout())); } diff --git a/src/me/teridax/jcash/gui/transfer/TransferData.java b/src/me/teridax/jcash/gui/transfer/TransferData.java new file mode 100644 index 0000000..fe2f827 --- /dev/null +++ b/src/me/teridax/jcash/gui/transfer/TransferData.java @@ -0,0 +1,39 @@ +package me.teridax.jcash.gui.transfer; + +import me.teridax.jcash.banking.BankingManagementSystem; +import me.teridax.jcash.decode.StringDecoder; + +public class TransferData { + + private final BankingManagementSystem bms; + + public TransferData(BankingManagementSystem bms) { + this.bms = bms; + } + + /** + * Transfers a certain amount of money to the specified account of the specified bank + * @param amount the amount to transfer + * @param blz the bank that manages the account + * @param ibanString the internal bank account number to transfer money to + * @throws IllegalArgumentException if the bank or the account do not exist + */ + public void transferValue(double amount, String blz, String ibanString) throws IllegalArgumentException { + var bank = bms.getBank(blz); + if (bank.isEmpty()) + throw new IllegalArgumentException("Bank not found: " + blz); + + var iban = 0; + try { + iban = StringDecoder.decodeUniqueIdentificationNumber(ibanString); + } catch (Exception ex) { + throw new IllegalArgumentException("IBAN has invalid format: " + ibanString); + } + + var account = bank.get().getAccount(iban); + if (account.isEmpty()) + throw new IllegalArgumentException("Account not found: " + iban); + + account.get().getPrimaryAccount().deposit(amount); + } +} diff --git a/src/me/teridax/jcash/gui/transfer/TransferDialog.java b/src/me/teridax/jcash/gui/transfer/TransferDialog.java index 9f3aa4d..5bdedec 100644 --- a/src/me/teridax/jcash/gui/transfer/TransferDialog.java +++ b/src/me/teridax/jcash/gui/transfer/TransferDialog.java @@ -1,15 +1,29 @@ package me.teridax.jcash.gui.transfer; import me.teridax.jcash.banking.Account; +import me.teridax.jcash.banking.BankingManagementSystem; + +import javax.swing.*; + +import static javax.swing.JOptionPane.ERROR_MESSAGE; +import static javax.swing.JOptionPane.showMessageDialog; public class TransferDialog { - public TransferDialog(Account account, Runnable onDeposit) { - var view = new TransferView(account); + public TransferDialog(Account account, BankingManagementSystem bms, Runnable onDeposit) { + var view = new TransferView(); + var data = new TransferData(bms); view.getTransfer().addActionListener(e -> { - account.takeoff(view.getAmount()); - onDeposit.run(); - view.dispose(); + try { + var amount = view.getAmount(); + + data.transferValue(amount, view.getBlz(), view.getIban()); + account.takeoff(amount); + onDeposit.run(); + view.dispose(); + } catch (IllegalArgumentException ex) { + showMessageDialog(null, "invalid account", "Could not transfer", ERROR_MESSAGE); + } }); view.getCancel().addActionListener(e -> view.dispose()); view.showDialog(); diff --git a/src/me/teridax/jcash/gui/transfer/TransferView.java b/src/me/teridax/jcash/gui/transfer/TransferView.java index 80b00ee..1a17bb9 100644 --- a/src/me/teridax/jcash/gui/transfer/TransferView.java +++ b/src/me/teridax/jcash/gui/transfer/TransferView.java @@ -1,7 +1,6 @@ package me.teridax.jcash.gui.transfer; -import me.teridax.jcash.banking.Account; -import me.teridax.jcash.decode.StringUtils; +import me.teridax.jcash.decode.StringDecoder; import javax.swing.*; import java.awt.*; @@ -20,8 +19,8 @@ public class TransferView { private JFormattedTextField blz; private JFormattedTextField value; - public TransferView(Account account) { - createComponents(account); + public TransferView() { + createComponents(); layoutComponents(); } @@ -105,12 +104,12 @@ public class TransferView { dialog.getContentPane().add(buttonPanel, c); } - private void createComponents(Account account) { + private void createComponents() { this.dialog = new JDialog(); this.cancel = new JButton("Cancel"); this.transfer = new JButton("Transfer"); - this.value = new JFormattedTextField(StringUtils.LOCAL_NUMBER_FORMAT); + this.value = new JFormattedTextField(StringDecoder.LOCAL_NUMBER_FORMAT); this.iban = new JFormattedTextField(); this.blz = new JFormattedTextField(); @@ -138,6 +137,14 @@ public class TransferView { return transfer; } + public String getIban() { + return iban.getText(); + } + + public String getBlz() { + return blz.getText(); + } + public void dispose() { this.dialog.dispose(); }