Calculator with stack
framework/.DS_Store
__MACOSX/framework/._.DS_Store
framework/application.css
/* JavaFX CSS - Leave this comment until you have at least create one rule which uses -fx-Property */ .vbox1 { -fx-background-color:rgb(20,64,144); -fx-padding: 20; -fx-font-size: 30; } .button{ -fx-background-color:rgb(174,184,219); } .btnEqual{ -fx-background-color:rgb(61,127,61); -fx-text-fill:white; } .btnClear{ -fx-background-color:rgb(155,56,54); -fx-text-fill:white; } .btnBackspace{ -fx-background-color:rgb(80,80,80); -fx-text-fill:white; } .vbox2 { -fx-background-color:rgb(207,59,62); -fx-padding: 20; -fx-font-size: 20; } .vbox3 { -fx-background-color:rgb(90,207,9); -fx-padding: 20; -fx-font-size: 20; } .style1 { -fx-background-color:rgb(217,144,185); } .style2 { -fx-text-fill:red; } .screen1 { -fx-background-color: #FFFFFF; -fx-min-width: 50; -fx-min-height: 50; } .screen2 { -fx-background-color: #FFFFFF; -fx-text-alignment:left ; }
__MACOSX/framework/._application.css
framework/Calculator.java
framework/Calculator.java
package
application
;
import
javafx
.
application
.
Application
;
import
javafx
.
beans
.
binding
.
Bindings
;
import
javafx
.
beans
.
property
.
DoubleProperty
;
import
javafx
.
beans
.
property
.
SimpleDoubleProperty
;
import
javafx
.
event
.
ActionEvent
;
import
javafx
.
event
.
EventHandler
;
import
javafx
.
geometry
.
Pos
;
import
javafx
.
scene
.
Scene
;
import
javafx
.
scene
.
control
.
Button
;
import
javafx
.
scene
.
control
.
TextField
;
import
javafx
.
scene
.
layout
.
TilePane
;
import
javafx
.
scene
.
layout
.
VBox
;
import
javafx
.
scene
.
text
.
Font
;
import
javafx
.
stage
.
Stage
;
import
javafx
.
stage
.
StageStyle
;
/* This is the GUI class that visualize the calculator
*
* */
public
class
Calculator
extends
Application
{
private
MyStack
stack
=
new
MyStack
();
private
double
font_size
=
30
;
//by default the font size on the screen is 30
/*The keyboard key values*/
private
static
final
String
[][]
key_values
=
{
{
"0"
,
"="
,
"c"
,
"<"
},
{
"1"
,
"2"
,
"3"
,
"-"
},
{
"4"
,
"5"
,
"6"
,
"*"
},
{
"7"
,
"8"
,
"9"
,
"+"
}
};
private
Button
btn
[][]
=
new
Button
[
4
][
4
];
//all the key buttons
TextField
calculator_screen
;
//the calculator screen
public
static
void
main
(
String
[]
args
)
{
launch
(
args
);
}
@
Override
public
void
start
(
Stage
stage
)
{
/*The outside layout*/
final
VBox
layout
=
new
VBox
(
30
);
//the size vertically
/*The inside layout for keys or buttons*/
TilePane
keypad
=
new
TilePane
();
//even it is called keypad, it is a layout
keypad
.
setVgap
(
7
);
keypad
.
setHgap
(
7
);
//set the gap between keys
/*Create Calculator Screen */
calculator_screen
=
new
TextField
();
calculator_screen
.
getStyleClass
().
add
(
"screen1"
);
//set the style of the screen
calculator_screen
.
setAlignment
(
Pos
.
CENTER_RIGHT
);
//make the screen in the center of the calculator
calculator_screen
.
setEditable
(
false
);
//make sure the screen cannot be typed in manually
calculator_screen
.
setPrefWidth
(
300
);
//set the windth of the screen
calculator_screen
.
setPrefHeight
(
30
);
calculator_screen
.
setFont
(
Font
.
font
(
"Verdana"
,
font_size
));
/*Create Calculator keyboard*/
keypad
.
setPrefColumns
(
key_values
[
0
].
length
);
//set the preferred number of columns
for
(
int
i
=
0
;
i
<
4
;
i
++
)
{
for
(
int
j
=
0
;
j
<
4
;
j
++
)
{
btn
[
i
][
j
]
=
new
Button
(
key_values
[
i
][
j
]);
final
int
a
=
i
;
final
int
b
=
j
;
/*Add button event*/
btn
[
i
][
j
].
setOnAction
(
new
EventHandler
<
ActionEvent
>
(){
@
Override
public
void
handle
(
ActionEvent
event
)
{
StackNode
node
=
new
StackNode
(
key_values
[
a
][
b
]);
if
(
a
==
0
&&
b
==
2
)
//if the key is "c"
{
stack
.
clear
();
calculator_screen
.
setFont
(
Font
.
font
(
"Verdana"
,
30
));
font_size
=
30
;
}
else
if
(
a
==
0
&&
b
==
3
)
//if the key is "b"
stack
.
pop
();
else
if
(
a
==
0
&&
b
==
1
)
// if the key is "="
{
stack
.
computeExp
();
}
else
stack
.
push
(
node
);
//otherwise push the key into the list
String
math_exp
=
stack
.
getAllNodeValues
();
if
(
math_exp
.
length
()
*
font_size
>
1.2
*
calculator_screen
.
getPrefWidth
())
{
font_size
/=
1.2
;
calculator_screen
.
setFont
(
Font
.
font
(
"Verdana"
,
font_size
));
}
calculator_screen
.
setText
(
math_exp
);
}
}
);
//Add special style for the "=" button
if
(
a
==
0
&&
b
==
1
)
btn
[
i
][
j
].
getStyleClass
().
add
(
"btnEqual"
);
else
if
(
a
==
0
&&
b
==
2
)
btn
[
i
][
j
].
getStyleClass
().
add
(
"btnClear"
);
else
if
(
a
==
0
&&
b
==
3
)
btn
[
i
][
j
].
getStyleClass
().
add
(
"btnBackspace"
);
keypad
.
getChildren
().
add
(
btn
[
i
][
j
]);
}
}
/*Put the calculator screen and keypad into a VBox layout*/
layout
.
setAlignment
(
Pos
.
CENTER
);
layout
.
getChildren
().
addAll
(
calculator_screen
,
keypad
);
layout
.
getStyleClass
().
add
(
"vbox1"
);
calculator_screen
.
prefWidthProperty
().
bind
(
keypad
.
widthProperty
());
/*Show the window*/
stage
.
setTitle
(
"Calculator"
);
stage
.
initStyle
(
StageStyle
.
UTILITY
);
stage
.
setResizable
(
false
);
Scene
scene
=
new
Scene
(
layout
);
scene
.
getStylesheets
().
add
(
getClass
().
getResource
(
"application.css"
).
toExternalForm
());
stage
.
setScene
(
scene
);
stage
.
show
();
}
}
__MACOSX/framework/._Calculator.java
framework/MyQueue.java
framework/MyQueue.java
package
application
;
public
class
MyQueue
<
T
>
{
private
T
[]
arr
;
//used to store data into this array in a queue manner
private
int
total
;
//the total number of elements in the queue
private
int
first
;
//the location of the first element in the queue
private
int
rear
;
//the location of the next available element (last one's next)
//Default constructor, by default the capacity is two elements of type T
public
MyQueue
()
{
arr
=
(
T
[])
new
Object
[
2
];
}
//Resize the MyQueue to the capacity as the input argument specifies
private
void
resize
(
int
capacity
)
{
T
[]
tmp
=
(
T
[])
new
Object
[
capacity
];
for
(
int
i
=
0
;
i
<
total
;
i
++
)
tmp
[
i
]
=
arr
[(
first
+
i
)
%
arr
.
length
];
arr
=
tmp
;
first
=
0
;
rear
=
total
;
}
//Check if the queue is empty: if empty, returns true; otherwise returns false
public
boolean
isEmpty
()
{
//Implementation here...
return
false
;
}
//Add one element "ele" into the queue
//Attention: (1) if the current queue is full, you need to resize it to twice of the current size.
// (2) if the "rear" is already pointing to the end of the queue, but there is available space
// in the beginning, you need to "loop" the rear position.
public
void
enqueue
(
T ele
)
{
//Implementation here...
}
//Delete the first (oldest) element from the queue and return this element.
//Below is just an example code, you need to modify it.
//Attention: (1) To save space, if the current number of elements is less than or equal to 1/4 of the
// the capacity, shrink the capacity to 1/2 (half) of the original size.
// (2) If the "first" is pointing to the end of the queue, but there is available space
// in the beginning, you need to consider "loop" the first position.
public
T dequeue
()
{
//Implementation here...
T ele
=
arr
[
0
];
return
ele
;
}
}
__MACOSX/framework/._MyQueue.java
framework/MyStack.java
framework/MyStack.java
package
application
;
/* Basic node element that is used by the linked list*/
class
StackNode
{
String
data
;
//the node stored value
StackNode
next
;
//pointing to next node
//Default constructor
public
StackNode
()
{
}
//Constructor with data value assigned
public
StackNode
(
String
value
)
{
data
=
value
;
}
//Constructor with data value and next node assigned
public
StackNode
(
String
value
,
StackNode
next
)
{
data
=
value
;
this
.
next
=
next
;
}
//Basic setters and getters
public
String
getData
()
{
return
data
;
}
public
void
setData
(
String
data
)
{
this
.
data
=
data
;
}
public
StackNode
getNext
()
{
return
next
;
}
public
void
setNext
(
StackNode
next
)
{
this
.
next
=
next
;
}
}
/* MyStack class is used to store string into the stack
* According to the stack logic, you need to implement the function members of MyStack based on the linked list structure
* */
public
class
MyStack
{
private
StackNode
top_node
;
//pointing to the first node
//Default constructor
public
MyStack
()
{
top_node
=
null
;
//by default, the node is empty.
}
//Constructor with the first node
public
MyStack
(
StackNode
node
)
{
top_node
=
node
;
}
//check if the stack is empty
public
boolean
isEmpty
()
{
if
(
top_node
==
null
)
return
true
;
else
return
false
;
}
//clear the stack
public
void
clear
()
{
top_node
=
null
;
}
//A general push() method for stack
public
void
pushNode
(
StackNode
node
)
{
if
(
top_node
==
null
)
{
top_node
=
node
;
}
else
{
node
.
setNext
(
top_node
);
top_node
=
node
;
}
}
//a specific push method for this calculator project uses only
public
void
push
(
StackNode
node
)
{
//if there is no any element in the stack, the calculator only accepts numbers or "-"
if
(
top_node
==
null
)
{
if
(
node
.
getData
().
matches
(
"^[0-9]+$"
)
||
node
.
getData
().
equals
(
"-"
))
top_node
=
node
;
}
else
if
(
node
.
getData
().
matches
(
"^[0-9]+$"
))
//if the inserted node is a number
{
node
.
setNext
(
top_node
);
top_node
=
node
;
}
else
//if the inserted node is "+", "-", "*"
{
if
(
top_node
.
getData
().
matches
(
"^[0-9]+$"
))
//if the recently inserted node is a number, then just insert the "node" straight away
{
node
.
setNext
(
top_node
);
top_node
=
node
;
}
else
//if recently inserted node is an operator, e.g. "+", "-", "*", then replace its value by the "node" value
{
if
(
top_node
.
getNext
()
!=
null
)
top_node
.
setData
(
node
.
getData
());
}
}
}
//remove the most recently inserted node
public
void
pop
()
{
if
(
top_node
!=
null
)
{
top_node
=
top_node
.
getNext
();
}
else
{
System
.
out
.
println
(
"The stack is already empty"
);
}
}
//get recently inserted node
public
StackNode
getTop
()
{
if
(
top_node
==
null
)
{
System
.
out
.
println
(
"The stack is empty"
);
}
else
{
return
top_node
;
}
return
null
;
}
//get and remove the most recently inserted node
public
StackNode
getTopandPop
()
{
if
(
top_node
==
null
)
{
System
.
out
.
println
(
"The stack is empty"
);
}
else
{
StackNode
temp
=
top_node
;
top_node
=
top_node
.
getNext
();
return
temp
;
}
return
null
;
}
//This function will all the stored strings connected and return it as a single string
public
String
getAllNodeValues
()
{
StringBuilder
all_strings
=
new
StringBuilder
();
//used to store all the strings from the stack
int
i
=
0
;
StackNode
temp
=
top_node
;
while
(
temp
!=
null
)
{
all_strings
.
append
(
temp
.
getData
());
temp
=
temp
.
getNext
();
}
all_strings
.
reverse
();
return
all_strings
.
toString
();
}
/*This function will to implement the "=" key that process the expression entered by users and calculates a final number.
In addition to the calculation, the final number needs to be converted into a string format and output to the display.
So there are five basic steps involved below.
Steps 1, 4, 5 are already completed. Your tasks will focus on Step 2 and Step 3
*/
public
void
computeExp
()
{
String
exp
=
getAllNodeValues
();
//get the current expression
//Step 1: convert a string into an infix queue
MyQueue
infix_queue
=
getInfixFromString
(
getAllNodeValues
());
//Step 2: convert an infix queue into an postfix queue
MyQueue
postfix_queue
=
infix2postfix
(
infix_queue
);
//Step 3: Compute the final value from the postfix queue
String
final_value
=
processPostfix
(
postfix_queue
);
//Step 4: Clear the stack
this
.
clear
();
//Step 5: put the final_value into the stack
for
(
int
i
=
0
;
i
<
final_value
.
length
();
i
++
)
this
.
pushNode
(
new
StackNode
(
final_value
.
substring
(
i
,
i
+
1
)));
}
/* Generate an infix expression according to an input String
* The infix expression is stored in a MyQueue variable, which is the returned value of the function */
private
MyQueue
getInfixFromString
(
String
exp
)
{
//Declare queue to store infix
MyQueue
infix_queue
=
new
MyQueue
();
//used as a temporary holder that extract operator and operand from exp
//Check if exp has at least one character
if
(
exp
.
length
()
<
1
)
return
infix_queue
;
// Check the first character if it is a negative sign
int
j
=
0
;
if
(
exp
.
substring
(
0
,
1
).
equals
(
"-"
))
{
j
=
1
;
}
// Check the last character if it is an operator, just drop it
if
(
exp
.
substring
(
exp
.
length
()
-
1
,
exp
.
length
()).
equals
(
"+"
)
||
exp
.
substring
(
exp
.
length
()
-
1
,
exp
.
length
()).
equals
(
"-"
)
||
exp
.
substring
(
exp
.
length
()
-
1
,
exp
.
length
()).
equals
(
"*"
))
{
exp
=
exp
.
substring
(
0
,
exp
.
length
()
-
1
);
}
// Traverse all the characters and push an operand or operator into
// infix_queue
for
(
int
i
=
j
;
i
<
exp
.
length
();
i
++
)
{
String
character
=
exp
.
substring
(
i
,
i
+
1
);
// If current character is an operator, then just push it to the
// stack and move to the next one
if
(
character
.
equals
(
"+"
)
||
character
.
equals
(
"*"
)
||
character
.
equals
(
"-"
))
{
infix_queue
.
enqueue
(
character
);
}
else
// If the current character is a number, it is an operand
{
StringBuilder
builder
=
new
StringBuilder
();
if
(
j
==
1
&&
i
==
1
)
builder
.
append
(
"-"
);
builder
.
append
(
character
);
i
++
;
// let the cursor moves one step forward
if
(
i
>=
exp
.
length
())
// check whether this move causes out of
// range
{
infix_queue
.
enqueue
(
builder
.
toString
());
break
;
}
while
(
exp
.
substring
(
i
,
i
+
1
).
matches
(
"^[0-9]+$"
))
{
builder
.
append
(
exp
.
substring
(
i
,
i
+
1
));
i
++
;
if
(
i
>=
exp
.
length
())
// check whether this move causes out
// of range
break
;
}
infix_queue
.
enqueue
(
builder
.
toString
());
i
--
;
// let the cursor moves one step back as at the end of the
// for-loop i++ automatically
}
}
//insert testing code here
return
infix_queue
;
}
/*Convert an input infix queue into A postfix queue, which is the output of the function */
private
MyQueue
infix2postfix
(
MyQueue
infix_queue
)
{
//Implementation here...
//Below is just a start, you need to fill the values for postfix_queue
//Declare a queue to store postfix
MyQueue
postfix_queue
=
new
MyQueue
();
//insert testing code here
return
postfix_queue
;
}
/* Process the postfix expression to compute the final value */
private
String
processPostfix
(
MyQueue
postfix
)
{
//Implementation here...
//Below is just a start, you need to fill the values for final_value
String
final_value
=
""
;
return
final_value
;
}
}