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 }