/* Applet Calculet implements a simple GUI calculator, providing an output LCD and buttons that support 6 operations (add, subtract, multiply, divide and negate), 10 digits, a decimal point, and a "clr" key. There is an non-Applet version called Calculator that implements the same feature set. To simplify program organization, there is only one ActionListener that processes GUI input by simulating a finite state machine (FSM or FSA, finite state automaton). State-driven algorithms have a long history in input parsing, and appear to simplify event processing in this program. */ import javax.swing.*; import java.awt.*; import java.awt.Frame.*; import javax.swing.border.*; import java.awt.event.*; public class Calculet extends javax.swing.JApplet implements ActionListener { public static int LCDvalue; public static String LCDvaluestring = new String("0"); public static double firstOperandValue=0; public static double secondOperandValue=0; public static double result=0; public static String operator = new String(""); public static int dotcount; public static int currentState = 0; public final int NUMSTATES = 5; public final int NUMCATEGORIES = 5; public final int NUM = 0; public final int CLR = 1; public final int EQ = 2; public final int OP = 3; public final int NEGATE = 4; public static StateN statelist[]; public static int[][] stateTransition; public static JTextField LCDtext = new JTextField(12); public static Font LCDfont = new Font("Geneva", Font.BOLD, 40); public void init() // set up the GUI here. { System.out.println("here moma\n"); JFrame calculatorFrame = new JFrame("Caculet"); Container c = calculatorFrame.getContentPane(); // replace this line with //Container c = this.getContentPane(); // this line to display applet // within the html page. c.setLayout(new BorderLayout(10,10)); calculatorFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); // remove this line to display in page. JPanel numerickeys = new JPanel(); TitledBorder numkeyBorder = new TitledBorder("Punch in numbers here"); numkeyBorder.setTitlePosition(TitledBorder.TOP); numkeyBorder.setTitleJustification(TitledBorder.LEFT); numerickeys.setBorder(numkeyBorder); numerickeys.setLayout(new GridLayout(4,4)); JButton but0 = new JButton("0"); but0.setActionCommand("0"); but0.addActionListener(this); Font numkeyfont = new Font("Geneva", Font.BOLD, 40); but0.setFont(numkeyfont); JButton but1 = new JButton("1"); but1.setActionCommand("1"); but1.addActionListener(this); but1.setFont(numkeyfont); JButton but2 = new JButton("2"); but2.setActionCommand("2"); but2.addActionListener(this); but2.setFont(numkeyfont); JButton but3 = new JButton("3"); but3.setActionCommand("3"); but3.addActionListener(this); but3.setFont(numkeyfont); JButton but4 = new JButton("4"); but4.setActionCommand("4"); but4.addActionListener(this); but4.setFont(numkeyfont); JButton but5 = new JButton("5"); but5.setActionCommand("5"); but5.addActionListener(this); but5.setFont(numkeyfont); JButton but6 = new JButton("6"); but6.setActionCommand("6"); but6.addActionListener(this); but6.setFont(numkeyfont); JButton but7 = new JButton("7"); but7.setActionCommand("7"); but7.addActionListener(this); but7.setFont(numkeyfont); JButton but8 = new JButton("8"); but8.setActionCommand("8"); but8.addActionListener(this); but8.setFont(numkeyfont); JButton but9 = new JButton("9"); but9.setActionCommand("9"); but9.addActionListener(this); but9.setFont(numkeyfont); JButton butdot = new JButton("."); butdot.setActionCommand("."); butdot.addActionListener(this); butdot.setFont(numkeyfont); JButton butclr = new JButton("clr"); butclr.setActionCommand("clr"); butclr.addActionListener(this); butclr.setFont(numkeyfont); numerickeys.add(but7); numerickeys.add(but8); numerickeys.add(but9); numerickeys.add(but4); numerickeys.add(but5); numerickeys.add(but6); numerickeys.add(but1); numerickeys.add(but2); numerickeys.add(but3); numerickeys.add(but0); numerickeys.add(butdot); numerickeys.add(butclr); c.add(numerickeys, "Center"); JPanel opkeys = new JPanel(); TitledBorder opkeyBorder = new TitledBorder("Punch an operator"); opkeyBorder.setTitlePosition(TitledBorder.TOP); opkeyBorder.setTitleJustification(TitledBorder.LEFT); opkeys.setBorder(opkeyBorder); Font opkeyfont = new Font("Geneva", Font.BOLD, 40); opkeys.setLayout(new GridLayout(2,4,0,0) ); JButton butplus = new JButton("+"); butplus.setActionCommand("plus"); butplus.addActionListener(this); butplus.setFont(opkeyfont); JButton butminus= new JButton("-"); butminus.setActionCommand("minus"); butminus.addActionListener(this); butminus.setFont(opkeyfont); JButton butmultiply = new JButton("*"); butmultiply.setActionCommand("multiply"); butmultiply.addActionListener(this); butmultiply.setFont(opkeyfont); JButton butdivide = new JButton("/"); butdivide.setActionCommand("divide"); butdivide.addActionListener(this); butdivide.setFont(opkeyfont); JButton butequal = new JButton("="); butequal.setActionCommand("equal"); butequal.addActionListener(this); butequal.setFont(opkeyfont); JButton butnegate = new JButton("neg"); butnegate.setActionCommand("negate"); butnegate.addActionListener(this); butnegate.setFont(numkeyfont); opkeys.add(butplus); opkeys.add(butminus); opkeys.add(butmultiply); opkeys.add(butdivide); opkeys.add(butnegate); opkeys.add(butequal); c.add(opkeys, "South"); JPanel lcd = new JPanel(); TitledBorder lcdBorder = new TitledBorder("Read this LCD"); lcdBorder.setTitlePosition(TitledBorder.TOP); lcdBorder.setTitleJustification(TitledBorder.LEFT); lcd.setBorder(lcdBorder); lcd.setLayout(new GridLayout(1,1,5,10) ); LCDtext.setFont(LCDfont); LCDtext.setEditable(false); LCDtext.setText(LCDvaluestring); lcd.add(LCDtext); c.add(lcd, "North"); calculatorFrame.setBounds(100,100,330,500); // replace this line with //this.setBounds(100,100,330,500); // this line to stay in page. calculatorFrame.setVisible(true); // replace this line with //this.setVisible(true); // this line to stay in page. } public Calculet() { /* First define the state machine. It consists of two components: 1) A set of states...here defined as inside classes morphed from a template state called StateN...and referenced through an array of (polymorphic) objects. 2) a set of transitions from one state to another goverened by category ot the GUI input....here represented by a matrix of integer index values where each integer is used to index into the array of states just mentioned. Note that "statelist" and "stateTransition" were earlier defined as instance variables: public static StateN statelist[] and public static int[][] stateTransition */ statelist = new StateN[NUMSTATES]; statelist[0] = new StateZero(); statelist[1] = new StateOne(); statelist[2] = new StateTwo(); statelist[3] = new StateThree(); statelist[4] = new StateFour(); stateTransition = new int[NUMSTATES][NUMCATEGORIES]; stateTransition [0] [CLR] = 0; stateTransition [0] [NUM] = 1; stateTransition [0] [EQ] = 0; stateTransition [0] [OP] = 0; stateTransition [0] [NEGATE] = 1; stateTransition [1] [CLR] = 0; stateTransition [1] [NUM] = 1; stateTransition [1] [EQ] = 1; stateTransition [1] [OP] = 2; stateTransition [1] [NEGATE] = 1; stateTransition [2] [CLR] = 0; stateTransition [2] [NUM] = 3; stateTransition [2] [EQ] = 2; stateTransition [2] [OP] = 2; stateTransition [2] [NEGATE] = 2; stateTransition [3] [CLR] = 2; stateTransition [3] [NUM] = 3; stateTransition [3] [EQ] = 4; stateTransition [3] [OP] = 2; stateTransition [3] [NEGATE] = 3; stateTransition [4] [CLR] = 0; stateTransition [4] [NUM] = 1; stateTransition [4] [EQ] = 4; stateTransition [4] [OP] = 2; stateTransition [4] [NEGATE] = 1; } public void actionPerformed(ActionEvent event) { /* Categorize the GUI input, execute current state, change state, and reprint the LCD text. This is the "guts" of the whole algorithm. */ String command = event.getActionCommand(); System.out.println("The command (button) pressed was:"+command); int inputCategory; if ( ( command == "1" ) || ( command == "2") || ( command == "3" ) || ( command == "4" ) ||( command == "5" ) || ( command == "6" ) || ( command == "7" ) ||( command == "8" ) || ( command == "9" ) || ( command == "0" ) || ( command == ".") ) { // note the use of "=="; should really be "command.equals"!!! inputCategory = NUM; } else if ( ( command == "plus" ) || ( command == "multiply" ) || ( command == "divide" ) || ( command == "minus" ) ) { inputCategory = OP; } else if ( command == "equal" ) { inputCategory = EQ; } else if (command == "clr" ) { inputCategory = CLR; } else if (command == "negate" ) { inputCategory = NEGATE; } else { LCDtext.setText("Input key category unknown."); System.out.println("Input key category unknown."); inputCategory = -1; stop(); } System.out.println ("Entering state "+currentState+ "\n with inputCategory="+inputCategory+ "\n and operator="+Calculet.operator+ "\n and LCDvaluestring="+ Calculet.LCDvaluestring+ "\n and firstOperandValue="+Calculet.firstOperandValue+ "\n and secondOperandValue="+Calculet.secondOperandValue+ "\n and result ="+Calculet.result); statelist[currentState].perform ( command, inputCategory ); System.out.println (" Leaving state "+currentState+ " for new state "+stateTransition[currentState][inputCategory]+ "\n with inputCategory="+inputCategory+ "\n and operator="+Calculet.operator+ "\n and LCDvaluestring="+ Calculet.LCDvaluestring+ "\n and firstOperandValue="+Calculet.firstOperandValue+ "\n and secondOperandValue="+Calculet.secondOperandValue+ "\n and result ="+Calculet.result); currentState = stateTransition [currentState] [inputCategory]; int fieldwidth = LCDtext.getSize().width; int fontsize = (int) (1.9 * (fieldwidth / (LCDvaluestring.length() + 1))); if (fontsize >40 ) { fontsize = 40; } Font LCDadjustablefont = new Font("Geneva", Font.BOLD, fontsize); LCDtext.setFont(LCDadjustablefont); LCDtext.setText(LCDvaluestring); } public String getSymbol ( String astring ) { // Convert operator command name to something printable. if ( astring.equals( "plus" ) ) { return " +"; } else if ( astring.equals( "multiply" ) ) { return " *"; } else if ( astring.equals( "divide" ) ) { return " /"; } else if ( astring.equals( "minus" ) ) { return " -"; } else if ( astring.equals( "negate" ) ) { System.out.println( "Shouldn't find negate here."); return "Shouldn't happen."; } else { return ""; } } class StateN { void perform(String aninputstring, int inputCategory) { //doesn't do anything but serve as polymorphism template } } class StateZero extends StateN // Starting state...wherein a digit, { // a dot or a "neg" starts a number. void perform(String astring, int inputCategory) { switch (inputCategory) { case NUM: LCDvaluestring = astring; if ( astring.equals(".") ) { dotcount = 1; } else { dotcount = 0; } break; case NEGATE: LCDvaluestring = "-"; break; case OP: break; case EQ: break; case CLR: LCDvaluestring = "0"; firstOperandValue = 0; dotcount = 0; break; } } } class StateOne extends StateN { /* Accumulate first operand digits until an operator is entered then store the operator, convert the digit string to Double, and exit. */ void perform(String astring, int inputCategory) { switch (inputCategory) { case NUM: if ( astring.equals(".") ) { dotcount = dotcount + 1; } else { LCDvaluestring = LCDvaluestring + astring; } System.out.println("here with dotcount="+dotcount); if ( astring.equals(".") && (dotcount < 2 ) ) { LCDvaluestring = LCDvaluestring+"."; } break; case NEGATE: System.out.println("---------lcdvalstring="+LCDvaluestring+"<"); System.out.println("*********string segment="+LCDvaluestring.substring(0,1)+"<"); if ( LCDvaluestring.substring( 0,1 ).equals( "-" ) ) { LCDvaluestring = "+" + LCDvaluestring.substring( 1 ); } else if (LCDvaluestring.substring( 0,1 ).equals( "+") ) { LCDvaluestring = "-" + LCDvaluestring.substring ( 1 ); } else if (LCDvaluestring.substring( 0,1 ).equals(" ") ) { LCDvaluestring = "-"; } else { LCDvaluestring = "-" + LCDvaluestring; } break; case OP: Calculet.operator = astring; System.out.println("***Calculet.operator getting "+Calculet.operator); if ( ( ! LCDvaluestring.equals( "" ) ) && ( ! LCDvaluestring.equals( "-" ) ) && ( ! LCDvaluestring.equals( "." ) ) ) { firstOperandValue = Double.parseDouble(LCDvaluestring); } else { firstOperandValue = 0; } LCDvaluestring = getSymbol (Calculet.operator); dotcount = 0; break; case EQ: break; case CLR: LCDvaluestring = "0"; firstOperandValue = 0; dotcount = 0; break; } } } class StateTwo extends StateN // Wait for start of another number. { void perform(String astring, int inputCategory) { switch (inputCategory) { case NUM: if ( LCDvaluestring.equals( "-") || LCDvaluestring.equals( "+" ) ) { LCDvaluestring = LCDvaluestring + astring; } else { LCDvaluestring = astring; } if ( astring.equals(".") ) { dotcount = 1; } else { dotcount = 0; } break; case NEGATE: if ( LCDvaluestring.substring( 0,1 ).equals( "-" ) ) { LCDvaluestring = "+" + LCDvaluestring.substring( 1 ); } else if (LCDvaluestring.substring( 0,1 ).equals( "+") ) { LCDvaluestring = "-" + LCDvaluestring.substring ( 1 ); } else if (LCDvaluestring.substring( 0,1 ).equals(" ") ) { LCDvaluestring = "-"; } else { LCDvaluestring = "-" + LCDvaluestring; } break; case OP: operator = astring; //System.out.println("...operator getting "+operator); dotcount = 0; break; case EQ: break; case CLR: LCDvaluestring = "0"; firstOperandValue = 0; dotcount = 0; break; } } } class StateThree extends StateN { /* Accumulate second operand digits until an equal is entered, then convert second operand to Double, calculate the result of the requested operation, convert the result to String and move it to the LCD textfield. */ void perform(String astring, int inputCategory) { switch (inputCategory) { case NUM: if ( astring.equals(".") ) { dotcount = dotcount + 1; } else { LCDvaluestring = LCDvaluestring + astring; } if ( astring.equals(".") && (dotcount < 2 ) ) { LCDvaluestring = LCDvaluestring + "."; } break; case NEGATE: if ( LCDvaluestring.substring( 0,1 ).equals( "-" ) ) { LCDvaluestring = "+" + LCDvaluestring.substring( 1 ); } else if (LCDvaluestring.substring( 0,1 ).equals( "+") ) { LCDvaluestring = "-" + LCDvaluestring.substring ( 1 ); } else { LCDvaluestring = "-" + LCDvaluestring; } break; case OP: break; case EQ: if ( ( ! LCDvaluestring.equals( "" ) ) && ( ! LCDvaluestring.equals( "-" ) ) && ( ! LCDvaluestring.equals( "." ) ) ) { secondOperandValue = Double.parseDouble(LCDvaluestring); } else { secondOperandValue = 0; } LCDvaluestring = ""; if ( Calculet.operator.equals( "plus" ) ) { result = firstOperandValue + secondOperandValue; } else { if (Calculet.operator.equals( "minus" ) ) { result = firstOperandValue - secondOperandValue; } else { if (Calculet.operator.equals( "multiply" ) ) { result = firstOperandValue * secondOperandValue; } else { if (Calculet.operator.equals( "divide" ) ) { if ( secondOperandValue != 0 ) { result = firstOperandValue / secondOperandValue; } else { result = 0; LCDvaluestring = "NaN"; } } else { System.out.println( "Operator value ("+ Calculet.operator+") not recognized in state 3!" ); } } } } firstOperandValue = result; if ( ! ( LCDvaluestring.equals ("NaN") ) ) { LCDvaluestring = Double.toString(result); } break; case CLR: LCDvaluestring = "0"; firstOperandValue = 0; dotcount = 0; break; } } } class StateFour extends StateN { /* Determine how to continue. An operator entered at this point requests a continuation using the last result as the new first operand. A numeric character ( digit, "neg" or decimal point ) requests a continuation using two NEW operands. */ void perform(String astring, int inputCategory) { switch (inputCategory) { case NUM: firstOperandValue = 0; LCDvaluestring = astring; if ( astring.equals(".") ) { dotcount = 1; } else { dotcount = 0; } break; case NEGATE: firstOperandValue = 0; LCDvaluestring = "-"; dotcount = 0; break; case OP: Calculet.operator = astring; System.out.println(">>>Calculet.operator getting "+Calculet.operator); LCDvaluestring = getSymbol (Calculet.operator); break; case EQ: break; case CLR: LCDvaluestring = "0"; firstOperandValue = 0; dotcount = 0; break; } } } }