package calculator;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.EmptyStackException;
import java.util.Scanner;
import java.util.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import javax.swing.*;
/**
* Main class runs the calculator GUI program and uses
* InfixToPostfixParens to use the operants and the operands
* to return values on an output textField.
*
* @author Andrew W. Wilson
* @version 2/28/2011
*/
public class Main extends JFrame {
// Data Fields
/** The operator stack */
private Stack<Character> operatorStack;
/** The operators */
private static final String OPERATORS = "-+*/()";
/**
* The Pattern to extract tokens
* A token is either a string of digits (\d+)
* or a JavaIdentifier or an operator
*/
private static final Pattern pattern = Pattern.compile("\\d+\\.\\d*|\\d+|"
+ "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*"
+ "|[" + OPERATORS + "]");
/** The precedence of the operators, matches order of OPERATORS. */
private static final int[] PRECEDENCE = {1, 1, 2, 2, -1, -1};
/** The postfix string */
private StringBuilder postfix;
// creates buttons for the user interface
JButton one = new JButton("1");
JButton two = new JButton("2");
JButton three = new JButton("3");
JButton four = new JButton("4");
JButton five = new JButton("5");
JButton six = new JButton("6");
JButton seven = new JButton("7");
JButton eight = new JButton("8");
JButton nine = new JButton("9");
JButton zero = new JButton("0");
JButton plus = new JButton("+");
JButton division = new JButton("/");
JButton subtraction = new JButton("-");
JButton multiply = new JButton("*");
JButton clear = new JButton("Clear");
JButton equals = new JButton("=");
JButton decimal = new JButton(".");
JButton blank = new JButton(" ");
int total;
String update = ""; // infix string
private static final Font BIG = new Font("monspaced", Font.PLAIN, 20);
JTextField calculator = new JTextField();
/**
* This is the main method which runs the GUI
* @param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
JFrame userframe = new Main();
userframe.setVisible(true);
}
/**
* Creates the calculator's grid
*/
public Main() {
setTitle("Calculator");
setDefaultCloseOperation(EXIT_ON_CLOSE);
Container contentPane = getContentPane();
contentPane.setLayout(new BorderLayout());
// Creates the display field
calculator = new JTextField("0", 12);
calculator.setHorizontalAlignment(JTextField.RIGHT);
calculator.setFont(BIG);
// calculator.addActionListener(new AddButtonListener());
JPanel north = new JPanel();
JPanel south = new JPanel();
JPanel east = new JPanel();
JPanel center = new JPanel();
north.setLayout(new GridLayout(0, 1));
south.setLayout(new GridLayout(1, 4));
east.setLayout(new GridLayout(5, 0));
center.setLayout(new GridLayout(4, 3));
north.add(calculator);
add(north, BorderLayout.NORTH);
add(east, BorderLayout.EAST);
center.add(seven); // Create a GridLayout
seven.setFont(BIG);
seven.addActionListener(new SevenListener());
clear.setFont(BIG);
clear.addActionListener(new ClearListener());
center.add(eight);
eight.setFont(BIG);
eight.addActionListener(new EightListener());
center.add(nine);
nine.setFont(BIG);
nine.addActionListener(new NineListener());
center.add(four);
four.setFont(BIG);
four.addActionListener(new FourListener());
center.add(five);
five.setFont(BIG);
five.addActionListener(new FiveListener());
center.add(six);
six.setFont(BIG);
six.addActionListener(new SixListener());
center.add(one);
one.setFont(BIG);
one.addActionListener(new OneListener());
center.add(two);
two.setFont(BIG);
two.addActionListener(new TwoListener());
center.add(three);
three.setFont(BIG);
three.addActionListener(new ThreeListener());
center.add(blank);
center.add(zero);
zero.setFont(BIG);
zero.addActionListener(new ZeroListener());
center.add(decimal);
decimal.setFont(BIG);
decimal.addActionListener(new DecimalListener());
east.add(plus);
plus.setFont(BIG);
plus.addActionListener(new PlusListener());
east.add(multiply);
multiply.setFont(BIG);
multiply.addActionListener(new TimesListener());
east.add(division);
division.setFont(BIG);
division.addActionListener(new DivisionListener());
east.add(subtraction);
subtraction.setFont(BIG);
subtraction.addActionListener(new SubtractionListener());
east.add(equals);
equals.setFont(BIG);
equals.addActionListener(new EqualsListener());
south.add(clear);
add(south, BorderLayout.SOUTH);
add(center, BorderLayout.CENTER);
pack();
setLocationRelativeTo(null);
}
public String getTotalString() {
return "" + total;
}
public class SyntaxErrorException extends Exception {
/**
* Construct a SyntaxErrorException with the specified
* message.
* @param message The message
*/
SyntaxErrorException(String message) {
super(message);
}
}
/**
* Convert a string from infix to postfix.
* @param infix The infix expression
* @throws SyntaxErrorException
*/
public String convert(String infix) throws SyntaxErrorException {
operatorStack = new Stack<Character>();
postfix = new StringBuilder();
Scanner scan = new Scanner(infix);
try {
// Process each token in the infix string.
String nextToken;
while ((nextToken = scan.findInLine(pattern)) != null) {
char firstChar = nextToken.charAt(0);
// Is it an operand?
if (Character.isJavaIdentifierStart(firstChar)
|| Character.isDigit(firstChar)) {
postfix.append(nextToken);
postfix.append(' ');
} // Is it an operator?
else if (isOperator(firstChar)) {
processOperator(firstChar);
} else {
throw new SyntaxErrorException("Unexpected Character Encountered: "
+ firstChar);
}
}
// End while.
// Pop any remaining operators
// and append them to postfix.
while (!operatorStack.empty()) {
char op = operatorStack.pop();
// Any '(' on the stack is not matched.
if (op == '(') {
throw new SyntaxErrorException(
"Unmatched opening parenthesis");
}
postfix.append(op);
postfix.append(' ');
}
// assert: Stack is empty, return result.
return postfix.toString();
} catch (EmptyStackException ex) {
throw new SyntaxErrorException("Syntax Error: The stack is empty");
}
}
/**
* Method to process operators.
* @param op The operator
* @throws EmptyStackException
*/
private void processOperator(char op) {
if (operatorStack.empty() || op == '(') {
operatorStack.push(op);
} else {
// Peek the operator stack and
// let topOp be the top operator.
char topOp = operatorStack.peek();
if (precedence(op) > precedence(topOp)) {
operatorStack.push(op);
} else {
// Pop all stacked operators with equal
// or higher precedence than op.
while (!operatorStack.empty()
&& precedence(op) <= precedence(topOp)) {
operatorStack.pop();
if (topOp == '(') {
// Matching '(' popped - exit loop.
break;
}
postfix.append(topOp);
postfix.append(' ');
if (!operatorStack.empty()) {
// Reset topOp.
topOp = operatorStack.peek();
}
}
// assert: Operator stack is empty or
// current operator precedence >
// top of stack operator precedence.
if (op != ')') {
operatorStack.push(op);
}
}
}
}
/**
* Determine whether a character is an operator.
* @param ch The character to be tested
* @return true if character is an operator
*/
private boolean isOperator(char ch) {
return OPERATORS.indexOf(ch) != -1;
}
/**
* Determine the precedence of an operator.
* @param op The operator
* @return the precedence
*/
private int precedence(char op) {
return PRECEDENCE[OPERATORS.indexOf(op)];
}
public static double evaluatePostfix(String expression) {
MyStack evalStack = new MyStack();
String[] split = expression.split(" ");
for (int i = 0; i < split.length; i++) {
//System.out.println("split[" + i + "] = " + split[i]);
if (split[i].equals("+")) {
double secondOperand = evalStack.pop().getData();
double firstOperand = evalStack.pop().getData();
double result = firstOperand + secondOperand;
evalStack.push(new StackNode(result));
} else if (split[i].equals("-")) {
double secondOperand = evalStack.pop().getData();
double firstOperand = evalStack.pop().getData();
double result = firstOperand - secondOperand;
evalStack.push(new StackNode(result));
} else if (split[i].equals("*")) {
double secondOperand = evalStack.pop().getData();
double firstOperand = evalStack.pop().getData();
double result = firstOperand * secondOperand;
evalStack.push(new StackNode(result));
} else if (split[i].equals("/")) {
double secondOperand = evalStack.pop().getData();
double firstOperand = evalStack.pop().getData();
double result = firstOperand / secondOperand;
evalStack.push(new StackNode(result));
} else {
// split[i] is a number (assumption)
evalStack.push(new StackNode(Double.parseDouble(split[i])));
}
}
double expressionValue = evalStack.pop().getData();
return expressionValue;
}
/**
* OneListener class creates an action for the one button
* which allows operations to be used.
*/
public class OneListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
String display = calculator.getText();
update = display + "1";
calculator.setText(update);
}
}
/**
* TwoListener class creates an action for the two button
* which allows operations to be used.
*/
public class TwoListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
String display = calculator.getText();
update = display + "2";
calculator.setText(update);
}
}
/**
* ThreeListener class creates an action for the three button
* which allows operations to be used.
*/
public class ThreeListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
String display = calculator.getText();
update = display + "3";
calculator.setText(update);
}
}
/**
* FourListener class creates an action for the four button
* which allows operations to be used.
*/
public class FourListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
String display = calculator.getText();
update = display + "4";
calculator.setText(update);
}
}
/**
* FiveListener class creates an action for the five button
* which allows operations to be used.
*/
public class FiveListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
String display = calculator.getText();
update = display + "5";
calculator.setText(update);
}
}
/**
* SixListener class creates an action for the six button
* which allows operations to be used.
*/
public class SixListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
String display = calculator.getText();
update = display + "6";
calculator.setText(update);
}
}
/**
* SevenListener class creates an action for the seven button
* which allows operations to be used.
*/
public class SevenListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
String display = calculator.getText();
update = display + "7";
calculator.setText(update);
}
}
/**
* EightListener class creates an action for the eight button
* which allows operations to be used.
*/
public class EightListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
String display = calculator.getText();
update = display + "8";
calculator.setText(update);
}
}
/**
* NineListener class creates an action for the nine button
* which allows operations to be used.
*/
public class NineListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
String display = calculator.getText();
update = display + "9";
calculator.setText(update);
}
}
/**
* ClearListener class creates an action for the clear button
* which allows operations to be used.
*/
public class ClearListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
calculator.setText(" ");
}
}
/**
* PlusListener class creates an action for the + button
* which allows operations to be used.
*/
public class PlusListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
String display = calculator.getText();
update = display + "+";
calculator.setText(update);
}
}
/**
* DivisionListener class creates an action for the / button
* which allows operations to be used.
*/
public class DivisionListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
String display = calculator.getText();
update = display + "/";
calculator.setText(update);
}
}
/**
* TimesListener class creates an action for the * button
* which allows operations to be used.
*/
public class TimesListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
String display = calculator.getText();
update = display + "*";
calculator.setText(update);
}
}
/**
* SubtractionListener class creates an action for the subtraction button
* which subtracts values.
*/
public class SubtractionListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
String display = calculator.getText();
update = display + "-";
calculator.setText(update);
}
}
/**
* DecimalListener class creates an action for the decimal button
* which allows operations to be used.
*/
public class DecimalListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
String display = calculator.getText();
update = display + ".";
calculator.setText(update);
}
}
/**
* ZeroListener class creates an action for the zero button
* which allows operations to be used.
*/
public class ZeroListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
String display = calculator.getText();
update = display + "0";
calculator.setText(update);
}
}
/**
* EqualsListener class creates an action for the = button
* which allows operations to be used.
*/
public class EqualsListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
String displayText = calculator.getText();
String equalsign = "=";
Main number = new Main();
try {
if (equalsign.equals("=")) {
number.convert(displayText);
} else if (equalsign.equals("+")) {
number.convert(displayText);
} else if (equalsign.equals("-")) {
number.convert(displayText);
} else if (equalsign.equals("*")) {
number.convert(displayText);
} else if (equalsign.equals("/")) {
number.convert(displayText);
}
} catch (SyntaxErrorException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
calculator.setText(" " + number.getTotalString());
}
}
}