Synthia
Generic and flexible data structure generator
Calculator.java
Go to the documentation of this file.
1 /*
2  Synthia, a data structure generator
3  Copyright (C) 2019-2021 Laboratoire d'informatique formelle
4  Université du Québec à Chicoutimi, Canada
5 
6  This program is free software: you can redistribute it and/or modify
7  it under the terms of the GNU Lesser General Public License as published
8  by the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU Lesser General Public License for more details.
15 
16  You should have received a copy of the GNU Lesser General Public License
17  along with this program. If not, see <http://www.gnu.org/licenses/>.
18  */
19 package examples.gui;
20 
21 import java.awt.BorderLayout;
22 import java.awt.Container;
23 import java.awt.GridLayout;
24 import java.awt.event.ActionEvent;
25 import java.awt.event.ActionListener;
26 import java.util.HashMap;
27 import java.util.Map;
28 
29 import javax.swing.JButton;
30 import javax.swing.JFrame;
31 import javax.swing.JPanel;
32 import javax.swing.JTextField;
33 
35 import ca.uqac.lif.synthia.test.Monkey;
36 
37 /**
38  * A simple calculator. The code for this window is an adaptation of the
39  * <a href="https://javacodex.com/Swing/Calculator">Java Codex</a>
40  * example.
41  * <p>
42  * <img src="./doc-files/gui/Calculator.png" alt="Calculator window" />
43  * <p>
44  * The original code on Java Codex is interesting since it contains a genuine
45  * fault that can be detected using the {@link Monkey}: clicking on
46  * <kbd>&#x2212;</kbd> followed by <kbd>=</kbd> throws a
47  * {@link NumberFormatException}. This error is not intentional and corresponds
48  * to a case that the source code does not properly take into account.
49  * For the purpose of the example, this fault can be disabled from the
50  * code by calling {@link #disableNumberFormatException()}.
51  * <p>
52  * The calculator has been modified so that other exceptions can be thrown:
53  * <ul>
54  * <li>Dividing by zero throws an {@link IllegalArgumentException}, which is
55  * expected</li>
56  * <li>Any result producing a value greater than 100,000 produces an
57  * {@link OverflowException}. The calculator can obviously manipulate larger
58  * values; this limitation is artificial. It is enabled by calling
59  * {@link #hasOverflow()}.</li>
60  * </ul>
61  */
62 public class Calculator extends JFrame implements Resettable
63 {
64  /**
65  * The array of button labels.
66  */
67  public static final String[] BUTTON_LABELS = {"7", "8", "9", "\u00f7",
68  "4", "5", "6", "\u00d7", "1", "2", "3", "\u2212", "0", ".", "=", "+"};
69 
70  /**
71  * Dummy UID.
72  */
73  private static final long serialVersionUID = 1L;
74 
75  /**
76  * A map associating each button to its label.
77  */
78  protected Map<String,JButton> m_buttons;
79 
80  /**
81  * The calculator panel contained within the frame.
82  */
84 
85  /**
86  * A flag specifying if the calculator checks the format of numbers.
87  */
88  protected boolean m_checkFormat;
89 
90  /**
91  * A flag specifying if the calculator produces an overflow exception.
92  */
93  protected boolean m_hasOverflow;
94 
95  /**
96  * Creates a new calculator window.
97  */
98  public Calculator()
99  {
100  super();
101  m_buttons = new HashMap<String,JButton>();
102  setTitle("Calculator");
103  setSize(200, 200);
104  setLocationRelativeTo(null);
105  setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
106  Container contentPane = getContentPane();
107  m_panel = new CalculatorPanel();
108  contentPane.add(m_panel);
109  m_checkFormat = false;
110  m_hasOverflow = false;
111  }
112 
113  /**
114  * Instructs the calculator to throw an {@link OverflowException} when
115  * producing a number over 100,000.
116  * @return This calculator
117  */
119  {
120  m_hasOverflow = true;
121  return this;
122  }
123 
124  /**
125  * Instructs the calculator to check the format of numbers and ignore
126  * parsing errors.
127  * @return
128  */
130  {
131  m_checkFormat = true;
132  return this;
133  }
134 
135 
136  /**
137  * Gets the button instance with given label.
138  * @param label The label
139  * @return The button instance
140  */
141  public JButton getButton(String label)
142  {
143  return m_buttons.get(label);
144  }
145 
146  @Override
147  public void reset()
148  {
149  m_panel.reset();
150  }
151 
152  /**
153  * A main method allowing the calculator to be run as a stand-alone
154  * application.
155  * @param args Command-line arguments
156  */
157  public static void main(String[] args)
158  {
159  Calculator c = new Calculator();
160  c.setVisible(true);
161  }
162 
163  /**
164  * The actual panel containing the interface of the calculator.
165  */
166  public class CalculatorPanel extends JPanel implements ActionListener, Resettable
167  {
168  /**
169  * Dummy UID.
170  */
171  private static final long serialVersionUID = 1L;
172 
173  private JTextField display = new JTextField("0");
174  private double result = 0;
175  private String operator = "=";
176  private boolean calculating = true;
177 
178  public CalculatorPanel()
179  {
180  setLayout(new BorderLayout());
181  display.setEditable(false);
182  add(display, "North");
183  JPanel panel = new JPanel();
184  panel.setLayout(new GridLayout(4, 4));
185  for (String label : BUTTON_LABELS)
186  {
187  JButton b = new JButton(label);
188  m_buttons.put(label, b);
189  panel.add(b);
190  b.addActionListener(this);
191  }
192  add(panel, "Center");
193  }
194 
195  @Override
196  public void actionPerformed(ActionEvent evt)
197  {
198  String cmd = evt.getActionCommand();
199  if ('0' <= cmd.charAt(0) && cmd.charAt(0) <= '9' || cmd.equals("."))
200  {
201  if (calculating)
202  {
203  display.setText(cmd);
204  }
205  else
206  {
207  display.setText(display.getText() + cmd);
208  }
209  calculating = false;
210  }
211  else
212  {
213  if (calculating)
214  {
215  if (cmd.equals("-"))
216  {
217  display.setText(cmd);
218  calculating = false;
219  }
220  else
221  {
222  operator = cmd;
223  }
224  }
225  else
226  {
227  double x = 0;
228  try
229  {
230  x = Double.parseDouble(display.getText());
231  }
232  catch (NumberFormatException e)
233  {
234  if (!m_checkFormat)
235  {
236  throw e;
237  }
238  }
239  calculate(x);
240  operator = cmd;
241  calculating = true;
242  }
243  }
244  }
245 
246  private void calculate(double n)
247  {
248  switch (operator)
249  {
250  case "+":
251  result += n;
252  break;
253  case "\u2212":
254  result -= n;
255  break;
256  case "\u00d7":
257  result *= n;
258  break;
259  case "\u00f7":
260  if (n == 0)
261  {
262  display.setText("Error");
263  throw new IllegalArgumentException("Division by zero");
264  }
265  result /= n;
266  break;
267  case "=":
268  result = n;
269  break;
270  }
271  if (m_hasOverflow && n > 100000)
272  {
273  // Artificial overflow
274  display.setText("OF");
275  throw new OverflowException();
276  }
277  display.setText("" + result);
278  }
279 
280  @Override
281  public void reset()
282  {
283  result = 0;
284  operator = "=";
285  calculating = true;
286  display.setText("0");
287  }
288  }
289 
290  /**
291  * Exception that is thrown when the calculator produces an overflow.
292  */
293  public static class OverflowException extends RuntimeException
294  {
295  /**
296  * Dummy UID.
297  */
298  private static final long serialVersionUID = 1L;
299 
300  /**
301  * Creates a new overflow exception.
302  */
303  public OverflowException()
304  {
305  super("Overflow");
306  }
307  }
308 
309  /**
310  * Exception that is thrown when the calculator produces an underflow.
311  */
312  public static class UnderflowException extends RuntimeException
313  {
314  /**
315  * Dummy UID.
316  */
317  private static final long serialVersionUID = 1L;
318 
319  /**
320  * Creates a new underflow exception.
321  */
322  public UnderflowException()
323  {
324  super("Underflow");
325  }
326  }
327 }
examples.gui.Calculator.reset
void reset()
Puts the object back into its initial state.
Definition: Calculator.java:147
examples.gui.Calculator.m_checkFormat
boolean m_checkFormat
A flag specifying if the calculator checks the format of numbers.
Definition: Calculator.java:88
ca.uqac.lif.synthia.Resettable
Signals that an object can be put back into its initial state.
Definition: Resettable.java:27
ca.uqac.lif.synthia.test.Monkey
Performs monkey testing by interacting with a component.
Definition: Monkey.java:54
examples.gui.Calculator.m_hasOverflow
boolean m_hasOverflow
A flag specifying if the calculator produces an overflow exception.
Definition: Calculator.java:93
examples.gui.Calculator.disableNumberFormatException
Calculator disableNumberFormatException()
Instructs the calculator to check the format of numbers and ignore parsing errors.
Definition: Calculator.java:129
examples.gui.Calculator.main
static void main(String[] args)
A main method allowing the calculator to be run as a stand-alone application.
Definition: Calculator.java:157
examples.gui.Calculator
A simple calculator.
Definition: Calculator.java:62
examples.gui.Calculator.Calculator
Calculator()
Creates a new calculator window.
Definition: Calculator.java:98
ca.uqac
ca.uqac.lif.synthia
Definition: Bounded.java:19
examples.gui.Calculator.CalculatorPanel
The actual panel containing the interface of the calculator.
Definition: Calculator.java:166
examples.gui.Calculator.CalculatorPanel.actionPerformed
void actionPerformed(ActionEvent evt)
Definition: Calculator.java:196
examples.gui.Calculator.CalculatorPanel.reset
void reset()
Puts the object back into its initial state.
Definition: Calculator.java:281
examples.gui.Calculator.m_buttons
Map< String, JButton > m_buttons
A map associating each button to its label.
Definition: Calculator.java:78
ca.uqac.lif
ca
examples.gui.Calculator.CalculatorPanel.CalculatorPanel
CalculatorPanel()
Definition: Calculator.java:178
examples.gui.Calculator.m_panel
CalculatorPanel m_panel
The calculator panel contained within the frame.
Definition: Calculator.java:83
ca.uqac.lif.synthia.test
Classes that enable Synthia to operate as a fuzz testing tool.
Definition: Action.java:19
examples.gui.Calculator.hasOverflow
Calculator hasOverflow()
Instructs the calculator to throw an OverflowException when producing a number over 100,...
Definition: Calculator.java:118
examples.gui.Calculator.getButton
JButton getButton(String label)
Gets the button instance with given label.
Definition: Calculator.java:141
examples.gui.Calculator.BUTTON_LABELS
static final String[] BUTTON_LABELS
The array of button labels.
Definition: Calculator.java:67