diff --git a/src/me/teridax/jcash/Logging.java b/src/me/teridax/jcash/Logging.java index ee1bfec..d5ab62d 100644 --- a/src/me/teridax/jcash/Logging.java +++ b/src/me/teridax/jcash/Logging.java @@ -21,6 +21,10 @@ public final class Logging { * Folder the log files are stored in, relative to the file containing the class */ private static final String LOG_FOLDER_NAME = "logs/"; + /** + * Format for the date time used for log files + */ + private static final String DATE_TIME_FORMAT = "yyyy-MM-dd_HH-mm-ss"; /** * Initialize the global system logger. @@ -35,17 +39,30 @@ public final class Logging { LOGGER.setLevel(level); } + /** + * Add a console log handler writing to the applications stderr + * @param level the level to set the handler to + */ private static void createConsoleLogger(Level level) { var ch = new ConsoleHandler(); ch.setLevel(level); LOGGER.addHandler(ch); } + /** + * Create a log handler writing to a file. + * The file will be located in a folder directly besides the application + * with the name LOG_FOLDER_NAME. The name will follow the format DATE_TIME_FORMAT plus "log" as + * file extension. + * @param level the log level to set the handler to + */ private static void createFileLogger(Level level) { + // setup log file name var now = LocalDateTime.now(); - var dateTime = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss").format(now); + var dateTime = DateTimeFormatter.ofPattern(DATE_TIME_FORMAT).format(now); var logFileName = LOG_FOLDER_NAME + dateTime + ".log"; + // setup the folder for the logs initializeLogFolder(); try { @@ -57,6 +74,10 @@ public final class Logging { } } + /** + * Checks if the folder containing log files is present. + * If the folder does not exist, the function will create a new folder. + */ private static void initializeLogFolder() { var folderPath = Path.of(LOG_FOLDER_NAME); diff --git a/src/me/teridax/jcash/Main.java b/src/me/teridax/jcash/Main.java index 937f592..5403e1a 100644 --- a/src/me/teridax/jcash/Main.java +++ b/src/me/teridax/jcash/Main.java @@ -47,6 +47,13 @@ public final class Main { getInstance().showLoginScreen(); } + /** + * Set the look and feel via the ui manager. + * This function will select a look and feel so that the application will + * look most like a native application. + * It will select the systems look and feel for Windows and MacOS + * and GTK for unix based systems. + */ private static void setPlatformDependingTheme() { // default look and feel var laf = UIManager.getCrossPlatformLookAndFeelClassName(); diff --git a/src/me/teridax/jcash/gui/Loader.java b/src/me/teridax/jcash/gui/Loader.java index 7d446b9..79321da 100644 --- a/src/me/teridax/jcash/gui/Loader.java +++ b/src/me/teridax/jcash/gui/Loader.java @@ -2,7 +2,6 @@ package me.teridax.jcash.gui; import me.teridax.jcash.Logging; import me.teridax.jcash.banking.management.BankingManagementSystem; -import me.teridax.jcash.lang.Translator; import javax.swing.*; import javax.swing.filechooser.FileNameExtensionFilter; diff --git a/src/me/teridax/jcash/gui/login/AccountSelectionListener.java b/src/me/teridax/jcash/gui/login/AccountSelectionListener.java index 080efe3..ffe8782 100644 --- a/src/me/teridax/jcash/gui/login/AccountSelectionListener.java +++ b/src/me/teridax/jcash/gui/login/AccountSelectionListener.java @@ -5,6 +5,7 @@ import me.teridax.jcash.banking.management.Profile; /** * Listens for changes in a selected account. */ +@FunctionalInterface public interface AccountSelectionListener { /** diff --git a/src/me/teridax/jcash/gui/login/LoginView.java b/src/me/teridax/jcash/gui/login/LoginView.java index a5e0c4e..5b790e3 100644 --- a/src/me/teridax/jcash/gui/login/LoginView.java +++ b/src/me/teridax/jcash/gui/login/LoginView.java @@ -1,14 +1,22 @@ package me.teridax.jcash.gui.login; import javax.swing.*; -import javax.swing.text.NumberFormatter; +import javax.swing.text.*; import java.awt.*; import java.text.NumberFormat; +import static java.awt.BorderLayout.NORTH; +import static javax.swing.SwingConstants.CENTER; import static me.teridax.jcash.lang.Translator.translate; public class LoginView extends JPanel { + /** + * Maximum number of decimal digits that can be stored lossless by a 32-bit signed integer. + * N = log10(2^32-1) = 9,632959861146281 + */ + private static final int MAX_PIN_DECIMAL_DIGITS = 9; + private JFormattedTextField blz; private JFormattedTextField iban; private JPasswordField pin; @@ -22,15 +30,15 @@ public class LoginView extends JPanel { private void layoutComponents() { var content = new JPanel(new GridBagLayout()); - this.setBorder(BorderFactory.createEmptyBorder(8,8,8,8)); + this.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); this.setLayout(new BorderLayout(16, 16)); this.add(new JScrollPane(content), BorderLayout.CENTER); - this.add(new JLabel(translate("Cashmachine")), BorderLayout.NORTH); + this.add(new JLabel("

" + translate("Cashmachine") + "", CENTER), NORTH); var constraints = new GridBagConstraints(); constraints.gridwidth = 4; - constraints.insets = new Insets(12,12,12,12); + constraints.insets = new Insets(12, 12, 12, 12); addInputRow(constraints, content, blz, 1, translate("BLZ")); addInputRow(constraints, content, iban, 2, translate("IBAN")); @@ -40,7 +48,7 @@ public class LoginView extends JPanel { constraints.anchor = GridBagConstraints.PAGE_END; constraints.weightx = 0; constraints.fill = GridBagConstraints.NONE; - constraints.insets = new Insets(12,12,12,12); + constraints.insets = new Insets(12, 12, 12, 12); content.add(login, constraints); } @@ -49,6 +57,26 @@ public class LoginView extends JPanel { this.iban = new JFormattedTextField(getNumberFormat()); this.pin = new JPasswordField(); this.login = new JButton(translate("Login")); + + restrictPasswordToDigits(); + } + + /** + * Adds a document filter onto {@link #pin} that filters out everything that is not a digit. + * The filter also restricts the amount of digits that can be entered to {@link #MAX_PIN_DECIMAL_DIGITS} + */ + private void restrictPasswordToDigits() { + ((AbstractDocument) this.pin.getDocument()).setDocumentFilter(new DocumentFilter() { + @Override + public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) + throws BadLocationException { + String newText = fb.getDocument().getText(0, fb.getDocument().getLength()) + text; + + if (newText.matches(String.format("\\d{1,%s}", MAX_PIN_DECIMAL_DIGITS))) { + super.replace(fb, offset, length, text, attrs); + } + } + }); } private NumberFormatter getNumberFormat() { diff --git a/src/me/teridax/jcash/gui/transfer/TransferData.java b/src/me/teridax/jcash/gui/transfer/TransferData.java index 6680683..77d9945 100644 --- a/src/me/teridax/jcash/gui/transfer/TransferData.java +++ b/src/me/teridax/jcash/gui/transfer/TransferData.java @@ -20,12 +20,14 @@ public class TransferData { * @throws IllegalArgumentException if the bank or the account do not exist */ public void transferValue(double amount, String blz, String ibanString) throws IllegalArgumentException { + // get bank to transfer to var bank = bms.getBank(blz); if (bank.isEmpty()) { Logging.LOGGER.warning("Bank not found: " + blz); throw new IllegalArgumentException("Bank not found: " + blz); } + // validate iban of target account var iban = 0; try { iban = StringDecoder.decodeUniqueIdentificationNumber(ibanString); @@ -34,6 +36,7 @@ public class TransferData { throw new IllegalArgumentException("IBAN has invalid format: " + ibanString); } + // get account to transfer value to var account = bank.get().getAccount(iban); if (account.isEmpty()) { Logging.LOGGER.warning("Account not found: " + iban); diff --git a/src/me/teridax/jcash/gui/transfer/TransferDialog.java b/src/me/teridax/jcash/gui/transfer/TransferDialog.java index e916490..751fd29 100644 --- a/src/me/teridax/jcash/gui/transfer/TransferDialog.java +++ b/src/me/teridax/jcash/gui/transfer/TransferDialog.java @@ -3,12 +3,14 @@ package me.teridax.jcash.gui.transfer; import me.teridax.jcash.Logging; import me.teridax.jcash.banking.accounts.Account; import me.teridax.jcash.banking.management.BankingManagementSystem; -import me.teridax.jcash.lang.Translator; 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; @@ -20,21 +22,21 @@ public class TransferDialog { this.account = account; this.onDeposit = onDeposit; - transferView = new TransferView(); - transferData = new TransferData(bms); - transferView.getTransfer().addActionListener(e -> transfer()); - transferView.getCancel().addActionListener(e -> transferView.dispose()); - transferView.showDialog(); + 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(); - account.takeoff(amount); - transferData.transferValue(amount, transferView.getBlz(), transferView.getIban()); - onDeposit.run(); - transferView.dispose(); + 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(null, translate("Invalid account"), translate("Could not transfer"), ERROR_MESSAGE);