View Javadoc
1   /**
2   
3   @file CalculatorApp.java
4   @brief This file serves as the main application file for the Calculator App.
5   @details This file contains the entry point of the application, which is the main method. It initializes the necessary components and executes the Calculator App.
6   */
7   /**
8   
9   @package com.ucoruh.calculator
10  @brief The com.ucoruh.calculator package contains all the classes and files related to the Calculator App.
11  */
12  package com.ucoruh.calculator;
13  
14  import org.slf4j.LoggerFactory;
15  
16  import ch.qos.logback.classic.Logger;
17  
18  /**
19   *
20   * @class CalculatorApp
21   * @brief This class represents the main application class for the Calculator
22   *        App.
23   * @details The CalculatorApp class provides the entry point for the Calculator
24   *          App. It parses three command-line arguments (operand, operator,
25   *          operand), computes the result with {@link Calculator} and prints
26   *          it. It never reads from standard input, so it is safe to run from
27   *          a non-interactive script and to unit test directly.
28   * @author ugur.coruh
29   */
30  public class CalculatorApp {
31    /**
32     * @brief Logger for the CalculatorApp class.
33     */
34    private static final Logger logger = (Logger) LoggerFactory.getLogger(CalculatorApp.class);
35  
36    /**
37     * @brief Usage message shown when the arguments do not describe a single
38     *        binary operation.
39     */
40    static final String USAGE = "Usage: CalculatorApp <number> <+|-|*|/> <number>";
41  
42    private CalculatorApp() {
43      // Utility/entry-point class: not meant to be instantiated.
44    }
45  
46    /**
47     * @brief The main entry point of the Calculator App.
48     *
49     * @details Delegates all the work to {@link #run(String[])} and prints its
50     *          result. Kept deliberately thin so that the parsing/calculation
51     *          logic in `run` can be unit tested without touching the console.
52     *
53     * @param args The command-line arguments passed to the application:
54     *             `<number> <operator> <number>`.
55     */
56    public static void main(String[] args) {
57      System.out.println(run(args));
58    }
59  
60    /**
61     * @brief Parses `args` as `<number> <operator> <number>` and computes the
62     *        result.
63     *
64     * @details This is the testable core of the application: it performs no
65     *          I/O (it neither reads from `System.in` nor writes to
66     *          `System.out`) and never blocks, so it can be called directly
67     *          from tests and from scripts alike. Recognized operators are
68     *          `+`, `-`, `*` and `/`. Invalid input (wrong argument count,
69     *          non-numeric operand, unknown operator, division by zero) is
70     *          reported as a descriptive `"Error: ..."` string instead of an
71     *          uncaught exception, so the process always exits cleanly with a
72     *          printable message.
73     *
74     * @param args The command-line arguments.
75     * @return A human-readable result or error message.
76     */
77    static String run(String[] args) {
78      if (args == null || args.length != 3) {
79        logger.warn("Expected 3 arguments, got {}", args == null ? 0 : args.length);
80        return USAGE;
81      }
82  
83      final int left;
84      final int right;
85  
86      try {
87        left = Integer.parseInt(args[0]);
88        right = Integer.parseInt(args[2]);
89      } catch (NumberFormatException e) {
90        logger.error("Invalid operand: {}", e.toString());
91        return "Error: operands must be integers. " + USAGE;
92      }
93  
94      String operator = args[1];
95      Calculator calculator = new Calculator();
96  
97      switch (operator) {
98        case "+":
99          return String.valueOf(calculator.add(left, right));
100 
101       case "-":
102         return String.valueOf(calculator.subtract(left, right));
103 
104       case "*":
105         return String.valueOf(calculator.multiply(left, right));
106 
107       case "/":
108         try {
109           return String.valueOf(calculator.divide(left, right));
110         } catch (ArithmeticException e) {
111           logger.error("Division by zero requested");
112           return "Error: division by zero";
113         }
114 
115       default:
116         logger.warn("Unknown operator: {}", operator);
117         return "Error: unknown operator '" + operator + "'. " + USAGE;
118     }
119   }
120 }