diff --git a/src/me/teridax/jcash/banking/Bank.java b/src/me/teridax/jcash/banking/Bank.java
index 9d24ff0..1330004 100644
--- a/src/me/teridax/jcash/banking/Bank.java
+++ b/src/me/teridax/jcash/banking/Bank.java
@@ -4,6 +4,7 @@ import me.teridax.jcash.Logging;
import me.teridax.jcash.banking.accounts.Account;
import me.teridax.jcash.banking.accounts.Owner;
import me.teridax.jcash.banking.management.Profile;
+import me.teridax.jcash.gui.Utils;
import java.util.*;
import java.util.regex.Pattern;
diff --git a/src/me/teridax/jcash/decode/StringDecoder.java b/src/me/teridax/jcash/decode/StringDecoder.java
index ba56388..dd53aeb 100644
--- a/src/me/teridax/jcash/decode/StringDecoder.java
+++ b/src/me/teridax/jcash/decode/StringDecoder.java
@@ -2,6 +2,7 @@ package me.teridax.jcash.decode;
import me.teridax.jcash.lang.Locales;
+import javax.swing.text.NumberFormatter;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Objects;
@@ -17,6 +18,17 @@ public class StringDecoder {
return NumberFormat.getInstance(Locales.getDefaultLocale());
}
+ public static NumberFormatter getNumberFormatter(double maxValue) {
+ var formatter = new NumberFormatter();
+ formatter.setValueClass(Double.class);
+ formatter.setMinimum(0d);
+ formatter.setMaximum(maxValue);
+ formatter.setAllowsInvalid(true);
+ formatter.setCommitsOnValidEdit(true);
+
+ return formatter;
+ }
+
/**
* Attempts to convert the given string into a double value representing a percentage.
* The output value will be in the range [0, 100]. Strings formatted without a percentage
diff --git a/src/me/teridax/jcash/gui/InvalidInputException.java b/src/me/teridax/jcash/gui/InvalidInputException.java
new file mode 100644
index 0000000..73fb872
--- /dev/null
+++ b/src/me/teridax/jcash/gui/InvalidInputException.java
@@ -0,0 +1,18 @@
+package me.teridax.jcash.gui;
+
+import java.text.ParseException;
+
+public class InvalidInputException extends IllegalStateException {
+
+ public InvalidInputException(String message, Exception cause) {
+ super(message, cause);
+ }
+
+ public InvalidInputException(String message) {
+ super(message);
+ }
+
+ public InvalidInputException(Exception cause) {
+ super(cause);
+ }
+}
diff --git a/src/me/teridax/jcash/gui/NumberDocumentFilter.java b/src/me/teridax/jcash/gui/NumberDocumentFilter.java
new file mode 100644
index 0000000..8a5188f
--- /dev/null
+++ b/src/me/teridax/jcash/gui/NumberDocumentFilter.java
@@ -0,0 +1,17 @@
+package me.teridax.jcash.gui;
+
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.DocumentFilter;
+import javax.swing.text.NumberFormatter;
+
+public class NumberDocumentFilter extends DocumentFilter {
+ @Override
+ public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException {
+ super.insertString(fb, offset, string, attr);
+ }
+
+ private String filterString(String text) {
+ return "";
+ }
+}
diff --git a/src/me/teridax/jcash/gui/Utils.java b/src/me/teridax/jcash/gui/Utils.java
index 45d1bc6..9e57fd9 100644
--- a/src/me/teridax/jcash/gui/Utils.java
+++ b/src/me/teridax/jcash/gui/Utils.java
@@ -1,12 +1,17 @@
package me.teridax.jcash.gui;
+import me.teridax.jcash.Main;
+import me.teridax.jcash.lang.Locales;
+
import javax.swing.*;
import java.awt.*;
+import java.text.Format;
+import java.text.NumberFormat;
public class Utils {
public static String addHeading(String title) {
- return String.format("
%s", title);
+ return String.format("%s
", title);
}
/**
@@ -58,4 +63,13 @@ public class Utils {
constraints.fill = GridBagConstraints.HORIZONTAL;
target.add(comp, constraints);
}
+
+ /**
+ * Opens an error message dialog. This function will block the calling thread until the error message
+ * disposed.
+ * @param message the message to show to the user
+ */
+ public static void error(String message) {
+ JOptionPane.showMessageDialog(Main.getInstance().getWindow(), message, "Error occurred", JOptionPane.ERROR_MESSAGE);
+ }
}
diff --git a/src/me/teridax/jcash/gui/account/AccountController.java b/src/me/teridax/jcash/gui/account/AccountController.java
index 7280bb1..330358e 100644
--- a/src/me/teridax/jcash/gui/account/AccountController.java
+++ b/src/me/teridax/jcash/gui/account/AccountController.java
@@ -4,9 +4,9 @@ import me.teridax.jcash.Logging;
import me.teridax.jcash.Main;
import me.teridax.jcash.banking.management.BankingManagementSystem;
import me.teridax.jcash.banking.management.Profile;
-import me.teridax.jcash.gui.deposit.DepositDialog;
-import me.teridax.jcash.gui.takeoff.TakeoffDialog;
-import me.teridax.jcash.gui.transfer.TransferDialog;
+import me.teridax.jcash.gui.deposit.DepositController;
+import me.teridax.jcash.gui.takeoff.TakeoffController;
+import me.teridax.jcash.gui.transfer.TransferController;
/**
* Controller for controlling the gui of an account.
@@ -31,14 +31,25 @@ public class AccountController {
private void createListeners(Profile profile) {
this.view.getAccountSelection().addActionListener(e -> changeAccount());
-
this.view.getLogout().addActionListener(e -> logout());
+ this.view.getDeposit().addActionListener(e -> depositMoney());
+ this.view.getTakeoff().addActionListener(e -> takeoffMoney());
+ this.view.getTransfer().addActionListener(e -> transferMoney());
+ }
- this.view.getDeposit().addActionListener(e -> new DepositDialog(profile.getPrimaryAccount(), () -> this.view.setProfile(profile)));
+ private void depositMoney() {
+ new DepositController(profile.getPrimaryAccount());
+ this.view.updateAccountVariables(profile);
+ }
- this.view.getTakeoff().addActionListener(e -> new TakeoffDialog(profile.getPrimaryAccount(), () -> this.view.setProfile(profile)));
+ private void transferMoney() {
+ new TransferController(profile.getPrimaryAccount(), data.getBms());
+ this.view.updateAccountVariables(profile);
+ }
- this.view.getTransfer().addActionListener(e -> new TransferDialog(profile.getPrimaryAccount(), data.getBms(), () -> this.view.setProfile(profile)));
+ private void takeoffMoney() {
+ new TakeoffController(profile.getPrimaryAccount());
+ this.view.updateAccountVariables(profile);
}
private void logout() {
diff --git a/src/me/teridax/jcash/gui/account/AccountView.java b/src/me/teridax/jcash/gui/account/AccountView.java
index 009a81f..e314a0c 100644
--- a/src/me/teridax/jcash/gui/account/AccountView.java
+++ b/src/me/teridax/jcash/gui/account/AccountView.java
@@ -38,37 +38,14 @@ public class AccountView extends JPanel {
}
public void setProfile(Profile profile) {
- Logging.LOGGER.finer("Changing profile of account view");
- var bank = profile.getBank();
- var account = profile.getPrimaryAccount();
- var owner = profile.getOwner();
-
- this.blz.setText(bank.getBlz());
- this.bankName.setText(bank.getName());
-
- this.iban.setText(String.valueOf(account.getIban()));
- this.name.setText(owner.getName() + " " + owner.getFamilyName());
- this.address.setText(owner.getStreet() + " " + owner.getCity());
-
- this.balance.setText(StringDecoder.getNumberFormat().format(account.getBalance()) + " €");
-
- this.type.setText(translate(account.getClass().getSimpleName()));
- if (account instanceof CurrentAccount) {
- this.typeSpecialLabel.setText(translate("Overdraft"));
- this.typeSpecialProperty.setText(StringDecoder.getNumberFormat().format(((CurrentAccount) account).getOverdraft()) + " €");
- } else if (account instanceof SavingsAccount) {
- this.typeSpecialLabel.setText(translate("Interest rate"));
- this.typeSpecialProperty.setText(((SavingsAccount) account).getInterest() + " %");
- } else {
- Logging.LOGGER.severe("Type of new primary account cannot be determined: " + account.getClass().getName());
- }
+ this.updateAccountVariables(profile);
this.accountSelection.removeAllItems();
for (var otherAccount : profile.getAccounts()) {
this.accountSelection.addItem(otherAccount.getDescription());
}
- this.accountSelection.setSelectedItem(account.getDescription());
+ this.accountSelection.setSelectedItem(profile.getPrimaryAccount().getDescription());
}
private void createLayout() {
@@ -156,4 +133,31 @@ public class AccountView extends JPanel {
public JButton getTakeoff() {
return takeoff;
}
+
+ public void updateAccountVariables(Profile profile) {
+ Logging.LOGGER.finer("Updating account view");
+ var bank = profile.getBank();
+ var account = profile.getPrimaryAccount();
+ var owner = profile.getOwner();
+
+ this.blz.setText(bank.getBlz());
+ this.bankName.setText(bank.getName());
+
+ this.iban.setText(String.valueOf(account.getIban()));
+ this.name.setText(owner.getName() + " " + owner.getFamilyName());
+ this.address.setText(owner.getStreet() + " " + owner.getCity());
+
+ this.balance.setText(StringDecoder.getNumberFormat().format(account.getBalance()) + " €");
+
+ this.type.setText(translate(account.getClass().getSimpleName()));
+ if (account instanceof CurrentAccount) {
+ this.typeSpecialLabel.setText(translate("Overdraft"));
+ this.typeSpecialProperty.setText(StringDecoder.getNumberFormat().format(((CurrentAccount) account).getOverdraft()) + " €");
+ } else if (account instanceof SavingsAccount) {
+ this.typeSpecialLabel.setText(translate("Interest rate"));
+ this.typeSpecialProperty.setText(((SavingsAccount) account).getInterest() + " %");
+ } else {
+ Logging.LOGGER.severe("Type of new primary account cannot be determined: " + account.getClass().getName());
+ }
+ }
}
diff --git a/src/me/teridax/jcash/gui/deposit/DepositController.java b/src/me/teridax/jcash/gui/deposit/DepositController.java
new file mode 100644
index 0000000..55a574e
--- /dev/null
+++ b/src/me/teridax/jcash/gui/deposit/DepositController.java
@@ -0,0 +1,71 @@
+package me.teridax.jcash.gui.deposit;
+
+import me.teridax.jcash.Logging;
+import me.teridax.jcash.banking.accounts.Account;
+import me.teridax.jcash.gui.InvalidInputException;
+import me.teridax.jcash.gui.Utils;
+
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import java.text.ParseException;
+
+public class DepositController {
+
+ private final DepositView view;
+ private final Account account;
+ private final DepositData data;
+
+ public DepositController(Account account) {
+ this.account = account;
+
+ this.data = new DepositData(account.getBalance());
+ this.view = new DepositView(this.data.getMaxValue());
+
+ this.view.getDeposit().addActionListener(e -> depositMoney());
+ this.view.getCancel().addActionListener(e -> view.dispose());
+ this.view.getValue().getDocument().addDocumentListener(new DocumentListener() {
+ private void validateInputState() {
+ var balance = account.getBalance();
+ try {
+ view.getValue().commitEdit();
+ var amount = view.getAmount();
+ view.setCommittedValue(amount, balance + amount);
+ view.getDeposit().setEnabled(true);
+ } catch (InvalidInputException | ParseException ex) {
+ view.setCommittedValue(0, balance);
+ view.getDeposit().setEnabled(false);
+ }
+ }
+
+ @Override
+ public void insertUpdate(DocumentEvent documentEvent) {
+ validateInputState();
+ }
+
+ @Override
+ public void removeUpdate(DocumentEvent documentEvent) {
+ validateInputState();
+ }
+
+ @Override
+ public void changedUpdate(DocumentEvent documentEvent) {
+ validateInputState();
+ }
+ });
+ this.view.showDialog();
+ }
+
+ private void depositMoney() {
+ try {
+ var amount = view.getAmount();
+ Logging.LOGGER.fine("Depositing money of account: " + account.getIban() + " amount: " + amount);
+ account.deposit(amount);
+ } catch (IllegalArgumentException ex) {
+ Logging.LOGGER.severe("Cannot deposit money of account: " + account.getIban() + " because: " + ex.getMessage());
+ Utils.error(ex.getMessage());
+ } catch (InvalidInputException ex) {
+ Utils.error(ex.getMessage());
+ }
+ view.dispose();
+ }
+}
diff --git a/src/me/teridax/jcash/gui/deposit/DepositData.java b/src/me/teridax/jcash/gui/deposit/DepositData.java
new file mode 100644
index 0000000..71187fd
--- /dev/null
+++ b/src/me/teridax/jcash/gui/deposit/DepositData.java
@@ -0,0 +1,14 @@
+package me.teridax.jcash.gui.deposit;
+
+public class DepositData {
+
+ private double maxValue;
+
+ public DepositData(double maxValue) {
+ this.maxValue = maxValue;
+ }
+
+ public double getMaxValue() {
+ return maxValue;
+ }
+}
diff --git a/src/me/teridax/jcash/gui/deposit/DepositDialog.java b/src/me/teridax/jcash/gui/deposit/DepositDialog.java
deleted file mode 100644
index 352a967..0000000
--- a/src/me/teridax/jcash/gui/deposit/DepositDialog.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package me.teridax.jcash.gui.deposit;
-
-import me.teridax.jcash.Logging;
-import me.teridax.jcash.banking.accounts.Account;
-
-public class DepositDialog {
-
- private final DepositView view;
- private final Account account;
- private final Runnable onDeposit;
-
- public DepositDialog(Account account, Runnable onDeposit) {
- this.account = account;
- this.onDeposit = onDeposit;
-
- this.view = new DepositView();
-
- this.view.getDeposit().addActionListener(e -> depositMoney());
- this.view.getCancel().addActionListener(e -> view.dispose());
- this.view.showDialog();
- }
-
- private void depositMoney() {
- var amount = view.getAmount();
-
- Logging.LOGGER.fine("Depositing money of account: " + account.getIban() + " amount: " + amount);
-
- try {
- account.deposit(amount);
- onDeposit.run();
- } catch (IllegalArgumentException ex) {
- Logging.LOGGER.severe("Cannot deposit money of account: " + account.getIban() + " because: " + ex.getMessage());
- } finally {
- view.dispose();
- }
- }
-}
diff --git a/src/me/teridax/jcash/gui/deposit/DepositView.java b/src/me/teridax/jcash/gui/deposit/DepositView.java
index 82159d2..95fcf8a 100644
--- a/src/me/teridax/jcash/gui/deposit/DepositView.java
+++ b/src/me/teridax/jcash/gui/deposit/DepositView.java
@@ -3,10 +3,10 @@ package me.teridax.jcash.gui.deposit;
import me.teridax.jcash.Logging;
import me.teridax.jcash.decode.StringDecoder;
import me.teridax.jcash.gui.IconProvider;
+import me.teridax.jcash.gui.InvalidInputException;
import javax.swing.*;
import java.awt.*;
-import java.text.NumberFormat;
import java.text.ParseException;
import static me.teridax.jcash.lang.Translator.translate;
@@ -16,10 +16,12 @@ public class DepositView {
private JDialog dialog;
private JButton cancel;
private JButton deposit;
+ private JLabel enteredValue;
+ private JLabel balanceAfterDeposit;
private JFormattedTextField value;
- public DepositView() {
- createComponents();
+ public DepositView(double maxValue) {
+ createComponents(maxValue);
layoutComponents();
}
@@ -42,71 +44,97 @@ public class DepositView {
c.gridx = 0;
c.gridy = 0;
- c.weightx = 1;
- c.weighty = 1;
- c.gridwidth = 3;
- c.fill = GridBagConstraints.HORIZONTAL;
- c.anchor = GridBagConstraints.CENTER;
- c.insets = new Insets(4, 4, 4, 4);
- dialog.getContentPane().add(new JLabel(translate("Deposit money")), c);
-
- c.gridx = 0;
- c.gridy = 1;
c.gridwidth = 1;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.LAST_LINE_END;
c.weightx = 0;
+ c.insets = new Insets(6, 6, 6, 6);
dialog.getContentPane().add(new JLabel(translate("Value"), SwingConstants.RIGHT), c);
c.gridx = 1;
- c.gridy = 1;
+ c.gridy = 0;
c.fill = GridBagConstraints.HORIZONTAL;
c.anchor = GridBagConstraints.LAST_LINE_END;
c.weightx = 0.5;
dialog.getContentPane().add(value, c);
c.gridx = 2;
- c.gridy = 1;
+ c.gridy = 0;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.LINE_START;
c.weightx = 0;
dialog.getContentPane().add(new JLabel("€"), c);
+ c.gridx = 0;
+ c.gridy = 1;
+ c.gridwidth = 1;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.LAST_LINE_END;
+ dialog.getContentPane().add(new JLabel("Value to deposit:", SwingConstants.RIGHT), c);
+
+ c.gridx = 1;
+ c.gridy = 1;
+ c.gridwidth = 2;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.LAST_LINE_END;
+ dialog.getContentPane().add(enteredValue, c);
+
+ c.gridx = 0;
+ c.gridy = 2;
+ c.gridwidth = 1;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.LAST_LINE_END;
+ dialog.getContentPane().add(new JLabel("Balance after deposit:", SwingConstants.RIGHT), c);
+
+ c.gridx = 1;
+ c.gridy = 2;
+ c.gridwidth = 2;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.LAST_LINE_END;
+ dialog.getContentPane().add(balanceAfterDeposit, c);
+
var buttonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING));
buttonPanel.add(cancel);
buttonPanel.add(deposit);
c.gridx = 0;
- c.gridy = 2;
+ c.gridy = 3;
c.gridwidth = 3;
c.fill = GridBagConstraints.HORIZONTAL;
c.anchor = GridBagConstraints.LAST_LINE_END;
- c.insets = new Insets(10, 10, 10, 10);
dialog.getContentPane().add(buttonPanel, c);
}
- private void createComponents() {
+ private void createComponents(double maxValue) {
this.dialog = new JDialog();
this.cancel = new JButton(translate("Cancel"));
this.deposit = new JButton(translate("Deposit"));
- this.value = new JFormattedTextField(StringDecoder.getNumberFormat());
+ this.value = new JFormattedTextField(StringDecoder.getNumberFormatter(Double.MAX_VALUE));
+ this.enteredValue = new JLabel();
+ this.balanceAfterDeposit = new JLabel(StringDecoder.getNumberFormat().format(maxValue));
+
+ this.deposit.setEnabled(false);
this.dialog.setContentPane(new JPanel(new GridBagLayout()));
}
- public double getAmount() {
+ public double getAmount() throws InvalidInputException {
if (value.getText().isBlank())
- return 0;
+ throw new InvalidInputException("currency value is blank or has been invalid whilst entered");
try {
- return NumberFormat.getNumberInstance().parse(value.getText()).doubleValue();
+ return StringDecoder.getNumberFormat().parse(value.getText()).doubleValue();
} catch (ParseException e) {
Logging.LOGGER.severe("Amount text field contains invalid value: " + value);
- throw new RuntimeException(e);
+ throw new InvalidInputException(e);
}
}
+ public JFormattedTextField getValue() {
+ return value;
+ }
+
public JButton getCancel() {
return cancel;
}
@@ -118,4 +146,9 @@ public class DepositView {
public void dispose() {
this.dialog.dispose();
}
+
+ public void setCommittedValue(double amount, double after) {
+ enteredValue.setText(StringDecoder.getNumberFormat().format(amount));
+ balanceAfterDeposit.setText(StringDecoder.getNumberFormat().format(after));
+ }
}
diff --git a/src/me/teridax/jcash/gui/login/LoginController.java b/src/me/teridax/jcash/gui/login/LoginController.java
index bda542b..9a90a09 100644
--- a/src/me/teridax/jcash/gui/login/LoginController.java
+++ b/src/me/teridax/jcash/gui/login/LoginController.java
@@ -2,6 +2,7 @@ package me.teridax.jcash.gui.login;
import me.teridax.jcash.Logging;
import me.teridax.jcash.banking.management.BankingManagementSystem;
+import me.teridax.jcash.gui.Utils;
import java.awt.event.ActionEvent;
import java.util.Optional;
@@ -52,15 +53,13 @@ public class LoginController {
var blz = this.view.getBlz().getText();
var iban = this.getIban();
if (iban.isEmpty()) {
- Logging.LOGGER.severe("IBAN is invalid: " + iban);
- showMessageDialog(null, translate("Invalid IBAN"), translate("Faulty login attempt"), ERROR_MESSAGE);
+ Utils.error("no IBAN entered");
return;
}
var pin = this.getPin();
if (pin.isEmpty()) {
- Logging.LOGGER.severe("PIN is invalid: " + pin);
- showMessageDialog(null, translate("Invalid pin"), translate("Faulty login attempt"), ERROR_MESSAGE);
+ Utils.error("no PIN entered");
return;
}
@@ -68,9 +67,8 @@ public class LoginController {
if (account.isPresent()) {
this.listener.onAccountSelected(account.get());
} else {
- Logging.LOGGER.severe("invalid login credentials: " + iban + " / " + pin);
- showMessageDialog(null, translate("Invalid login credentials"), translate("Faulty login attempt"), ERROR_MESSAGE);
- }
+ Logging.LOGGER.warning("invalid login credentials: " + iban + " / " + pin);
+ }
}
public void addAccountSelectionListener(AccountSelectionListener listener) {
diff --git a/src/me/teridax/jcash/gui/login/LoginData.java b/src/me/teridax/jcash/gui/login/LoginData.java
index 3619372..3c937c8 100644
--- a/src/me/teridax/jcash/gui/login/LoginData.java
+++ b/src/me/teridax/jcash/gui/login/LoginData.java
@@ -3,6 +3,7 @@ package me.teridax.jcash.gui.login;
import me.teridax.jcash.Logging;
import me.teridax.jcash.banking.management.BankingManagementSystem;
import me.teridax.jcash.banking.management.Profile;
+import me.teridax.jcash.gui.Utils;
import java.util.Optional;
@@ -27,12 +28,26 @@ public class LoginData {
*/
public Optional authenticateAccount(String blz, int iban, int pin) {
Logging.LOGGER.info("Authenticating account " + iban);
+
var optionalBank = bms.getBank(blz);
- if (optionalBank.isEmpty())
+ if (optionalBank.isEmpty()) {
+ Utils.error("Unknown BLZ: " + blz);
return Optional.empty();
+ }
var profile = optionalBank.get().getAccount(iban);
- return profile.filter(value -> value.getPrimaryAccount().getPin() == pin);
+ if (profile.isEmpty()) {
+ Utils.error("Unknown account: " + iban);
+ return Optional.empty();
+ }
+
+ var account = profile.filter(value -> value.getPrimaryAccount().getPin() == pin);
+ if (account.isEmpty()) {
+ Utils.error("PIN does not match");
+ return Optional.empty();
+ }
+
+ return account;
}
public BankingManagementSystem getBms() {
diff --git a/src/me/teridax/jcash/gui/takeoff/TakeoffController.java b/src/me/teridax/jcash/gui/takeoff/TakeoffController.java
new file mode 100644
index 0000000..71a465a
--- /dev/null
+++ b/src/me/teridax/jcash/gui/takeoff/TakeoffController.java
@@ -0,0 +1,67 @@
+package me.teridax.jcash.gui.takeoff;
+
+import me.teridax.jcash.Logging;
+import me.teridax.jcash.banking.accounts.Account;
+import me.teridax.jcash.gui.InvalidInputException;
+import me.teridax.jcash.gui.Utils;
+
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import java.text.ParseException;
+
+public class TakeoffController {
+
+ private final Account account;
+ private final TakeoffView view;
+ private final TakeoffData data;
+
+ public TakeoffController(Account account) {
+ this.account = account;
+
+ this.data = new TakeoffData(account.getBalance());
+ this.view = new TakeoffView(this.data.getMaxValue());
+
+ this.view.getTakeoff().addActionListener(e -> takeOff());
+ this.view.getCancel().addActionListener(e -> view.dispose());
+ this.view.getValue().getDocument().addDocumentListener(new DocumentListener() {
+ private void validateInputState() {
+ var balance = account.getBalance();
+ try {
+ view.getValue().commitEdit();
+ var amount = view.getAmount();
+ view.setCommittedValue(amount, balance - amount);
+ view.getTakeoff().setEnabled(true);
+ } catch (InvalidInputException | ParseException ex) {
+ view.setCommittedValue(0, balance);
+ view.getTakeoff().setEnabled(false);
+ }
+ }
+
+ @Override
+ public void insertUpdate(DocumentEvent documentEvent) {
+ validateInputState();
+ }
+
+ @Override
+ public void removeUpdate(DocumentEvent documentEvent) {
+ validateInputState();
+ }
+
+ @Override
+ public void changedUpdate(DocumentEvent documentEvent) {
+ validateInputState();
+ }
+ });
+ this.view.showDialog();
+ }
+
+ private void takeOff() {
+ try {
+ account.takeoff(view.getAmount());
+ } catch (IllegalArgumentException | InvalidInputException ex) {
+ Logging.LOGGER.severe("Could not take off money: " + ex.getMessage());
+ Utils.error("Reason: " + ex.getMessage());
+ }
+ view.dispose();
+ }
+}
diff --git a/src/me/teridax/jcash/gui/takeoff/TakeoffData.java b/src/me/teridax/jcash/gui/takeoff/TakeoffData.java
new file mode 100644
index 0000000..d3c694b
--- /dev/null
+++ b/src/me/teridax/jcash/gui/takeoff/TakeoffData.java
@@ -0,0 +1,14 @@
+package me.teridax.jcash.gui.takeoff;
+
+public class TakeoffData {
+
+ private final double maxValue;
+
+ public TakeoffData(double maxValue) {
+ this.maxValue = maxValue;
+ }
+
+ public double getMaxValue() {
+ return maxValue;
+ }
+}
diff --git a/src/me/teridax/jcash/gui/takeoff/TakeoffDialog.java b/src/me/teridax/jcash/gui/takeoff/TakeoffDialog.java
deleted file mode 100644
index 18f338f..0000000
--- a/src/me/teridax/jcash/gui/takeoff/TakeoffDialog.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package me.teridax.jcash.gui.takeoff;
-
-import me.teridax.jcash.Logging;
-import me.teridax.jcash.Main;
-import me.teridax.jcash.banking.accounts.Account;
-
-import static javax.swing.JOptionPane.ERROR_MESSAGE;
-import static javax.swing.JOptionPane.showMessageDialog;
-import static me.teridax.jcash.lang.Translator.translate;
-
-public class TakeoffDialog {
-
- private final Account account;
- private final Runnable onTakeoff;
- private final TakeoffView view;
-
- public TakeoffDialog(Account account, Runnable onTakeoff) {
- this.account = account;
- this.onTakeoff = onTakeoff;
-
- this.view = new TakeoffView();
-
- this.view.getTakeoff().addActionListener(e -> takeOff());
- this.view.getCancel().addActionListener(e -> view.dispose());
- this.view.showDialog();
- }
-
- private void takeOff() {
- try {
- account.takeoff(view.getAmount());
- onTakeoff.run();
- view.dispose();
- } catch (IllegalArgumentException ex) {
- Logging.LOGGER.severe("Could not take off money: " + ex.getMessage());
- showMessageDialog(Main.getInstance().getWindow(), "Reason: " + ex.getMessage(), translate("Could not take off money"), ERROR_MESSAGE);
- }
- }
-}
diff --git a/src/me/teridax/jcash/gui/takeoff/TakeoffView.java b/src/me/teridax/jcash/gui/takeoff/TakeoffView.java
index 92db0d9..e977494 100644
--- a/src/me/teridax/jcash/gui/takeoff/TakeoffView.java
+++ b/src/me/teridax/jcash/gui/takeoff/TakeoffView.java
@@ -1,15 +1,14 @@
package me.teridax.jcash.gui.takeoff;
+import me.teridax.jcash.Logging;
import me.teridax.jcash.decode.StringDecoder;
import me.teridax.jcash.gui.IconProvider;
+import me.teridax.jcash.gui.InvalidInputException;
import javax.swing.*;
import java.awt.*;
-import java.text.NumberFormat;
import java.text.ParseException;
-import static javax.swing.JOptionPane.ERROR_MESSAGE;
-import static javax.swing.JOptionPane.showMessageDialog;
import static me.teridax.jcash.lang.Translator.translate;
public class TakeoffView {
@@ -17,10 +16,12 @@ public class TakeoffView {
private JDialog dialog;
private JButton cancel;
private JButton takeoff;
+ private JLabel enteredValue;
+ private JLabel balanceAfterDeposit;
private JFormattedTextField value;
- public TakeoffView() {
- createComponents();
+ public TakeoffView(double maxValue) {
+ createComponents(maxValue);
layoutComponents();
}
@@ -41,42 +42,61 @@ public class TakeoffView {
c.gridx = 0;
c.gridy = 0;
- c.weightx = 1;
- c.weighty = 1;
- c.gridwidth = 3;
- c.fill = GridBagConstraints.HORIZONTAL;
- c.anchor = GridBagConstraints.CENTER;
- c.insets = new Insets(4, 4, 4, 4);
- dialog.getContentPane().add(new JLabel(translate("Takeoff money")), c);
-
- c.gridx = 0;
- c.gridy = 1;
c.gridwidth = 1;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.LAST_LINE_END;
c.weightx = 0;
+ c.insets = new Insets(6,6,6,6);
dialog.getContentPane().add(new JLabel(translate("Value"), SwingConstants.RIGHT), c);
c.gridx = 1;
- c.gridy = 1;
+ c.gridy = 0;
c.fill = GridBagConstraints.HORIZONTAL;
c.anchor = GridBagConstraints.LAST_LINE_END;
c.weightx = 0.5;
dialog.getContentPane().add(value, c);
c.gridx = 2;
- c.gridy = 1;
+ c.gridy = 0;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.LINE_START;
c.weightx = 0;
dialog.getContentPane().add(new JLabel("€"), c);
+ c.gridx = 0;
+ c.gridy = 1;
+ c.gridwidth = 1;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.LAST_LINE_END;
+ dialog.getContentPane().add(new JLabel("Value to deposit:", SwingConstants.RIGHT), c);
+
+ c.gridx = 1;
+ c.gridy = 1;
+ c.gridwidth = 2;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.LAST_LINE_END;
+ dialog.getContentPane().add(enteredValue, c);
+
+ c.gridx = 0;
+ c.gridy = 2;
+ c.gridwidth = 1;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.LAST_LINE_END;
+ dialog.getContentPane().add(new JLabel("Balance after takeoff:", SwingConstants.RIGHT), c);
+
+ c.gridx = 1;
+ c.gridy = 2;
+ c.gridwidth = 2;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.LAST_LINE_END;
+ dialog.getContentPane().add(balanceAfterDeposit, c);
+
var buttonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING));
buttonPanel.add(cancel);
buttonPanel.add(takeoff);
c.gridx = 0;
- c.gridy = 2;
+ c.gridy = 3;
c.gridwidth = 3;
c.fill = GridBagConstraints.HORIZONTAL;
c.anchor = GridBagConstraints.LAST_LINE_END;
@@ -84,29 +104,35 @@ public class TakeoffView {
dialog.getContentPane().add(buttonPanel, c);
}
- private void createComponents() {
+ private void createComponents(double maxValue) {
this.dialog = new JDialog();
this.cancel = new JButton(translate("Cancel"));
this.takeoff = new JButton(translate("Takeoff"));
- this.value = new JFormattedTextField(StringDecoder.getNumberFormat());
+ this.value = new JFormattedTextField(StringDecoder.getNumberFormatter(maxValue));
+ this.enteredValue = new JLabel();
+ this.balanceAfterDeposit = new JLabel(StringDecoder.getNumberFormat().format(maxValue));
this.dialog.setContentPane(new JPanel(new GridBagLayout()));
}
- public double getAmount() {
- if (value.getText().isBlank()) {
- showMessageDialog(null, translate("Invalid amount"), translate("Currency must not be blank"), ERROR_MESSAGE);
- return 0;
- }
+ public double getAmount() throws InvalidInputException {
+ if (value.getText().isBlank())
+ throw new InvalidInputException("currency value is blank or has been invalid whilst entered");
try {
- return NumberFormat.getNumberInstance().parse(value.getText()).doubleValue();
+ return StringDecoder.getNumberFormat().parse(value.getText()).doubleValue();
} catch (ParseException e) {
- throw new RuntimeException(e);
+ Logging.LOGGER.severe("Amount text field contains invalid value: " + value);
+ throw new InvalidInputException(e);
}
}
+ public void setCommittedValue(double amount, double after) {
+ enteredValue.setText(StringDecoder.getNumberFormat().format(amount));
+ balanceAfterDeposit.setText(StringDecoder.getNumberFormat().format(after));
+ }
+
public JButton getCancel() {
return cancel;
}
@@ -115,6 +141,10 @@ public class TakeoffView {
return takeoff;
}
+ public JFormattedTextField getValue() {
+ return value;
+ }
+
public void dispose() {
this.dialog.dispose();
}
diff --git a/src/me/teridax/jcash/gui/transfer/TransferController.java b/src/me/teridax/jcash/gui/transfer/TransferController.java
new file mode 100644
index 0000000..9aaa3c1
--- /dev/null
+++ b/src/me/teridax/jcash/gui/transfer/TransferController.java
@@ -0,0 +1,72 @@
+package me.teridax.jcash.gui.transfer;
+
+import me.teridax.jcash.Logging;
+import me.teridax.jcash.Main;
+import me.teridax.jcash.banking.accounts.Account;
+import me.teridax.jcash.banking.management.BankingManagementSystem;
+import me.teridax.jcash.gui.InvalidInputException;
+import me.teridax.jcash.gui.Utils;
+
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import java.text.ParseException;
+
+/**
+ * Dialog class for transferring some value from one account to another
+ */
+public class TransferController {
+
+ private final Account account;
+ private final TransferData transferData;
+ private final TransferView view;
+
+ public TransferController(Account account, BankingManagementSystem bms) {
+ this.account = account;
+
+ this.view = new TransferView(account.getBalance());
+ this.transferData = new TransferData(bms);
+ this.view.getTransfer().addActionListener(e -> transfer());
+ this.view.getCancel().addActionListener(e -> view.dispose());this.view.getValue().getDocument().addDocumentListener(new DocumentListener() {
+ private void validateInputState() {
+ var balance = account.getBalance();
+ try {
+ view.getValue().commitEdit();
+ var amount = view.getAmount();
+ view.setCommittedValue(amount, balance - amount);
+ view.getTransfer().setEnabled(true);
+ } catch (InvalidInputException | ParseException ex) {
+ view.setCommittedValue(0, balance);
+ view.getTransfer().setEnabled(false);
+ }
+ }
+
+ @Override
+ public void insertUpdate(DocumentEvent documentEvent) {
+ validateInputState();
+ }
+
+ @Override
+ public void removeUpdate(DocumentEvent documentEvent) {
+ validateInputState();
+ }
+
+ @Override
+ public void changedUpdate(DocumentEvent documentEvent) {
+ validateInputState();
+ }
+ });
+ this.view.showDialog();
+ }
+
+ private void transfer() {
+ try {
+ var amount = view.getAmount();
+ this.account.takeoff(amount);
+ this.transferData.transferValue(amount, view.getBlz(), view.getIban());
+ } catch (IllegalArgumentException | InvalidInputException ex) {
+ Logging.LOGGER.severe("Could not transfer: " + ex.getMessage());
+ Utils.error("Reason: " + ex.getMessage());
+ }
+ this.view.dispose();
+ }
+}
diff --git a/src/me/teridax/jcash/gui/transfer/TransferDialog.java b/src/me/teridax/jcash/gui/transfer/TransferDialog.java
deleted file mode 100644
index 66333ce..0000000
--- a/src/me/teridax/jcash/gui/transfer/TransferDialog.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package me.teridax.jcash.gui.transfer;
-
-import me.teridax.jcash.Logging;
-import me.teridax.jcash.Main;
-import me.teridax.jcash.banking.accounts.Account;
-import me.teridax.jcash.banking.management.BankingManagementSystem;
-
-import static javax.swing.JOptionPane.ERROR_MESSAGE;
-import static javax.swing.JOptionPane.showMessageDialog;
-import static me.teridax.jcash.lang.Translator.translate;
-
-/**
- * Dialog class for transferring some value from one account to another
- */
-public class TransferDialog {
-
- private final Account account;
- private final Runnable onDeposit;
- private final TransferData transferData;
- private final TransferView transferView;
-
- public TransferDialog(Account account, BankingManagementSystem bms, Runnable onDeposit) {
- this.account = account;
- this.onDeposit = onDeposit;
-
- this.transferView = new TransferView();
- this.transferData = new TransferData(bms);
- this.transferView.getTransfer().addActionListener(e -> transfer());
- this.transferView.getCancel().addActionListener(e -> transferView.dispose());
- this.transferView.showDialog();
- }
-
- private void transfer() {
- try {
- var amount = transferView.getAmount();
-
- this.account.takeoff(amount);
- this.transferData.transferValue(amount, transferView.getBlz(), transferView.getIban());
- this.onDeposit.run();
- this.transferView.dispose();
- } catch (IllegalArgumentException ex) {
- Logging.LOGGER.severe("Could not transfer: " + ex.getMessage());
- showMessageDialog(Main.getInstance().getWindow(), translate("Invalid account"), translate("Could not transfer"), ERROR_MESSAGE);
- }
- }
-}
diff --git a/src/me/teridax/jcash/gui/transfer/TransferView.java b/src/me/teridax/jcash/gui/transfer/TransferView.java
index f2d5be1..11e6e27 100644
--- a/src/me/teridax/jcash/gui/transfer/TransferView.java
+++ b/src/me/teridax/jcash/gui/transfer/TransferView.java
@@ -3,12 +3,12 @@ package me.teridax.jcash.gui.transfer;
import me.teridax.jcash.Logging;
import me.teridax.jcash.decode.StringDecoder;
import me.teridax.jcash.gui.IconProvider;
+import me.teridax.jcash.gui.InvalidInputException;
import javax.swing.*;
import java.awt.*;
+import java.text.ParseException;
-import static javax.swing.JOptionPane.ERROR_MESSAGE;
-import static javax.swing.JOptionPane.showMessageDialog;
import static me.teridax.jcash.lang.Translator.translate;
public class TransferView {
@@ -20,8 +20,11 @@ public class TransferView {
private JFormattedTextField blz;
private JFormattedTextField value;
- public TransferView() {
- createComponents();
+ private JLabel balanceAfterTransfer;
+ private JLabel enteredValue;
+
+ public TransferView(double maxValue) {
+ createComponents(maxValue);
layoutComponents();
}
@@ -42,97 +45,123 @@ public class TransferView {
c.gridx = 0;
c.gridy = 0;
- c.weightx = 1;
- c.weighty = 1;
- c.gridwidth = 3;
- c.fill = GridBagConstraints.HORIZONTAL;
- c.anchor = GridBagConstraints.CENTER;
- c.insets = new Insets(4, 4, 4, 4);
- dialog.getContentPane().add(new JLabel(translate("Transfer money")), c);
-
- c.gridx = 0;
- c.gridy = 1;
c.gridwidth = 1;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.LAST_LINE_END;
c.weightx = 0;
+ c.insets = new Insets(6,6,6,6);
dialog.getContentPane().add(new JLabel(translate("BLZ"), SwingConstants.RIGHT), c);
c.gridx = 1;
- c.gridy = 1;
+ c.gridy = 0;
c.fill = GridBagConstraints.HORIZONTAL;
c.anchor = GridBagConstraints.LAST_LINE_END;
c.weightx = 0.5;
dialog.getContentPane().add(blz, c);
c.gridx = 2;
- c.gridy = 1;
+ c.gridy = 0;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.LAST_LINE_END;
c.weightx = 0;
dialog.getContentPane().add(new JLabel(translate("IBAN"), SwingConstants.RIGHT), c);
c.gridx = 3;
- c.gridy = 1;
+ c.gridy = 0;
c.fill = GridBagConstraints.HORIZONTAL;
c.anchor = GridBagConstraints.LAST_LINE_END;
c.weightx = 1;
dialog.getContentPane().add(iban, c);
c.gridx = 0;
- c.gridy = 2;
+ c.gridy = 1;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.LAST_LINE_END;
c.weightx = 0;
dialog.getContentPane().add(new JLabel(translate("Betrag"), SwingConstants.RIGHT), c);
c.gridx = 1;
- c.gridy = 2;
+ c.gridy = 1;
c.fill = GridBagConstraints.HORIZONTAL;
c.anchor = GridBagConstraints.LAST_LINE_END;
c.weightx = 0.5;
dialog.getContentPane().add(value, c);
+ c.gridx = 0;
+ c.gridy = 2;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.LAST_LINE_END;
+ c.weightx = 0.5;
+ dialog.getContentPane().add(new JLabel("entered value:", JLabel.RIGHT), c);
+
+ c.gridx = 1;
+ c.gridy = 2;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.LAST_LINE_END;
+ c.weightx = 0.5;
+ dialog.getContentPane().add(enteredValue, c);
+
+ c.gridx = 0;
+ c.gridy = 3;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.LAST_LINE_END;
+ c.weightx = 0.5;
+ dialog.getContentPane().add(new JLabel("Balance after transfer:", JLabel.RIGHT), c);
+
+ c.gridx = 1;
+ c.gridy = 3;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.LAST_LINE_END;
+ c.weightx = 0.5;
+ dialog.getContentPane().add(balanceAfterTransfer, c);
+
var buttonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING));
buttonPanel.add(cancel);
buttonPanel.add(transfer);
c.gridx = 0;
- c.gridy = 3;
+ c.gridy = 4;
c.gridwidth = 4;
c.fill = GridBagConstraints.HORIZONTAL;
c.anchor = GridBagConstraints.LAST_LINE_END;
- c.insets = new Insets(10, 10, 10, 10);
dialog.getContentPane().add(buttonPanel, c);
}
- private void createComponents() {
+ private void createComponents(double maxValue) {
this.dialog = new JDialog();
this.cancel = new JButton(translate("Cancel"));
this.transfer = new JButton(translate("Transfer"));
- this.value = new JFormattedTextField(StringDecoder.getNumberFormat());
+ this.value = new JFormattedTextField(StringDecoder.getNumberFormatter(maxValue));
this.iban = new JFormattedTextField();
this.blz = new JFormattedTextField();
+ this.enteredValue = new JLabel();
+ this.balanceAfterTransfer = new JLabel(StringDecoder.getNumberFormat().format(maxValue));
this.dialog.setContentPane(new JPanel(new GridBagLayout()));
}
- public double getAmount() {
- if (value.getText().isBlank()) {
- Logging.LOGGER.severe("Amount is empty");
- showMessageDialog(null, translate("invalid amount"), translate("currency must not be blank"), ERROR_MESSAGE);
- return 0;
- }
+ public double getAmount() throws InvalidInputException {
+ if (value.getText().isBlank())
+ throw new InvalidInputException("currency value is blank or has been invalid whilst entered");
try {
- return StringDecoder.decodeCurrency(value.getText());
- } catch (IllegalArgumentException e) {
- Logging.LOGGER.severe("Invalid amount: " + value.getText());
- throw new RuntimeException(e);
+ return StringDecoder.getNumberFormat().parse(value.getText()).doubleValue();
+ } catch (ParseException e) {
+ Logging.LOGGER.severe("Amount text field contains invalid value: " + value);
+ throw new InvalidInputException(e);
}
}
+ public void setCommittedValue(double amount, double after) {
+ enteredValue.setText(StringDecoder.getNumberFormat().format(amount));
+ balanceAfterTransfer.setText(StringDecoder.getNumberFormat().format(after));
+ }
+
+ public JFormattedTextField getValue() {
+ return value;
+ }
+
public JButton getCancel() {
return cancel;
}