diff --git a/src/Lecture4_interfaces_abstract_classes/BaseTransaction.java b/src/Lecture4_interfaces_abstract_classes/BaseTransaction.java index ed81eb8..3e32aa3 100644 --- a/src/Lecture4_interfaces_abstract_classes/BaseTransaction.java +++ b/src/Lecture4_interfaces_abstract_classes/BaseTransaction.java @@ -4,29 +4,27 @@ import java.util.Calendar; -public abstract class BaseTransaction implements TransactionInterface { - private final int amount; +public class BaseTransaction implements TransactionInterface { + private final double amount; private final Calendar date; private final String transactionID; /** - * Lecture1_adt.TransactionInterface Constructor - * @param amount in an integer + * Transaction Constructor + * @param amount the transaction amount * @param date: Not null, and must be a Calendar object - * @return void - * Instialises the field, attributes of a transaction - * Creates a object of this + * Instialises the fields of a transaction */ - public BaseTransaction(int amount, @NotNull Calendar date) { + public BaseTransaction(double amount, @NotNull Calendar date) { this.amount = amount; this.date = (Calendar) date.clone(); - int uniq = (int) Math.random()*10000; - transactionID = date.toString()+uniq; + int uniq = (int) (Math.random() * 10000); + transactionID = date.toString() + uniq; } /** * getAmount() - * @return integer + * @return double */ public double getAmount() { return amount; // Because we are dealing with Value types we need not worry about what we return @@ -37,7 +35,6 @@ public double getAmount() { * @return Calendar Object */ public Calendar getDate() { -// return date; // Because we are dealing with Reference types we need to judiciously copy what our getters return return (Calendar) date.clone(); // Defensive copying or Judicious Copying } @@ -45,7 +42,21 @@ public Calendar getDate() { public String getTransactionID(){ return transactionID; } - // Method to print a transaction receipt or details - public abstract void printTransactionDetails(); - public abstract void apply(BankAccount ba); + + // Method to print details of the transaction + @Override + public void printTransactionDetails() { + System.out.println("Transaction ID: " + transactionID); + System.out.println("Date: " + date.getTime()); + System.out.println("Amount: $" + amount); + } + + // Method to apply the transaction on a bank account + @Override + public void apply(BankAccount ba) throws InsufficientFundsException { + if (ba == null) { + throw new IllegalArgumentException("BankAccount cannot be null."); + } + System.out.println("BaseTransaction: No balance changes applied. Current balance verified: $" + ba.getBalance()); + } } diff --git a/src/Lecture4_interfaces_abstract_classes/DepositTrasaction.java b/src/Lecture4_interfaces_abstract_classes/DepositTrasaction.java index 81afab5..2b41401 100644 --- a/src/Lecture4_interfaces_abstract_classes/DepositTrasaction.java +++ b/src/Lecture4_interfaces_abstract_classes/DepositTrasaction.java @@ -5,26 +5,37 @@ import java.util.Calendar; public class DepositTrasaction extends BaseTransaction { - public DepositTrasaction(int amount, @NotNull Calendar date){ + public DepositTrasaction(double amount, @NotNull Calendar date){ super(amount, date); } - private boolean checkDepositAmount(int amt){ - if (amt < 0){ - return false; - } - else{ - return true; - } + + private boolean checkDepositAmount(double amt){ + return amt >= 0; } // Method to print a transaction receipt or details + @Override public void printTransactionDetails(){ - System.out.println("Deposit Trasaction: "+this.toString()); + System.out.println("--- Deposit Transaction Receipt ---"); + System.out.println("Transaction ID: " + getTransactionID()); + System.out.println("Date: " + getDate().getTime()); + System.out.println("Amount: $" + getAmount()); + System.out.println("Type: Deposit (Irreversible)"); + System.out.println("-----------------------------------"); } + @Override public void apply(BankAccount ba){ + if (ba == null) { + throw new IllegalArgumentException("BankAccount cannot be null."); + } + if (!checkDepositAmount(getAmount())) { + System.out.println("Error: Deposit amount cannot be negative."); + return; + } double curr_balance = ba.getBalance(); double new_balance = curr_balance + getAmount(); ba.setBalance(new_balance); + System.out.println("Deposit applied: Credited $" + getAmount() + ". New Balance: $" + new_balance); } } diff --git a/src/Lecture4_interfaces_abstract_classes/InsufficientFundsException.java b/src/Lecture4_interfaces_abstract_classes/InsufficientFundsException.java new file mode 100644 index 0000000..5d8bce6 --- /dev/null +++ b/src/Lecture4_interfaces_abstract_classes/InsufficientFundsException.java @@ -0,0 +1,23 @@ +package Lecture4_interfaces_abstract_classes; + +/** + * Exception thrown when a bank account does not have sufficient funds for a withdrawal. + */ +public class InsufficientFundsException extends Exception { + + /** + * Constructs a new InsufficientFundsException with a null detail message. + */ + public InsufficientFundsException() { + super(); + } + + /** + * Constructs a new InsufficientFundsException with the specified detail message. + * + * @param message the detail message + */ + public InsufficientFundsException(String message) { + super(message); + } +} diff --git a/src/Lecture4_interfaces_abstract_classes/TransactionInterface.java b/src/Lecture4_interfaces_abstract_classes/TransactionInterface.java index 5902713..8c245ac 100644 --- a/src/Lecture4_interfaces_abstract_classes/TransactionInterface.java +++ b/src/Lecture4_interfaces_abstract_classes/TransactionInterface.java @@ -16,6 +16,12 @@ public interface TransactionInterface { // Method to get a unique identifier for the transaction String getTransactionID(); + // Method to print details of the transaction + void printTransactionDetails(); + + // Method to apply this transaction on a Bank account object + void apply(BankAccount ba) throws InsufficientFundsException; + } diff --git a/src/Lecture4_interfaces_abstract_classes/WithdrawalTransaction.java b/src/Lecture4_interfaces_abstract_classes/WithdrawalTransaction.java index face5b6..1c9716f 100644 --- a/src/Lecture4_interfaces_abstract_classes/WithdrawalTransaction.java +++ b/src/Lecture4_interfaces_abstract_classes/WithdrawalTransaction.java @@ -5,41 +5,101 @@ import java.util.Calendar; public class WithdrawalTransaction extends BaseTransaction { - public WithdrawalTransaction(int amount, @NotNull Calendar date) { + private BankAccount associatedAccount; + private boolean isApplied = false; + private double actualWithdrawnAmount = 0.0; + private double remainingAmountNotWithdrawn = 0.0; + + public WithdrawalTransaction(double amount, @NotNull Calendar date) { super(amount, date); } - private boolean checkDepositAmount(int amt) { - if (amt < 0) { - return false; - } else { - return true; - } + private boolean checkWithdrawalAmount(double amt) { + return amt >= 0; } // Method to reverse the transaction public boolean reverse() { - return true; - } // return true if reversal was successful + if (isApplied && associatedAccount != null) { + double currentBalance = associatedAccount.getBalance(); + associatedAccount.setBalance(currentBalance + actualWithdrawnAmount); + System.out.println("Withdrawal reversed: Refunded $" + actualWithdrawnAmount + " to the account. New Balance: $" + associatedAccount.getBalance()); + this.isApplied = false; + this.associatedAccount = null; + this.actualWithdrawnAmount = 0.0; + this.remainingAmountNotWithdrawn = 0.0; + return true; + } + System.out.println("Reversal failed: Transaction has not been applied or is already reversed."); + return false; + } // Method to print a transaction receipt or details + @Override public void printTransactionDetails() { - System.out.println("Deposit Trasaction: " + this.toString()); + System.out.println("--- Withdrawal Transaction Receipt ---"); + System.out.println("Transaction ID: " + getTransactionID()); + System.out.println("Date: " + getDate().getTime()); + System.out.println("Amount: $" + getAmount()); + System.out.println("Actual Withdrawn Amount: $" + actualWithdrawnAmount); + System.out.println("Remaining Amount Not Withdrawn: $" + remainingAmountNotWithdrawn); + System.out.println("Applied: " + isApplied); + System.out.println("Type: Withdrawal (Reversible)"); + System.out.println("--------------------------------------"); } - /* - Oportunity for assignment: implementing different form of withdrawal - */ - public void apply(BankAccount ba) { + @Override + public void apply(BankAccount ba) throws InsufficientFundsException { + if (ba == null) { + throw new IllegalArgumentException("BankAccount cannot be null."); + } double curr_balance = ba.getBalance(); - if (curr_balance > getAmount()) { - double new_balance = curr_balance - getAmount(); - ba.setBalance(new_balance); + if (curr_balance < getAmount()) { + throw new InsufficientFundsException("Insufficient funds. Account balance: $" + curr_balance + ", Withdrawal amount: $" + getAmount()); } + double new_balance = curr_balance - getAmount(); + ba.setBalance(new_balance); + + this.associatedAccount = ba; + this.isApplied = true; + this.actualWithdrawnAmount = getAmount(); + this.remainingAmountNotWithdrawn = 0.0; + System.out.println("Withdrawal applied: Debited $" + getAmount() + ". New Balance: $" + new_balance); } - /* - Assignment 1 Q3: Write the Reverse method - a method unique to the WithdrawalTransaction Class - */ + // Overloaded apply method implementing partial withdrawal exception handling + public void apply(BankAccount ba, boolean allowPartial) { + if (ba == null) { + throw new IllegalArgumentException("BankAccount cannot be null."); + } + if (allowPartial) { + try { + this.apply(ba); + } catch (InsufficientFundsException e) { + double balance = ba.getBalance(); + if (balance > 0) { + double amountNotWithdrawn = getAmount() - balance; + this.remainingAmountNotWithdrawn = amountNotWithdrawn; + this.actualWithdrawnAmount = balance; + + ba.setBalance(0); + this.associatedAccount = ba; + this.isApplied = true; + System.out.println("Partial withdrawal applied. Withdrew available balance: $" + balance); + System.out.println("Amount not withdrawn (record kept): $" + amountNotWithdrawn); + } else { + System.out.println("Withdrawal failed. Balance is not greater than 0 (Balance: $" + balance + ")."); + } + } finally { + System.out.println("Partial withdrawal attempt completed."); + } + } else { + try { + this.apply(ba); + } catch (InsufficientFundsException e) { + System.out.println("Withdrawal failed due to insufficient funds: " + e.getMessage()); + } + } + } } diff --git a/src/Main.java b/src/Main.java index 584a048..1ea2730 100644 --- a/src/Main.java +++ b/src/Main.java @@ -1,4 +1,5 @@ import Lecture1_adt.*; // Import all classes from Lecture1_adt package to be used in this client code +import Lecture4_interfaces_abstract_classes.*; // Import all classes from Lecture4_interfaces_abstract_classes package import java.util.Calendar; import java.util.GregorianCalendar; @@ -144,7 +145,117 @@ public static void testTransaction4() { } + public static void testLecture4Transactions() { + System.out.println("================================================="); + System.out.println(" TESTING LECTURE 4 TRANSACTION SYSTEM "); + System.out.println("================================================="); + + // 1. Create a bank account with 1000.0 balance + BankAccount account = new BankAccount(1000.0); + System.out.println("Initial Bank Account Balance: $" + account.getBalance()); + + // 2. Test BaseTransaction (Concrete Class) + Calendar d1 = new GregorianCalendar(); + BaseTransaction baseTx = new BaseTransaction(200.0, d1); + System.out.println("\n--- 2.1 BaseTransaction Details ---"); + baseTx.printTransactionDetails(); + try { + System.out.println("Applying BaseTransaction:"); + baseTx.apply(account); + } catch (InsufficientFundsException e) { + System.out.println("Caught Exception: " + e.getMessage()); + } + System.out.println("Account Balance after BaseTransaction: $" + account.getBalance()); + + // 3. Test DepositTransaction + DepositTrasaction depositTx = new DepositTrasaction(500.0, d1); + System.out.println("\n--- 3.1 DepositTransaction Details ---"); + depositTx.printTransactionDetails(); + System.out.println("Applying DepositTransaction:"); + depositTx.apply(account); + System.out.println("Account Balance after DepositTransaction: $" + account.getBalance()); + + // 4. Test WithdrawalTransaction - Normal successful full withdrawal + WithdrawalTransaction withdrawTx1 = new WithdrawalTransaction(400.0, d1); + System.out.println("\n--- 4.1 WithdrawalTransaction Details (Full) ---"); + withdrawTx1.printTransactionDetails(); + try { + System.out.println("Applying WithdrawalTransaction (amount = 400.0):"); + withdrawTx1.apply(account); + } catch (InsufficientFundsException e) { + System.out.println("Caught InsufficientFundsException: " + e.getMessage()); + } + System.out.println("Account Balance after WithdrawalTransaction: $" + account.getBalance()); + withdrawTx1.printTransactionDetails(); + + // 5. Test Reversal of WithdrawalTransaction + System.out.println("\n--- 5.1 Reversal of Withdrawal ---"); + boolean isReversed = withdrawTx1.reverse(); + System.out.println("Reversal successful? " + isReversed); + System.out.println("Account Balance after Reversal: $" + account.getBalance()); + withdrawTx1.printTransactionDetails(); + + // 6. Test WithdrawalTransaction - Insufficient funds (throws Exception) + WithdrawalTransaction withdrawTx2 = new WithdrawalTransaction(2000.0, d1); + System.out.println("\n--- 6.1 Withdrawal exceeding balance (throws Exception) ---"); + try { + System.out.println("Applying WithdrawalTransaction (amount = 2000.0, Balance = 1500.0):"); + withdrawTx2.apply(account); + } catch (InsufficientFundsException e) { + System.out.println("Caught Expected Exception: " + e.getMessage()); + } + System.out.println("Account Balance after failed withdrawal: $" + account.getBalance()); + + // 7. Test Overloaded apply() method with partial withdrawal allowed + System.out.println("\n--- 7.1 Overloaded apply() - Partial Withdrawal Allowed (amount = 2000.0, Balance = 1500.0) ---"); + withdrawTx2.apply(account, true); + System.out.println("Account Balance after partial withdrawal: $" + account.getBalance()); + withdrawTx2.printTransactionDetails(); + + // 8. Test Reversal of Partial Withdrawal + System.out.println("\n--- 8.1 Reversal of Partial Withdrawal ---"); + boolean isPartialReversed = withdrawTx2.reverse(); + System.out.println("Reversal successful? " + isPartialReversed); + System.out.println("Account Balance after partial reversal: $" + account.getBalance()); + withdrawTx2.printTransactionDetails(); + + // 9. Test Polymorphic execution and Type Casting + System.out.println("\n--- 9.1 Polymorphic Execution via Superclass Reference ---"); + BaseTransaction polyDeposit = new DepositTrasaction(300.0, d1); + BaseTransaction polyWithdraw = new WithdrawalTransaction(100.0, d1); + + System.out.println("Polymorphic Deposit apply:"); + try { + polyDeposit.apply(account); + } catch (InsufficientFundsException e) { + System.out.println("Exception: " + e.getMessage()); + } + System.out.println("Balance after polymorphic deposit: $" + account.getBalance()); + + System.out.println("Polymorphic Withdrawal apply:"); + try { + polyWithdraw.apply(account); + } catch (InsufficientFundsException e) { + System.out.println("Exception: " + e.getMessage()); + } + System.out.println("Balance after polymorphic withdrawal: $" + account.getBalance()); + + // Test reversing the polymorphic withdrawal by downcasting + System.out.println("\nDowncasting polyWithdraw to WithdrawalTransaction for reversal:"); + if (polyWithdraw instanceof WithdrawalTransaction) { + WithdrawalTransaction downcastedWithdraw = (WithdrawalTransaction) polyWithdraw; + boolean polyReversed = downcastedWithdraw.reverse(); + System.out.println("Polymorphic withdrawal reversal successful? " + polyReversed); + System.out.println("Final Account Balance: $" + account.getBalance()); + } + + System.out.println("================================================="); + } + public static void main(String[] args) { + // Run testing for Lecture 4 Assignment + testLecture4Transactions(); + // This is the client code // Uncomment the following lines to test the class which you would like to test