CIS 260 Assignment: Calculator II
The objective for this assignment is to be able to construct
a calculator using the GUI components created in the previous assignment
(with a couple of minor modifications). The calculator is designed to
allow some minor precedence issues to be take care of. Tests should be done
to make sure your calculator works properly before submitting it for grading.
StackItem class
This calculator will have some limited ability to deal with operator
precedence (such as doing multiplication and division before doing
addition and subtraction). To do this, we will use a limited stack-type
of data structure. Each item in the stack will be of type StackItem.
The requirements for StackItem are as follows:
- There should be three public variables:
- a double named number to store a
number from the display on the stack
- an int named operator to store an
operator from the display on the stack
- an int named precedence used to
keep track of the precedence of the operator being stored
- You will need a constructor which takes three arguments and uses them
to set (initialize) the three instance variables.
- [optional] A toString method would be useful for debugging purposes.
Calculator class instance variables
You should have the following instance variables (all private) for
use in your Calculator class:
- a Hashtable<String,Integer> named hash which
is used to translate from the String the display button returns
into an integer that is easier to use
- a Vector<StackItem> named stack which is
used to hold pending operations and results so the calculator can
deal with precedence issues - although this is named stack, it is used
in a manner in this program that is not entirely stack-like
- NumField display which is used to refer to the
display in your calculator
- a couple of integer constants which are used to keep track of
whether the calculator is expecting a new number (NEW_MODE), or if it is
expecting additional digits added to an existing number (GATHER_MODE):
- int GATHER_MODE should be set to some integer value
- int NEW_MODE should be set to an integer value different than GATHER_MODE
- an integer named state which is used to keep track
of whether the calculator is in NEW_MODE or GATHER_MODE
- fourteen integer constants which are used to identify
the current button being responded to:
- int NONE which is set to -1
- int DECIMAL_POINT which is set to 10
- int MULTIPLY which is set to 11
- int ADD which is set to 12
- int SUBTRACT which is set to 13
- int DIVIDE which is set to 14
- int CHANGE_SIGN which is set to 15
- int PERCENT which is set to 16
- int SQUARE_ROOT which is set to 17
- int EQUAL which is set to 18
- int BACKSPACE which is set to 19
- int CLEAR_ENTRY which is set to 20
- int CLEAR which is set to 21
- int RECIPROCAL which is set to 22
- Note: To save a little time and space, the numbers 0 through
9 will be assigned to the digits 0 through 9 on the keypad. If
we follow that rule throughout the program, we can dispense with
having constants here to represent the digits.
- a boolean named error which will keep track of
whether the calculator is in error mode (such as division by 0)
Calculator class methods
The Calculator class should extend the JFrame class. It should also
implement ActionListener. There are eight methods you need to
implement. They are as follows:
- public static void main(String[] args) - to get the calculator started
- public Calculator() - the constructor: creates GUI and inits instance variables
- private void reset() - used to clear the calculator back to initial conditions
- private void setDisplayValue(double n) - a convenience routine used to
set the calculator's display with a calculated result
- private void resolveStack(StackItem itm) - used to perform any pending
operations on the stack (needed for precedence to work)
- public void actionPerformed(ActionEvent e) - handles button clicks from
keypad (actually, it just passes the job off to handleButton()); this
method is required to be an ActionListener
- private void handleButton(int src) - this is where all the button clicks are handled;
this routine and resolveStack perform almost all of the logic to make the calculator
function properly
- private void initHashtable() - this routine just sets up the hashtable for
later use; it is used in the ActionListener to translate the keypad's
Strings into integers that identify which button was clicked
The main(String[] args) method
Create a new Calculator object.
The Calculator constructor
This constructor assembles the GUI and initializes the instance variables.
- call initHashtable()
- create a new Vector<StackItem> to use as a stack
- set the look-and-feel of the GUI if desired
- set the layout manager you want to use
- create a Keypad and add it to the Calculator
- create a display (NumField) and add it to the display
- set the Calculator to exit when the user chooses to close the JFrame
- add ourself as the ActionListener for the keypad
- call reset()
- set the size of the Calculator and make it visible
The reset() method
This method is pretty short and easy.
- set the state to NEW_MODE
- set the error indicator variable to false
- clear the stack
- use the display's clear() method to clear the display
The setDisplayValue(double n) method
This method is simple.
- use the display's setValue method to display the argument n
- set the state to NEW_MODE (the calculator is expecting a
new number to be entered)
The resolveStack(StackItem itm) method
This method handles pending operations. For example, if the user
enters "5 + 2 =", the "5 +" must be stored on
the stack so it can be used when the "=" is encountered
(the 2 will still be in the display). The Calculator will actually
push the "2 =" onto the stack as well and then resolve
the stack calculations. Here's what it should do:
- add the argument sent to the start of the stack
- while the stack has more than one item and the precedence of
the second item is greater than or equal to the precedence of
the first item, do the following:
- let val0 be the value of the first item in the stack
- let val1 be the value of the second item in the stack
- let operator be the operator stored in the second stack item
- if the operator is ADD, let val = val1 + val0
- if the operator is SUBTRACT, let val = val1 - val0
- if the operator is MULTIPLY, let val = val1 * val0
- if the operator is DIVIDE, check to see if val0 is 0; if it is
not 0, then let val = val1 / val0; if it is 0, then set
error to true, and send the message
""Can not divide by 0"" to the display using
its setError method
- if error is set to true, then clear the stack
- if error is set to false, then do the following:
- display val on the display using its setValue method
- get the first item from the stack and change its number to val
- if you removed the first item from the stack in the previous step,
then remove the new first item from the stack, otherwise remove
the first two items from the stack
- add the item you retrieved and changed to the beginning of the stack
- set the state to NEW_MODE
The actionPerformed(ActionEvent e) method
This method creates a thread to be run at Swing's convenience.
The thread just finds out what button was clicked, translates
that into an identifying integer, and then calls handleButton.
- first, store the value of argument e in a new constant local
variable named event
- use the SwingUtilities.invokeLater method to run a thread which
does the following:
- get the event's source [.getSource()]
- get the action command from the event source [.getActionCommand()]
- get the identifying number from the hash table for the given action command
[hash.get(String)]
- call handleButton(int), sending it the identifying number you just obtained
The handleButton(int src) method
This is the method that has to figure out what to do for each button click.
It is likely to be the longest method in your Calculator application. Here's
what you have to do:
- if error is true and src is not equal to CLEAR or CLEAR_ENTRY, then return
- if src is equal to CLEAR, then call reset()
- if src is equal to CLEAR_ENTRY, then set error to false and clear the display
- if src is equal to ADD or SUBTRACT, then call resolveStack with a new StackItem
where the value is the display's value, the operator is src, and the precedence
is 1
- if src is equal to MULTIPLY or DIVIDE, then call resolveStack with a new StackItem
where the value is the display's value, the operator is src, and the precedence
is 2
- if src is equal to EQUAL, then call resolveStack with a new StackItem
where the value is the display's value, the operator is src, and the precedence
is 0; then clear the stack
- if src is equal to CHANGE_SIGN, then use setDisplayValue to set the display's
value to: -1 * the current value in the display
- if src is equal to SQUARE_ROOT, then use setDisplayValue to set the display's
value to the square root of the current value in the display [you might
want to use Math.pow(value, .5) for this]
- if src is equal to PERCENT and state equals GATHER_MODE and the stack has
at least one element in it, then use setDisplayValue to set the display's
value to the number stored in the first stack item * the display's current value
divided by 100.0
- if src is equal to RECIPROCAL and the display's value is not 0, then use
setDisplayValue to set the display's value to 1.0 divided by the current
value shown in the display
- if src is equal to RECIPROCAL and the display's value is 0, then call reset(),
set error to true, and and send the message ""Can not divide by 0""
to the display using its setError method
- if src is equal to BACKSPACE and the state is GATHER_MODE and the text currently
shown in the display has at least one character in it, then use the display's
setText method to display the same text with the last character missing [you
probably want to use the String object's substring method for this]
- if src is equal to 0 through 10, then do the following:
- if state is NEW_MODE then clear the display
- if src is 10, then append a period character (.) to the display;
otherwise append the following character: (char)('0'+src)
- set the state to GATHER_MODE
The initHashtable() method
The purpose of this method is to create the hashtable and populate
it with all of the possible Strings returned from the keypad. Each
entry also associates a unique integer value used to identify the
String, and therefore the button that was clicked.
- create the hashtable as a new Hashtable<String,Integer>
- use the hashtable's put method to enter all ten digit strings,
where the String "0" is entered with the integer 0, etc.
- use the hashtable's put method to enter all the rest of the
possible Strings returned from the keypad and their associated
identification number [eg. hash.put("Backspace", BACKSPACE);]
- Keep in mind that the identifying numbers are stored as constants
which should be defined already as instance variables.