From 2245b89aae1aed082b1cd5996d7e0e4a3c97ee2d Mon Sep 17 00:00:00 2001 From: bucky Date: Tue, 9 Jun 2026 13:27:49 +0300 Subject: [PATCH] implemntetion of assignment --- .../BaseTransaction.java | 69 ++- .../DepositTransaction.java | 58 +++ .../DepositTrasaction.java | 30 -- .../InsufficientFundsException.java | 16 + .../WithdrawalTransaction.java | 168 +++++++- src/Main.java | 406 +++++++++++------- 6 files changed, 518 insertions(+), 229 deletions(-) create mode 100644 src/Lecture4_interfaces_abstract_classes/DepositTransaction.java delete mode 100644 src/Lecture4_interfaces_abstract_classes/DepositTrasaction.java create mode 100644 src/Lecture4_interfaces_abstract_classes/InsufficientFundsException.java diff --git a/src/Lecture4_interfaces_abstract_classes/BaseTransaction.java b/src/Lecture4_interfaces_abstract_classes/BaseTransaction.java index ed81eb8..6c17d31 100644 --- a/src/Lecture4_interfaces_abstract_classes/BaseTransaction.java +++ b/src/Lecture4_interfaces_abstract_classes/BaseTransaction.java @@ -1,51 +1,78 @@ package Lecture4_interfaces_abstract_classes; import org.jetbrains.annotations.NotNull; - import java.util.Calendar; -public abstract class BaseTransaction implements TransactionInterface { +/** + * Concrete class BaseTransaction representing a general financial transaction. + * Implements the TransactionInterface. + */ +public class BaseTransaction implements TransactionInterface { private final int amount; private final Calendar date; private final String transactionID; /** - * Lecture1_adt.TransactionInterface Constructor - * @param amount in an integer - * @param date: Not null, and must be a Calendar object - * @return void - * Instialises the field, attributes of a transaction - * Creates a object of this + * BaseTransaction Constructor. + * @param amount the transaction amount as an integer. + * @param date: Not null, and must be a Calendar object. + * Initializes the fields of a transaction and generates a unique ID. */ public BaseTransaction(int amount, @NotNull Calendar date) { this.amount = amount; this.date = (Calendar) date.clone(); - int uniq = (int) Math.random()*10000; - transactionID = date.toString()+uniq; + // Correcting the precedence issue: (int) Math.random() is always 0. + // We cast to int after multiplying by 10000. + int uniq = (int) (Math.random() * 10000); + this.transactionID = date.toString() + uniq; } /** * getAmount() - * @return integer + * @return the transaction amount as a double. */ + @Override public double getAmount() { - return amount; // Because we are dealing with Value types we need not worry about what we return + return amount; // Value type, safe to return directly } /** * getDate() - * @return Calendar Object + * @return a defensive clone of the transaction date Calendar object. */ + @Override 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 + return (Calendar) date.clone(); // Defensive copying + } + + /** + * getTransactionID() + * @return the unique transaction identifier. + */ + @Override + public String getTransactionID() { + return transactionID; } - // Method to get a unique identifier for the transaction - public String getTransactionID(){ - return transactionID; + /** + * Prints the details of the transaction. + */ + public void printTransactionDetails() { + System.out.println("--- Generic Transaction Details ---"); + System.out.println("Transaction ID: " + getTransactionID()); + System.out.println("Date: " + getDate().getTime()); + System.out.println("Amount: $" + getAmount()); + System.out.println("------------------------------------"); + } + + /** + * Applies the transaction on the BankAccount object. + * Declares that it can throw InsufficientFundsException. + * This generic implementation differs substantially from Deposit/Withdrawal subclasses. + * @param ba the BankAccount to apply the transaction to. + * @throws InsufficientFundsException if balance is insufficient (not thrown by this base class). + */ + public void apply(BankAccount ba) throws InsufficientFundsException { + System.out.println("Applying BaseTransaction: Generic transaction applied. No balance changes made."); } - // Method to print a transaction receipt or details - public abstract void printTransactionDetails(); - public abstract void apply(BankAccount ba); } diff --git a/src/Lecture4_interfaces_abstract_classes/DepositTransaction.java b/src/Lecture4_interfaces_abstract_classes/DepositTransaction.java new file mode 100644 index 0000000..51fad0b --- /dev/null +++ b/src/Lecture4_interfaces_abstract_classes/DepositTransaction.java @@ -0,0 +1,58 @@ +package Lecture4_interfaces_abstract_classes; + +import org.jetbrains.annotations.NotNull; +import java.util.Calendar; + +/** + * Concrete class DepositTransaction representing a deposit transaction. + * Inherits from BaseTransaction. + */ +public class DepositTransaction extends BaseTransaction { + + /** + * DepositTransaction Constructor. + * @param amount the deposit amount. + * @param date the transaction date. + */ + public DepositTransaction(int amount, @NotNull Calendar date) { + super(amount, date); + } + + /** + * Helper method to validate deposit amount. + * @param amt the amount to check. + * @return true if amount is positive, false otherwise. + */ + private boolean checkDepositAmount(int amt) { + return amt >= 0; + } + + /** + * Prints the details of the deposit transaction. + */ + @Override + public void printTransactionDetails() { + System.out.println("--- Deposit Transaction Details ---"); + System.out.println("Transaction ID: " + getTransactionID()); + System.out.println("Date: " + getDate().getTime()); + System.out.println("Amount: $" + getAmount()); + System.out.println("------------------------------------"); + } + + /** + * Applies the deposit transaction by adding the amount to the bank account's balance. + * Overrides the apply method inherited from BaseTransaction. + * @param ba the BankAccount to deposit to. + */ + @Override + public void apply(BankAccount ba) { + if (!checkDepositAmount((int) getAmount())) { + System.out.println("Deposit failed: Invalid amount."); + return; + } + double curr_balance = ba.getBalance(); + double new_balance = curr_balance + getAmount(); + ba.setBalance(new_balance); + System.out.println("Deposited $" + getAmount() + " successfully. New balance: $" + new_balance); + } +} diff --git a/src/Lecture4_interfaces_abstract_classes/DepositTrasaction.java b/src/Lecture4_interfaces_abstract_classes/DepositTrasaction.java deleted file mode 100644 index 81afab5..0000000 --- a/src/Lecture4_interfaces_abstract_classes/DepositTrasaction.java +++ /dev/null @@ -1,30 +0,0 @@ -package Lecture4_interfaces_abstract_classes; - -import org.jetbrains.annotations.NotNull; - -import java.util.Calendar; - -public class DepositTrasaction extends BaseTransaction { - public DepositTrasaction(int amount, @NotNull Calendar date){ - super(amount, date); - } - private boolean checkDepositAmount(int amt){ - if (amt < 0){ - return false; - } - else{ - return true; - } - } - - // Method to print a transaction receipt or details - public void printTransactionDetails(){ - System.out.println("Deposit Trasaction: "+this.toString()); - } - - public void apply(BankAccount ba){ - double curr_balance = ba.getBalance(); - double new_balance = curr_balance + getAmount(); - ba.setBalance(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..b03a135 --- /dev/null +++ b/src/Lecture4_interfaces_abstract_classes/InsufficientFundsException.java @@ -0,0 +1,16 @@ +package Lecture4_interfaces_abstract_classes; + +/** + * Custom Exception class representing insufficient funds for withdrawal transactions. + * Inherits from the standard Java Exception class. + */ +public class InsufficientFundsException extends Exception { + + /** + * Constructor for InsufficientFundsException. + * @param message The detailed error message. + */ + public InsufficientFundsException(String message) { + super(message); + } +} diff --git a/src/Lecture4_interfaces_abstract_classes/WithdrawalTransaction.java b/src/Lecture4_interfaces_abstract_classes/WithdrawalTransaction.java index face5b6..99fc923 100644 --- a/src/Lecture4_interfaces_abstract_classes/WithdrawalTransaction.java +++ b/src/Lecture4_interfaces_abstract_classes/WithdrawalTransaction.java @@ -1,45 +1,169 @@ package Lecture4_interfaces_abstract_classes; import org.jetbrains.annotations.NotNull; - import java.util.Calendar; +/** + * Concrete class WithdrawalTransaction representing a withdrawal transaction. + * Inherits from BaseTransaction. + */ public class WithdrawalTransaction extends BaseTransaction { + // State-tracking fields for reversal and partial withdrawals + private BankAccount appliedAccount = null; + private double withdrawnAmount = 0.0; + private boolean isApplied = false; + private boolean isReversed = false; + private double amountNotWithdrawn = 0.0; + + /** + * WithdrawalTransaction Constructor. + * @param amount the withdrawal amount. + * @param date the transaction date. + */ public WithdrawalTransaction(int amount, @NotNull Calendar date) { super(amount, date); } + /** + * Helper method to validate deposit/withdrawal amount check. + * @param amt the amount to check. + * @return true if amount is positive, false otherwise. + */ private boolean checkDepositAmount(int amt) { - if (amt < 0) { - return false; - } else { - return true; - } + return amt >= 0; } - // Method to reverse the transaction - public boolean reverse() { - return true; - } // return true if reversal was successful - - // Method to print a transaction receipt or details + /** + * Prints the details of the withdrawal transaction. + */ + @Override public void printTransactionDetails() { - System.out.println("Deposit Trasaction: " + this.toString()); + System.out.println("--- Withdrawal Transaction Details ---"); + System.out.println("Transaction ID: " + getTransactionID()); + System.out.println("Date: " + getDate().getTime()); + System.out.println("Amount: $" + getAmount()); + System.out.println("Status: " + (isApplied ? (isReversed ? "Reversed" : "Applied") : "Not Applied")); + if (isApplied) { + System.out.println(" Actually Withdrawn: $" + withdrawnAmount); + } + if (amountNotWithdrawn > 0) { + System.out.println(" Amount Not Withdrawn (Unpaid): $" + amountNotWithdrawn); + } + System.out.println("---------------------------------------"); } - /* - Oportunity for assignment: implementing different form of withdrawal + /** + * Applies the withdrawal transaction on a BankAccount. + * Overrides the BaseTransaction apply method. + * @param ba the BankAccount to withdraw from. + * @throws InsufficientFundsException if the account balance is less than the withdrawal amount. */ - public void apply(BankAccount ba) { + @Override + public void apply(BankAccount ba) throws InsufficientFundsException { 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 is $" + curr_balance + ", but tried to withdraw $" + getAmount()); } + double new_balance = curr_balance - getAmount(); + ba.setBalance(new_balance); + + this.appliedAccount = ba; + this.withdrawnAmount = getAmount(); + this.isApplied = true; + this.isReversed = false; + this.amountNotWithdrawn = 0.0; + System.out.println("Withdrew $" + getAmount() + " successfully. New balance: $" + new_balance); } - /* - Assignment 1 Q3: Write the Reverse method - a method unique to the WithdrawalTransaction Class + /** + * Overloaded apply method that handles partial withdrawal. + * If partial is true, and the balance is insufficient but > 0, it withdraws all remaining balance. + * Uses try-catch-finally block to handle InsufficientFundsException. + * @param ba the BankAccount to withdraw from. + * @param partial true to allow partial withdrawal of remaining balance. */ -} + public void apply(BankAccount ba, boolean partial) { + if (partial) { + try { + // Attempt standard withdrawal + apply(ba); + } catch (InsufficientFundsException e) { + System.out.println("Standard withdrawal failed during partial attempt: " + e.getMessage()); + double currentBalance = ba.getBalance(); + // Check if 0 < balance < withdrawal amount + if (currentBalance > 0 && currentBalance < getAmount()) { + double originalBalance = currentBalance; + ba.setBalance(0.0); + + this.appliedAccount = ba; + this.withdrawnAmount = originalBalance; + this.isApplied = true; + this.isReversed = false; + this.amountNotWithdrawn = getAmount() - originalBalance; + System.out.println("Partial withdrawal applied. Withdrew remaining balance of $" + originalBalance + + ". Amount not withdrawn: $" + amountNotWithdrawn); + } else { + System.out.println("Partial withdrawal failed: Account balance is $" + currentBalance + + ". Cannot perform withdrawal of $" + getAmount()); + } + } finally { + System.out.println("Finished partial withdrawal execution. Current account balance: $" + ba.getBalance()); + } + } else { + try { + apply(ba); + } catch (InsufficientFundsException e) { + System.out.println("Standard withdrawal failed: " + e.getMessage()); + } finally { + System.out.println("Finished standard withdrawal execution. Current account balance: $" + ba.getBalance()); + } + } + } + + /** + * Reverses the withdrawal transaction. + * Restores the balance of the BankAccount it was applied to to its original amount. + * @return true if reversal was successful, false otherwise. + */ + public boolean reverse() { + if (!isApplied) { + System.out.println("Reversal failed: Transaction was not successfully applied."); + return false; + } + if (isReversed) { + System.out.println("Reversal failed: Transaction has already been reversed."); + return false; + } + if (appliedAccount == null) { + System.out.println("Reversal failed: No associated bank account found."); + return false; + } + double currentBalance = appliedAccount.getBalance(); + appliedAccount.setBalance(currentBalance + withdrawnAmount); + isReversed = true; + System.out.println("Withdrawal transaction reversed successfully. Restored $" + withdrawnAmount + " to the account."); + return true; + } + + // Getters for status monitoring + public BankAccount getAppliedAccount() { + return appliedAccount; + } + + public double getWithdrawnAmount() { + return withdrawnAmount; + } + + public boolean isApplied() { + return isApplied; + } + + public boolean isReversed() { + return isReversed; + } + + public double getAmountNotWithdrawn() { + return amountNotWithdrawn; + } +} diff --git a/src/Main.java b/src/Main.java index 584a048..7c1ef75 100644 --- a/src/Main.java +++ b/src/Main.java @@ -1,156 +1,250 @@ -import Lecture1_adt.*; // Import all classes from Lecture1_adt package to be used in this client code - -import java.util.Calendar; -import java.util.GregorianCalendar; -import java.util.ArrayList; -import java.util.List; - -//TIP To Run code, press or -// click the icon in the gutter. -/* -* Client Code for accessing the Lecture1_adt.TransactionInterface.java module - */ -public class Main { - - public static void testTransaction1() { - Calendar d1 = new GregorianCalendar(); // d1 is an Object [Objects are Reference types] - Lecture1_adt.Transaction1 t1 = new Lecture1_adt.Transaction1(1000, d1); // amount and d1 are arguments - - System.out.println(t1.toString()); - System.out.println("Lecture1_adt.TransactionInterface Amount: \t " + t1.amount); - System.out.println("Lecture1_adt.TransactionInterface Date: \t " + t1.date); - - // Please note that the Client Codes can access the data in the class directly through the dot operator - // This kind of exposure is a threat to both the Representation Independence and Preservation of Invariants - } - - - /** @return a transaction of same amount as t, one month later - * This is a PRODUCER of the class Lecture1_adt.Transaction2 - * This code will help demostrate the Design exposures still present in transaction2 class - * */ - - public static Transaction2 makeNextPayment(Transaction2 t) { - Calendar d = t.getDate(); - d.add(Calendar.MONTH, 1); - return new Transaction2(t.getAmount(), d); - } - - /* - Testing Transaction2 class - */ - public static void testTransaction2() { - - Calendar d1 = new GregorianCalendar(); - - Lecture1_adt.Transaction2 t = new Lecture1_adt.Transaction2(1000, d1); - - Lecture1_adt.Transaction2 modified_t = makeNextPayment(t); - - System.out.println("\n\nState of the Object T1 After Client Code Tried to Change the Amount"); - System.out.println("Lecture1_adt.TransactionInterface Amount: \t "+modified_t.getAmount()); - System.out.println("Lecture1_adt.TransactionInterface Date: \t "+modified_t.getDate().getTime()); - - System.out.println("\n\nHow does T2 Look Like?????"); - System.out.println("Lecture1_adt.TransactionInterface Amount: \t "+modified_t.getAmount()); - System.out.println("Lecture1_adt.TransactionInterface Date: \t "+modified_t.getDate().getTime()); - - /* Please note that Although we have solved the problem of Transaction1 - * And client code can no longer use the dot (.) operator to directly access the data - * There is still some exposure especially if we pass an object of a previous Transaction2 to create a new Transaction2 object - */ - - } - - - /** @return a list of 12 monthly payments of identical amounts - * This code will help demostrate the Design exposures still present in transaction3 class - * */ - public static List makeYearOfPayments (int amount) throws NullPointerException { - - List listOfTransaction3s = new ArrayList(); - Calendar date = new GregorianCalendar(2024, Calendar.JANUARY, 3); - - - for (int i = 0; i < 12; i++) { - listOfTransaction3s.add(new Transaction3(amount, date)); - date.add(Calendar.MONTH, 1); - } - return listOfTransaction3s; - } - - /* - Testing Transaction3 class - */ - public static void testTransaction3() { - - List allPaymentsIn2024 = makeYearOfPayments(1000); - - for (Transaction3 t3 : allPaymentsIn2024) { - - // Display all the 12 Transactions - for (Transaction3 transact : allPaymentsIn2024) { - System.out.println("\n\n ::::::::::::::::::::::::::::::::::::::::::::\n"); - System.out.println("Lecture1_adt.TransactionInterface Amount: \t "+transact.getAmount()); - System.out.println("Lecture1_adt.TransactionInterface Date: \t "+transact.getDate().getTime()); - } - } - - /* Please Check all the 12 transactions displayed and hwo their dates look like - * Note that Although Transaction3 class resolves to an extent the exposure in Transaction2 class - * There is still some exposure especially if we pass an object of a previous Transaction3 to create a - * new Transaction3 object - */ - } - - - /** @return a list of 12 monthly payments of identical amounts - * This code Show that by judicious copying and defensive programming we eliminate the exposure in Transaction3 - * As defined in the constructor of Transaction4 class - * */ - - public static List makeYearOfPaymentsFinal (int amount) throws NullPointerException { - - List listOfTransaction4s = new ArrayList(); - Calendar date = new GregorianCalendar(2024, Calendar.JANUARY, 3); - - - for (int i = 0; i < 12; i++) { - listOfTransaction4s.add(new Transaction4(amount, date)); - date.add(Calendar.MONTH, 1); - } - return listOfTransaction4s; - } - - /* - Testing Transaction3 class - */ - public static void testTransaction4() { - - /* - * Call the function to make all the Twelve transaction in a year of our business - */ - - List transactionsIn2024 = makeYearOfPaymentsFinal(1200); - - // Display all the 12 Transactions - for (Transaction4 transact : transactionsIn2024) { - System.out.println("\n\n ::::::::::::::::::::::::::::::::::::::::::::\n"); - System.out.println("Lecture1_adt.TransactionInterface Amount: \t "+transact.getAmount()); - System.out.println("Lecture1_adt.TransactionInterface Date: \t "+transact.getDate().getTime()); - } - - // Please Take a look at all the 12 transaction now and compare with the outputs of the Transaction3 class - } - - - public static void main(String[] args) { - // This is the client code - // Uncomment the following lines to test the class which you would like to test - - // testTransaction1() - // testTransaction2() - // testTransaction3() - // testTransaction4() - } -} \ No newline at end of file +import Lecture1_adt.*; // Import all classes from Lecture1_adt package to be used in this client code +import Lecture4_interfaces_abstract_classes.*; + +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.ArrayList; +import java.util.List; + +//TIP To Run code, press or +// click the icon in the gutter. +/* +* Client Code for accessing the Lecture1_adt.TransactionInterface.java module + */ +public class Main { + + public static void testTransaction1() { + Calendar d1 = new GregorianCalendar(); // d1 is an Object [Objects are Reference types] + Lecture1_adt.Transaction1 t1 = new Lecture1_adt.Transaction1(1000, d1); // amount and d1 are arguments + + System.out.println(t1.toString()); + System.out.println("Lecture1_adt.TransactionInterface Amount: \t " + t1.amount); + System.out.println("Lecture1_adt.TransactionInterface Date: \t " + t1.date); + + // Please note that the Client Codes can access the data in the class directly through the dot operator + // This kind of exposure is a threat to both the Representation Independence and Preservation of Invariants + } + + + /** @return a transaction of same amount as t, one month later + * This is a PRODUCER of the class Lecture1_adt.Transaction2 + * This code will help demostrate the Design exposures still present in transaction2 class + * */ + + public static Transaction2 makeNextPayment(Transaction2 t) { + Calendar d = t.getDate(); + d.add(Calendar.MONTH, 1); + return new Transaction2(t.getAmount(), d); + } + + /* + Testing Transaction2 class + */ + public static void testTransaction2() { + + Calendar d1 = new GregorianCalendar(); + + Lecture1_adt.Transaction2 t = new Lecture1_adt.Transaction2(1000, d1); + + Lecture1_adt.Transaction2 modified_t = makeNextPayment(t); + + System.out.println("\n\nState of the Object T1 After Client Code Tried to Change the Amount"); + System.out.println("Lecture1_adt.TransactionInterface Amount: \t "+modified_t.getAmount()); + System.out.println("Lecture1_adt.TransactionInterface Date: \t "+modified_t.getDate().getTime()); + + System.out.println("\n\nHow does T2 Look Like?????"); + System.out.println("Lecture1_adt.TransactionInterface Amount: \t "+modified_t.getAmount()); + System.out.println("Lecture1_adt.TransactionInterface Date: \t "+modified_t.getDate().getTime()); + + /* Please note that Although we have solved the problem of Transaction1 + * And client code can no longer use the dot (.) operator to directly access the data + * There is still some exposure especially if we pass an object of a previous Transaction2 to create a new Transaction2 object + */ + + } + + + /** @return a list of 12 monthly payments of identical amounts + * This code will help demostrate the Design exposures still present in transaction3 class + * */ + public static List makeYearOfPayments (int amount) throws NullPointerException { + + List listOfTransaction3s = new ArrayList(); + Calendar date = new GregorianCalendar(2024, Calendar.JANUARY, 3); + + + for (int i = 0; i < 12; i++) { + listOfTransaction3s.add(new Transaction3(amount, date)); + date.add(Calendar.MONTH, 1); + } + return listOfTransaction3s; + } + + /* + Testing Transaction3 class + */ + public static void testTransaction3() { + + List allPaymentsIn2024 = makeYearOfPayments(1000); + + for (Transaction3 t3 : allPaymentsIn2024) { + + // Display all the 12 Transactions + for (Transaction3 transact : allPaymentsIn2024) { + System.out.println("\n\n ::::::::::::::::::::::::::::::::::::::::::::\n"); + System.out.println("Lecture1_adt.TransactionInterface Amount: \t "+transact.getAmount()); + System.out.println("Lecture1_adt.TransactionInterface Date: \t "+transact.getDate().getTime()); + } + } + + /* Please Check all the 12 transactions displayed and hwo their dates look like + * Note that Although Transaction3 class resolves to an extent the exposure in Transaction2 class + * There is still some exposure especially if we pass an object of a previous Transaction3 to create a + * new Transaction3 object + */ + } + + + /** @return a list of 12 monthly payments of identical amounts + * This code Show that by judicious copying and defensive programming we eliminate the exposure in Transaction3 + * As defined in the constructor of Transaction4 class + * */ + + public static List makeYearOfPaymentsFinal (int amount) throws NullPointerException { + + List listOfTransaction4s = new ArrayList(); + Calendar date = new GregorianCalendar(2024, Calendar.JANUARY, 3); + + + for (int i = 0; i < 12; i++) { + listOfTransaction4s.add(new Transaction4(amount, date)); + date.add(Calendar.MONTH, 1); + } + return listOfTransaction4s; + } + + /* + Testing Transaction3 class + */ + public static void testTransaction4() { + + /* + * Call the function to make all the Twelve transaction in a year of our business + */ + + List transactionsIn2024 = makeYearOfPaymentsFinal(1200); + + // Display all the 12 Transactions + for (Transaction4 transact : transactionsIn2024) { + System.out.println("\n\n ::::::::::::::::::::::::::::::::::::::::::::\n"); + System.out.println("Lecture1_adt.TransactionInterface Amount: \t "+transact.getAmount()); + System.out.println("Lecture1_adt.TransactionInterface Date: \t "+transact.getDate().getTime()); + } + + // Please Take a look at all the 12 transaction now and compare with the outputs of the Transaction3 class + } + + + public static void testLecture4Transactions() { + System.out.println("\n=================================================="); + System.out.println(" TESTING LECTURE 4 TRANSACTIONS "); + System.out.println("=================================================="); + + // 1. Create a BankAccount with a starting balance + BankAccount account = new BankAccount(500.0); + System.out.println("Created BankAccount with initial balance: $" + account.getBalance()); + + // 2. Test DepositTransaction + Calendar d1 = new GregorianCalendar(2026, Calendar.JUNE, 9); + DepositTransaction deposit = new DepositTransaction(200, d1); + System.out.println("\nApplying Deposit of $200..."); + deposit.apply(account); + deposit.printTransactionDetails(); + + // 3. Test WithdrawalTransaction (Successful Standard Withdrawal) + Calendar d2 = new GregorianCalendar(2026, Calendar.JUNE, 9); + WithdrawalTransaction withdrawal1 = new WithdrawalTransaction(150, d2); + System.out.println("\nApplying Withdrawal of $150 (standard)..."); + try { + withdrawal1.apply(account); + } catch (InsufficientFundsException e) { + System.out.println("Caught Expected Exception: " + e.getMessage()); + } + withdrawal1.printTransactionDetails(); + + // 4. Test Reversal of WithdrawalTransaction + System.out.println("\nReversing the $150 Withdrawal..."); + withdrawal1.reverse(); + System.out.println("Balance after reversal: $" + account.getBalance()); + + // Try to reverse again to check idempotency/prevention of double reversal + System.out.println("Attempting to reverse the withdrawal again..."); + withdrawal1.reverse(); + + // 5. Test WithdrawalTransaction (Insufficient Funds - Exception Thrown) + WithdrawalTransaction withdrawalTooBig = new WithdrawalTransaction(1000, d2); + System.out.println("\nApplying Withdrawal of $1000 (exceeds balance)..."); + try { + withdrawalTooBig.apply(account); + } catch (InsufficientFundsException e) { + System.out.println("Caught Expected Exception for insufficient funds: " + e.getMessage()); + } + withdrawalTooBig.printTransactionDetails(); + + // 6. Test Overloaded apply() for partial withdrawal (when 0 < balance < withdrawal) + // Set balance to a value less than withdrawal amount but greater than 0 + account.setBalance(300.0); + System.out.println("\nSet account balance to: $" + account.getBalance()); + WithdrawalTransaction withdrawalPartial = new WithdrawalTransaction(500, d2); + System.out.println("Applying Withdrawal of $500 with partial option allowed..."); + withdrawalPartial.apply(account, true); + withdrawalPartial.printTransactionDetails(); + + // Reverse the partial withdrawal to verify restoring exact withdrawn amount + System.out.println("\nReversing the partial withdrawal..."); + withdrawalPartial.reverse(); + System.out.println("Balance after partial reversal: $" + account.getBalance()); + + // 7. Test polymorphism and early/late binding + System.out.println("\n--- Testing Polymorphism and Dynamic Binding ---"); + + // Map subclass objects to the BaseTransaction reference + BaseTransaction baseRef1 = deposit; + BaseTransaction baseRef2 = withdrawalTooBig; + + System.out.println("1) Casting DepositTransaction to BaseTransaction reference:"); + try { + baseRef1.apply(account); // Late binding calls DepositTransaction.apply() + } catch (InsufficientFundsException e) { + System.out.println("Error: " + e.getMessage()); + } + + System.out.println("\n2) Casting WithdrawalTransaction to BaseTransaction reference (throws exception):"); + try { + baseRef2.apply(account); // Late binding calls WithdrawalTransaction.apply() + } catch (InsufficientFundsException e) { + System.out.println("Caught exception via polymorphic BaseTransaction reference call: " + e.getMessage()); + } + + // Test calling BaseTransaction's own apply() + BaseTransaction baseTx = new BaseTransaction(100, d2); + System.out.println("\n3) Direct execution of BaseTransaction.apply():"); + try { + baseTx.apply(account); // Early binding calls BaseTransaction.apply() + } catch (InsufficientFundsException e) { + System.out.println("Error: " + e.getMessage()); + } + baseTx.printTransactionDetails(); + } + + public static void main(String[] args) { + // This is the client code + // Uncomment the following lines to test the class which you would like to test + + // testTransaction1() + // testTransaction2() + // testTransaction3() + // testTransaction4() + testLecture4Transactions(); + } +} \ No newline at end of file