From e91b096de5159dae071dc3d0847b75785da33da3 Mon Sep 17 00:00:00 2001
From: Dao <comnuoc@users.noreply.gitlab.utu.fi>
Date: Wed, 26 Jun 2024 00:57:26 +0300
Subject: [PATCH] feat(part2): Exercise 3.

---
 part_2/exercise_3/.gitignore                  | 30 ++++++
 part_2/exercise_3/exercise_3.iml              | 11 +++
 part_2/exercise_3/src/CommandLineApp.java     | 92 +++++++++++++++++++
 part_2/exercise_3/src/Loan.java               | 33 +++++++
 part_2/exercise_3/src/LoanCalculator.java     | 16 ++++
 part_2/exercise_3/src/Main.java               |  7 ++
 .../exercise_3/src/SimpleLoanCalculator.java  | 23 +++++
 7 files changed, 212 insertions(+)
 create mode 100644 part_2/exercise_3/.gitignore
 create mode 100644 part_2/exercise_3/exercise_3.iml
 create mode 100644 part_2/exercise_3/src/CommandLineApp.java
 create mode 100644 part_2/exercise_3/src/Loan.java
 create mode 100644 part_2/exercise_3/src/LoanCalculator.java
 create mode 100644 part_2/exercise_3/src/Main.java
 create mode 100644 part_2/exercise_3/src/SimpleLoanCalculator.java

diff --git a/part_2/exercise_3/.gitignore b/part_2/exercise_3/.gitignore
new file mode 100644
index 0000000..d578053
--- /dev/null
+++ b/part_2/exercise_3/.gitignore
@@ -0,0 +1,30 @@
+### IntelliJ IDEA ###
+/.idea/
+out/
+!**/src/main/**/out/
+!**/src/test/**/out/
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+bin/
+!**/src/main/**/bin/
+!**/src/test/**/bin/
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
\ No newline at end of file
diff --git a/part_2/exercise_3/exercise_3.iml b/part_2/exercise_3/exercise_3.iml
new file mode 100644
index 0000000..c90834f
--- /dev/null
+++ b/part_2/exercise_3/exercise_3.iml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>
\ No newline at end of file
diff --git a/part_2/exercise_3/src/CommandLineApp.java b/part_2/exercise_3/src/CommandLineApp.java
new file mode 100644
index 0000000..f1ec57b
--- /dev/null
+++ b/part_2/exercise_3/src/CommandLineApp.java
@@ -0,0 +1,92 @@
+import java.util.Scanner;
+
+public class CommandLineApp {
+    private final LoanCalculator loanCalculator;
+
+    /*
+     * @.pre: loanCalculator != null
+     * @.post: object is constructed.
+     */
+    public CommandLineApp(LoanCalculator loanCalculator) {
+        this.loanCalculator = loanCalculator;
+    }
+
+    /*
+     * Ask for information from the user, then calculate and display monthly installment.
+     *
+     * @.pre: true
+     * @.post: print the monthly installment.
+     */
+    public void run() {
+        while (true) {
+            try {
+                Loan loan = createLoanFromUserInput();
+                double monthlyInstallment = loanCalculator.calculateMonthlyInstallment(loan);
+                displayMonthlyInstallment(monthlyInstallment);
+
+                break;
+            } catch (LoanCalculationException e) {
+                System.out.println("Error: " + e.getMessage());
+            }
+        }
+    }
+
+    private Loan createLoanFromUserInput() {
+        Loan loan;
+
+        Scanner reader = new Scanner(System.in);
+
+        while (true) {
+            try {
+                double principleAmount = getInputPrincipleAmount(reader);
+                int loanTermInMonths = getInputLoanTerm(reader);
+
+                loan = new Loan(principleAmount, loanTermInMonths);
+
+                break;
+            } catch (ZeroOrNegativePrincipleAmountException|DisallowedLoanTermException e) {
+                System.out.println("Error: " + e.getMessage());
+            }
+        }
+
+        return loan;
+    }
+
+    private void displayMonthlyInstallment(double installment) {
+        System.out.println("Monthly installment: " + installment);
+    }
+
+    private double getInputPrincipleAmount(Scanner reader) {
+        double principleAmount;
+
+        while (true) {
+            try {
+                System.out.print("Principle amount: ");
+                principleAmount = Double.parseDouble(reader.nextLine());
+
+                break;
+            } catch (NumberFormatException e) {
+                System.out.println("Error: Please enter a number");
+            }
+        }
+
+        return principleAmount;
+    }
+
+    private int getInputLoanTerm(Scanner reader) {
+        int loanTermInMonths;
+
+        while (true) {
+            try {
+                System.out.print("Loan term (in months): ");
+                loanTermInMonths = Integer.parseInt(reader.nextLine());
+
+                break;
+            } catch (NumberFormatException e) {
+                System.out.println("Error: Please enter an integer number");
+            }
+        }
+
+        return loanTermInMonths;
+    }
+}
diff --git a/part_2/exercise_3/src/Loan.java b/part_2/exercise_3/src/Loan.java
new file mode 100644
index 0000000..f1d1eee
--- /dev/null
+++ b/part_2/exercise_3/src/Loan.java
@@ -0,0 +1,33 @@
+/*
+ * @.classInvariant:
+ *     principleAmount() > 0 && 0 <= loanTermInMonths() <= 300
+ */
+public record Loan(double principleAmount, int loanTermInMonths) {
+    /*
+     * @.pre: true
+     * @.post: Object is constructed
+     *      throw ZeroOrNegativePrincipleAmountException if principleAmount is less than or equal zero
+     *      throw DisallowedLoanTermException if loanTermInMonths < 0 || loanTermInMonths > 300
+     */
+    public Loan {
+        if (principleAmount <= 0) {
+            throw new ZeroOrNegativePrincipleAmountException("Principle amount should be greater than zero");
+        }
+
+        if (loanTermInMonths < 0 || loanTermInMonths > 300) {
+            throw new DisallowedLoanTermException("Loan term in months allowed range 0-300");
+        }
+    }
+}
+
+class ZeroOrNegativePrincipleAmountException extends RuntimeException {
+    public ZeroOrNegativePrincipleAmountException(String message) {
+        super(message);
+    }
+}
+
+class DisallowedLoanTermException extends RuntimeException {
+    public DisallowedLoanTermException(String message) {
+        super(message);
+    }
+}
\ No newline at end of file
diff --git a/part_2/exercise_3/src/LoanCalculator.java b/part_2/exercise_3/src/LoanCalculator.java
new file mode 100644
index 0000000..ed8ab95
--- /dev/null
+++ b/part_2/exercise_3/src/LoanCalculator.java
@@ -0,0 +1,16 @@
+public interface LoanCalculator {
+    /*
+     * Calculate monthly installment based on principle amount and loan term (in months).
+     *
+     * @.pre: loan != null
+     * @.post: RESULT >= 0.
+     *      throw LoanCalculationException if there is an error in calculation
+     */
+    double calculateMonthlyInstallment(Loan loan) throws LoanCalculationException;
+}
+
+class LoanCalculationException extends RuntimeException {
+    public LoanCalculationException(String message) {
+        super(message);
+    }
+}
\ No newline at end of file
diff --git a/part_2/exercise_3/src/Main.java b/part_2/exercise_3/src/Main.java
new file mode 100644
index 0000000..33a7f19
--- /dev/null
+++ b/part_2/exercise_3/src/Main.java
@@ -0,0 +1,7 @@
+public class Main {
+    public static void main(String[] args) {
+        LoanCalculator calculator = new SimpleLoanCalculator();
+        CommandLineApp app = new CommandLineApp(calculator);
+        app.run();
+    }
+}
\ No newline at end of file
diff --git a/part_2/exercise_3/src/SimpleLoanCalculator.java b/part_2/exercise_3/src/SimpleLoanCalculator.java
new file mode 100644
index 0000000..527c73c
--- /dev/null
+++ b/part_2/exercise_3/src/SimpleLoanCalculator.java
@@ -0,0 +1,23 @@
+public class SimpleLoanCalculator implements LoanCalculator {
+    /*
+     * Calculate monthly installment with simple formula
+     *
+     * @.pre: loan != null
+     * @.post: RESULT >= 0.
+     *      throw ZeroLoanTermException when loan term is equal to zero.
+     */
+    @Override
+    public double calculateMonthlyInstallment(Loan loan) throws LoanCalculationException {
+        if (0 == loan.loanTermInMonths()) {
+            throw new ZeroLoanTermException("Loan term should be greater than zero");
+        }
+
+        return loan.principleAmount() / loan.loanTermInMonths() + loan.principleAmount() / 240;
+    }
+}
+
+class ZeroLoanTermException extends LoanCalculationException {
+    public ZeroLoanTermException(String message) {
+        super(message);
+    }
+}
\ No newline at end of file
-- 
GitLab