Java/SWT JFace Eclipse/Calculator

Материал из Java эксперт
Перейти к: навигация, поиск

Calculator by Michael Schmidt

/**
 * Instances of this object class open a dialog with a simple calculator.
 * The calculator includes a memory register, Tooltips for the keys, and error 
 * checking.  It performs double precision calculations and includes square 
 * root and inverse functions.  Other computations can be added easily.
 * The code contains extensive comments.    
 * This class can be called via an Action from the menu or toolbar.
 * Feel free to use this in your application!
 * 
 * @author Michael Schmidt, 2006.
 */
//package mschmidt.komo;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.ruposite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
public class CalculatorMichaelSchmidt extends Dialog {
  /*
  * Initialize variables needed for this class.
  */
  private Text displayText;
  // The three calculator registers.
  private String displayString = "0.";
  private String memoryString = new String();
  private String operatorString = new String();
  // A variable to store the pending calculation
  private char calcChar = " ";
  // Error strings
  private final String ERROR_STRING = "Error: ";
  private final String NAN_STRING = "Not a Number";
  private final String LONG_STRING = "Number too long";
  private final String INFINITY_STRING = "Infinity";
  // A flag to check if display should be cleared on the next keystroke
  private boolean clearDisplay = true;
  // An ID constant for the copy to clipboard key
  private static final int CLIPBOARD_ID = IDialogConstants.NO_TO_ALL_ID + 1;
  public static void main(String[] a){
    new CalculatorMichaelSchmidt(new Shell(new Display()));
    
  }
  
  /*
   * Standard constructor to create the dialog.
   * @param parentShell the Dialog shell
   */
  public CalculatorMichaelSchmidt(final Shell parentShell) {
    super(parentShell);
  }
  /*
   * Create contents of the dialog, a display at the top and the various
   * buttons.  The Tooltip for each button explains its function.
   * @param parent the composite
   * @return Control the controls
   */
  @Override
  protected Control createDialogArea(final Composite parent) {
    Composite container = (Composite) super.createDialogArea(parent);
    final GridLayout calculatorGridLayout = new GridLayout();
    calculatorGridLayout.marginRight = 5;
    calculatorGridLayout.marginLeft = 5;
    calculatorGridLayout.marginBottom = 5;
    calculatorGridLayout.marginTop = 5;
    calculatorGridLayout.marginWidth = 10;
    calculatorGridLayout.marginHeight = 2;
    calculatorGridLayout.numColumns = 4;
    calculatorGridLayout.verticalSpacing = 2;
    calculatorGridLayout.makeColumnsEqualWidth = true;
    calculatorGridLayout.horizontalSpacing = 2;
    container.setLayout(calculatorGridLayout);
    // The display.  Note that it has a limit of 30 characters, 
    // much greater than the length of a double-precision number. 
    displayText = new Text(container, SWT.RIGHT | SWT.BORDER);
    displayText.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
    displayText.setEditable(false);
    displayText.setDoubleClickEnabled(false);
    displayText.setTextLimit(30);
    displayText.setText(displayString);
    displayText.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 4, 1));
    final Button msButton = new Button(container, SWT.NONE);
    msButton.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(final SelectionEvent e) {
        updateMemory("S");
      }
    });
    msButton.setToolTipText("Save value to Memory");
    msButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    msButton.setText("MS");
    final Button mcButton = new Button(container, SWT.NONE);
    mcButton.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(final SelectionEvent e) {
        updateDisplay("D");
      }
    });
    mcButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    mcButton.setToolTipText("Clear Memory");
    mcButton.setText("MC");
    final Button clearButton = new Button(container, SWT.NONE);
    clearButton.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(final SelectionEvent e) {
        updateDisplay("C");
      }
    });
    clearButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    clearButton.setToolTipText("Clear all Calculator Registers");
    clearButton.setText("C");
    final Button ceButton = new Button(container, SWT.NONE);
    ceButton.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(final SelectionEvent e) {
        updateDisplay("E");
      }
    });
    ceButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    ceButton.setToolTipText("Clear Entry");
    ceButton.setText("CE");
    final Button memAddButton = new Button(container, SWT.NONE);
    memAddButton.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(final SelectionEvent e) {
        updateMemory("+");
      }
    });
    memAddButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    memAddButton.setToolTipText("Add value to Memory");
    memAddButton.setText("M+");
    final Button mrButton = new Button(container, SWT.NONE);
    mrButton.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(final SelectionEvent e) {
        updateDisplay("R");
      }
    });
    mrButton.setToolTipText("Recall value in Memory");
    mrButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    mrButton.setText("MR");
    final Button backButton = new Button(container, SWT.NONE);
    backButton.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(final SelectionEvent e) {
        updateDisplay("B");
      }
    });
    backButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    backButton.setToolTipText("Backspace");
    backButton.setText("BACK");
    final Button divideButton = new Button(container, SWT.NONE);
    divideButton.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(final SelectionEvent e) {
        updateCalc("/");
      }
    });
    divideButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    divideButton.setToolTipText("Divide");
    divideButton.setText("/");
    final Button memSubtractButton = new Button(container, SWT.NONE);
    memSubtractButton.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(final SelectionEvent e) {
        updateMemory("-");
      }
    });
    memSubtractButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    memSubtractButton.setToolTipText("Subtract value from Memory");
    memSubtractButton.setText("M-");
    final Button inverseButton = new Button(container, SWT.NONE);
    inverseButton.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(final SelectionEvent e) {
        updateDisplay("I");
      }
    });
    inverseButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    inverseButton.setToolTipText("Inverse of value");
    inverseButton.setText("1/X");
    final Button sqrtButton = new Button(container, SWT.NONE);
    sqrtButton.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(final SelectionEvent e) {
        updateDisplay("Q");
      }
    });
    sqrtButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    sqrtButton.setToolTipText("Square Root of value");
    sqrtButton.setText("SQRT");
    final Button multiplyButton = new Button(container, SWT.NONE);
    multiplyButton.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(final SelectionEvent e) {
        updateCalc("*");
      }
    });
    multiplyButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    multiplyButton.setToolTipText("Multiply");
    multiplyButton.setText("*");
    final Button num7Button = new Button(container, SWT.NONE);
    num7Button.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(final SelectionEvent e) {
        updateDisplay("7");
      }
    });
    num7Button.setToolTipText("Numeric Pad");
    num7Button.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    num7Button.setText("7");
    final Button num8Button = new Button(container, SWT.NONE);
    num8Button.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(final SelectionEvent e) {
        updateDisplay("8");
      }
    });
    num8Button.setToolTipText("Numeric Pad");
    num8Button.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    num8Button.setText("8");
    final Button num9Button = new Button(container, SWT.NONE);
    num9Button.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(final SelectionEvent e) {
        updateDisplay("9");
      }
    });
    num9Button.setToolTipText("Numeric Pad");
    num9Button.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    num9Button.setText("9");
    final Button SubtractButton = new Button(container, SWT.NONE);
    SubtractButton.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(final SelectionEvent e) {
        updateCalc("-");
      }
    });
    SubtractButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    SubtractButton.setToolTipText("Subtract");
    SubtractButton.setText("-");
    final Button num4Button = new Button(container, SWT.NONE);
    num4Button.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(final SelectionEvent e) {
        updateDisplay("4");
      }
    });
    num4Button.setToolTipText("Numeric Pad");
    num4Button.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    num4Button.setText("4");
    final Button num5Button = new Button(container, SWT.NONE);
    num5Button.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(final SelectionEvent e) {
        updateDisplay("5");
      }
    });
    num5Button.setToolTipText("Numeric Pad");
    num5Button.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    num5Button.setText("5");
    final Button num6Button = new Button(container, SWT.NONE);
    num6Button.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(final SelectionEvent e) {
        updateDisplay("6");
      }
    });
    num6Button.setToolTipText("Numeric Pad");
    num6Button.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    num6Button.setText("6");
    final Button AdditionButton = new Button(container, SWT.NONE);
    AdditionButton.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(final SelectionEvent e) {
        updateCalc("+");
      }
    });
    AdditionButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    AdditionButton.setToolTipText("Add");
    AdditionButton.setText("+");
    final Button num1Button = new Button(container, SWT.NONE);
    num1Button.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(final SelectionEvent e) {
        updateDisplay("1");
      }
    });
    num1Button.setCapture(true);
    num1Button.setToolTipText("Numeric Pad");
    num1Button.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    num1Button.setText("1");
    final Button num2Button = new Button(container, SWT.NONE);
    num2Button.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(final SelectionEvent e) {
        updateDisplay("2");
      }
    });
    num2Button.setToolTipText("Numeric Pad");
    num2Button.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    num2Button.setText("2");
    final Button num3Button = new Button(container, SWT.NONE);
    num3Button.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(final SelectionEvent e) {
        updateDisplay("3");
      }
    });
    num3Button.setToolTipText("Numeric Pad");
    num3Button.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    num3Button.setText("3");
    final Button equalsButton = new Button(container, SWT.NONE);
    equalsButton.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(final SelectionEvent e) {
        updateCalc("=");
      }
    });
    equalsButton.setToolTipText("Equals (get result)");
    equalsButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 1, 2));
    equalsButton.setText("=");
    final Button num0Button = new Button(container, SWT.NONE);
    num0Button.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(final SelectionEvent e) {
        updateDisplay("0");
      }
    });
    num0Button.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    num0Button.setToolTipText("Numeric Pad");
    num0Button.setText("0");
    final Button decimalButton = new Button(container, SWT.NONE);
    decimalButton.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(final SelectionEvent e) {
        updateDisplay(".");
      }
    });
    decimalButton.setToolTipText("Numeric Pad");
    decimalButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    decimalButton.setText(".");
    final Button signButton = new Button(container, SWT.NONE);
    signButton.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(final SelectionEvent e) {
        updateDisplay("-");
      }
    });
    signButton.setToolTipText("Change sign of value");
    signButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    signButton.setText("+/-");
    new Label(container, SWT.NONE);
    //
    return container;
  }
  /*
   * Create contents of the button bar, including a button to copy the 
   * display to the system clipboard and a close button.
   * @param parent
   */
  @Override
  protected void createButtonsForButtonBar(final Composite parent) {
    createButton(parent, CLIPBOARD_ID, "Copy to Clipboard", false);
    createButton(parent, IDialogConstants.CANCEL_ID, 
        IDialogConstants.CLOSE_LABEL, true);
  }
  /*
   * Set the initial size of the dialog.
   */
  @Override
  protected Point getInitialSize() {
    return new Point(260, 350);
  }
  /*
  *  Standard method to configure the shell.
  */
  protected void configureShell(final Shell newShell) {
    super.configureShell(newShell);
    newShell.setText("Calculator");
    // Include code here to set the window icon.  The line here is a sample using the 
    // Eclipse plugin getImageDescriptor convenience method.
//    newShell.setImage(KomoPlugin.getImageDescriptor(ApplicationImages.CALCULATOR_ICON).createImage());
  }
  
  /*
   *  (non-Javadoc)
   *  Method that copies contents of the display window to the clipboard when 
   *  the Copy to Clipboard button is clicked.
   * @see org.eclipse.jface.dialogs.Dialog#buttonPressed(int)
   */
  @Override
  protected void buttonPressed(final int buttonID) {
    if (buttonID == CLIPBOARD_ID) {
      displayText.selectAll();
      displayText.copy();
      displayText.clearSelection();
    } else {
      super.buttonPressed(buttonID);
      
    }
  }
  /*
   * This method updates the display text based on user input.
   */
  private void updateDisplay(final char keyPressed) {
    char keyVal = keyPressed;
    String tempString = new String();
    boolean doClear = false;
    
    if (!clearDisplay) {
      tempString = displayString;
    }
    
    switch (keyVal) {
    case "B": // Backspace
      if (tempString.length() < 2) {
        tempString = "";
      } else {
        tempString = tempString.substring(0, tempString.length() - 1);
      }
      break;
      
    case "C": // Clear
      tempString = "0.";
      operatorString = "";
      calcChar = " ";
      doClear = true;
      break;
      
    case "D": // Clear Memory
      memoryString = "";
      break;
      
    case "E": // Clear Entry
      tempString = "0.";
      doClear = true;
      break;
      
    case "I": // Inverse
      tempString = doCalc(displayString, "", keyVal);
      doClear = true;
      break;
      
    case "Q": // Square Root
      tempString = doCalc(displayString, "", keyVal);
      doClear = true;
      break;
      
    case "R": // Recall Memory to Display
      tempString = memoryString;
      doClear = true;
      break;
      
    case "-": // Change Sign
      if (tempString.startsWith("-")) {
        tempString = tempString.substring(1, tempString.length());
      } else {
        tempString = keyVal + tempString;
      }
      break;
    
    case ".": // Can"t have two decimal points.
      if (tempString.indexOf(".") == -1 && tempString.length() < 29) {
        tempString = tempString + keyVal;
      }
      break;
      
    case "0": // Don"t want 00 to be entered.
      if (!tempString.equals("0") && tempString.length() < 29) {
        tempString = tempString + keyVal;
      }
      break;
    default:  // Default case is for the digits 1 through 9.
      if (tempString.length() < 29) {
        tempString = tempString + keyVal;
      }
      break;
    }
    
    clearDisplay = doClear;
    if (!displayString.equals(tempString)) {
      displayString = tempString;
      displayText.setText(displayString);
    }
  }
  
  /*
   * This method updates the value stored in memory.
   * The value is cleared in the updateDisplay method. 
   */
  private void updateMemory(final char keyPressed) {
    char keyVal = keyPressed;
    String tempString = new String();
    
    switch (keyVal) {
    case "S": // Save to Memory
      tempString = trimString(displayString);
      break;
      
    case "+": // Add to Memory
      if (memoryString.length() == 0) {
        tempString = trimString(displayString);
      } else {
        tempString = doCalc(memoryString, displayString, "+");
      }
      break;
    
    case "-": // Subtract from Memory
      if (memoryString.length() == 0) {
        if (displayString.startsWith("-")) {
          tempString = displayString.substring(1, displayString.length());
          tempString = trimString(tempString);
        } else if (displayString.equals("0.0") 
          || displayString.equals("0") 
          || displayString.equals("0.")
          || displayString.equals("-0.0")) {
          tempString = "0";
        } else {
          tempString = keyVal + displayString;
          tempString = trimString(displayString);
        }
      } else {
        tempString = doCalc(memoryString, displayString, "-");
      }
      break;
      
    default:  // Do nothing - this should never happen.
      break;
    }
    
    // Do not save invalid entries to memory.
    if (tempString.startsWith(ERROR_STRING)) {
      if (!displayString.equals(tempString)) {
        displayString = tempString;
        displayText.setText(displayString);
      }
    } else {
      memoryString = tempString;
    }
    clearDisplay = true;
  }
  /*
   * This method converts the operator and display strings to double values
   * and performs the calculation.
   */
  private String doCalc(final String valAString, final String valBString, 
      final char opChar) {
    String resultString = ERROR_STRING + NAN_STRING;
    Double valA = 0.0; 
    Double valB = 0.0;
    Double valAnswer = 0.0;
    
    // Make sure register strings are numbers
    if (valAString.length() > 0) {
      try {
        valA = Double.parseDouble(valAString);
      } catch (NumberFormatException e) {
        return resultString;
      }
    } else {
      return resultString;
    }
    
    if (opChar != "Q" && opChar != "I") {
      if (valBString.length() > 0) {
        try {
          valB = Double.parseDouble(valBString);
        } catch (NumberFormatException e) {
          return resultString;
        }
      } else {
        return resultString;
      }
    }
    
    switch (opChar) {
    case "Q": // Square Root
      valAnswer = Math.sqrt(valA);
      break;
      
    case "I": // Inverse
      valB = 1.0;
      valAnswer = valB/valA;
      break;
      
    case "+": // Addition
      valAnswer = valA + valB;
      break;
      
    case "-": // Subtraction
      valAnswer = valA - valB;
      break;
      
    case "/": // Division
      valAnswer = valA/valB;
      break;
      
    case "*": // Multiplication
      valAnswer = valA*valB;
      break;
      
    default:  // Do nothing - this should never happen
      break;
      
    }
    // Convert answer to string and format it before return.
    resultString = valAnswer.toString();
    resultString = trimString(resultString);
    return resultString;
  }
  
  /*
   * This method updates the operator and display strings, and the 
   * pending calculation flag.
   */
  private void updateCalc(char keyPressed) {
    char keyVal = keyPressed;
    String tempString = displayString;
    
    /*
     * If there is no display value, the keystroke is deemed invalid and 
     * nothing is done.
     */ 
    if (tempString.length() == 0) {
      return;
    }
    
    /* 
     * If there is no operator value, only calculation key presses are 
     * considered valid.  Check that the display value is valid and
     * if so, move the display value to the operator.  No calculation is done.
     */
    if (operatorString.length() == 0) {
      if (keyVal != "=") {
        tempString = trimString(tempString);
        if (tempString.startsWith(ERROR_STRING)) {
          clearDisplay = true;
          operatorString = "";
          calcChar = " ";
        } else {
          operatorString = tempString;
          calcChar = keyVal;
          clearDisplay = true;
        }
      }
      return;
    }
    
    // There is an operator and a display value, so do the calculation.
    displayString = doCalc(operatorString, tempString, calcChar);
    
    /* 
     * If "=" was pressed or result was invalid, reset pending calculation
     * flag and operator value.  Otherwise, set new calculation flag so 
     * calculations can be chained.
     */ 
    if (keyVal == "=" || displayString.startsWith(ERROR_STRING)) {
      calcChar = " ";
      operatorString = "";
    } else {
      calcChar = keyVal;
      operatorString = displayString;
    }
    
    // Set the clear display flag and show the result.
    clearDisplay = true;
    displayText.setText(displayString);
  }
  /*
   * This method formats a string.
   */
  private String trimString(final String newString) {
    String tempString = newString;
    
    // Value is not a number
    if (tempString.equals("NaN")) {
      tempString = ERROR_STRING + NAN_STRING;
      return tempString;
    }
    // Value is infinity
    if (tempString.equals("Infinity") || tempString.equals("-Infinity")) {
      tempString = ERROR_STRING + INFINITY_STRING;
      return tempString;
    }
    // Value is -0
    if (tempString.equals(-0.0)) {
      tempString = "0";
      return tempString;
    }
    // Trim unnecessary trailing .0
    if (tempString.endsWith(".0")) {
      tempString = tempString.substring(0, tempString.length() - 2);
    }
    // String is too long to display
    if (tempString.length() > 28) {
      tempString = ERROR_STRING + LONG_STRING;
    }
    
    return tempString;
  }
}





Engine for Calculator by Michael Schmidt

/**
  The following code is from Michael Schmidt(MichaelMSchmidt (at) msn.ru).
  
  The code is published under BSD license.
  
  Thanks for the input from Michael Schmidt.
*/

package us.mschmidt.komo;
/**
 * This is a generic calculator engine that includes a memory register, some
 * numeric conversions (square root, inverse), and the ability to chain 
 * calculations.  To use it, create a GUI shell with a display and some means 
 * of entering keystrokes (Buttons work well).  Invoke this engine from the GUI
 * shell using the constructor method, which requires that the display size 
 * (String length) be set.  Obtain the initial display, if desired, with the 
 * getDisplayString() method.  Subsequently, the display string is returned by 
 * the setInput() method when an operation (character) is passed to the engine.
 * <p>
 * The constructor requires a display size to be set.  The minimum is three
 * characters, with at least 22 characters recommended and recommended  size 
 * being 30 characters.  
 *  
 * @author   Michael Schmidt
 * @version  1.1
 */
class CalcEngine {
  
  // Instantiatee constants
  private final String calcChars;
  private final String convChars;
  private final String dispChars;
  private final String memChars;
  private final String errChars;
  
  // Instantiate variables
  // The error messages
  private String nanErr = new String();
  private String tooLongErr = new String();
  private String infinityErr = new String();
  private int displaySize;
  
  // The calculator "registers"
  private String displayString = new String();
  private String memoryString = new String();
  private String operatorString = new String();
  
  // A boolean to indicate if display will be cleared on the next key entry
  private boolean clearDisplay;
  
  // A character to store the pending calculation
  private char calcChar;
  /**
   * Constructor to create the calculator engine object.
   * 
   * @param displaySizeVal  The GUI display size, minimum of 3 and
   *                         recommended size of 30
   */
  CalcEngine(final int displaySizeVal) {
    final int minSize = 3;
    // Initialize operation characters
    calcChars = "+-/*=";
    convChars = "IQ";
    dispChars = "BCERS.0123456789";
    memChars = "DLMP";
    errChars = "EINO*";
    
    // Set initial display and internal variables
    displayString = "0.";
    clearDisplay = true;
    calcChar = " ";
    
    // Set the display size
    displaySize = minSize;
    if (displaySizeVal > minSize) {
      displaySize = displaySizeVal;
    } 
    // Customize the error strings based on the display size
    setErrorStrings();
  }
  
  /**
   * Customizes the error messages based on the display size.
   */
  private void setErrorStrings() {
    
    // Initialize constants for display size thresholds
    final int infLength = 8;
    final int nanLength = 12;
    final int tooLongLength = 15;
    final int fullLength = 22;
    
    // Set default strings for optimal display size
    nanErr = "Not a Number";
    tooLongErr = "Number too long";
    infinityErr = "Infinity";
    
    if (displaySize < infLength) {
      nanErr = "NaN";
      tooLongErr = "***";
      infinityErr = "Inf";
    } else if (displaySize < nanLength) {
      nanErr = "NaN";
      tooLongErr = "Overflow";
    } else if (displaySize < tooLongLength) {
      tooLongErr = "Overflow";
    } else if (displaySize >= fullLength) {
      nanErr = "ERROR: " + nanErr;
      tooLongErr = "ERROR: " + tooLongErr; 
      infinityErr = "ERROR: " + infinityErr; 
    }
  }
  
  /**
   * Provides the display string to the calling class.  Used after the  
   * engine is instantiated.  Subsequently, the display string is returned by
   * the setInput() method.
   * 
   * @return  the display String
   */
  public String getDisplayString() {
    return displayString;
  }
  
  /**
   * Allows entry of keystrokes from the calling class.
   * 
   * @param keyVal  A designation of the key pressed by the user using the
   *                 code.  
   *                 Display codes: B backspace, C clear, E clear entry,
   *                 R recall memory to display, S change sign, . decimal, 
   *                 0-9 numeric entries.
   *                 Memory codes: D clear memory, M subtract from memory, 
   *                 L store in memory, P add to memory. 
   *                 Calculate codes: I inverse, Q square root, S subtract.
   * @return        boolean true if the display has changed, false if not
   */
  public String setInput(final char keyVal) {
    final char keyChar = keyVal;
    if (dispChars.indexOf(keyChar) != -1) {
      doDisplayOp(keyChar);
    } else if (convChars.indexOf(keyChar) != -1) {
      doConvertOp(keyChar);
    } else if (memChars.indexOf(keyChar) != -1) {
      doMemoryOp(keyChar);
    } else if (calcChars.indexOf(keyChar) != -1) {
      doCalcOp(keyChar);
    }
    return displayString;
  }
  
  /**
   * Clears the display when 1) the clearDisplay flag had previously been set
   * to "true" and 2) a new character or calculation operation is entered.
   */
  private void doDisplayClear() {
    if (clearDisplay) {
      displayString = "";
      clearDisplay = false;
    }
  }
  /**
   * Performs number conversion operations: inverse (1/X) and square root.
   * 
   * @param keyVal  a designation of the key pressed by the user
   */
  private void doConvertOp(final char keyVal) {
    final char opChar = keyVal;
    String tempString = doConvert(displayString, opChar);
    clearDisplay = true;
    if (tempString.length() > 0) {
      displayString = tempString;
    }
  }
  /**
   * Performs display operations.  Simple assignment operations are performed 
   * in this method while more complex operations are performed in sub-methods. 
   * 
   * @param keyVal  a designation of which key was pressed by the user
   */
  private void doDisplayOp(final char keyVal) {
    final char opChar = keyVal;
    switch (opChar) {
      case "B":  // Backspace
        doBackspace();
        break;
      case "C":  // Clear.  Note use of dropthrough
        operatorString = "";
        calcChar = " ";
      case "E":  // Clear Entry
        displayString = "0.";
        clearDisplay = true;
        break;

      case "R":  // Recall Memory to Display
        displayString = memoryString;
        break;
      case "S":  // Change Sign
        doChangeSign();
        break;
      case ".":  // Can"t have two decimal points.
        doDecimal(opChar);
        break;
      case "0":  // Don"t want 00 to be entered.
        doZero(opChar);
        break;
      default:  // Default case is for the digits 1 through 9.
        doAddChar(opChar);
        break;
    }
  }
  
  /**
   * Performs backspace operation.
   */
  private void doBackspace() {
    if (isError(displayString)) {
      return;
    }
    if (displayString.length() > 0) {
      displayString = displayString.substring(
          0, displayString.length() - 1);
    }
  }
  /**
   * Performs operation to add a character to the display.
   * 
   * @param c  the character to add
   */
  private void doAddChar(final char c) {
    if (isError(displayString)) {
      return;
    }
    if (displayString.length() < displaySize) {
      doDisplayClear();
      displayString += c;
    }
  }
  
  /**
   * Performs sign change operation.
   */
  private void doChangeSign() {
    if (isError(displayString)) {
      return;
    }
    if ("-" == displayString.charAt(0)) {
      displayString = displayString.substring(
          1, displayString.length());
    } else if (displayString.length() < displaySize) {
      displayString = "-" + displayString;
    }
  }
  
  /**
   * Performs operation when decimal is pressed.
   * 
   * @param c  the decimal character
   */
  private void doDecimal(final char c) {
    if (isError(displayString)) {
      return;
    }
    if (displayString.indexOf(".") == -1 
        && displayString.length() < displaySize) {
      doDisplayClear();
      displayString += c;
    }
  }
  
  /**
   * Performs operation when "0" is pressed.
   * 
   * @param c  the zero operation character
   */
  private void doZero(final char c) {
    if (isError(displayString)) {
      return;
    }
    if (!displayString.equals("0") && displayString.length() <  displaySize) {
      doDisplayClear();
      displayString += c;
    }
  }
  /**
   * Updates the value stored in the memory register.  Simple assignment 
   * operations are performed here while more complex operations are performed 
   * in sub-methods.
   * 
   * @param keyVal  a designation of which key was pressed by the user
   */
  private void doMemoryOp(final char keyVal) {
    final char opChar = keyVal;
    switch (opChar) {
      case "D":  // Clear Memory
        memoryString = "";
        break;
        
      case "L":  // Save to Memory
        assignMemoryString(trimString(displayString));
        break;
      case "M":  // Subtract from Memory
        subtractFromMemory();
        break;
      case "P":  // Add to Memory
        addToMemory();
        break;
      default:  // Do nothing - this should never happen.
        break;
    }
    clearDisplay = true;
  }
  
  /**
   * Performs the operation to add a display entry to the number in the
   * memory register.
   */
  private void addToMemory() {
    String tempString = new String();
    if (0 == memoryString.length()) {
      tempString = trimString(displayString);
    } else {
      tempString = doComputation(memoryString, displayString, "+");
    }
    assignMemoryString(tempString);
  }
  
  /**
   * Performs the operation to subtract a display entry from the number in the
   * memory register.
   */
  private void subtractFromMemory() {
    String tempString = new String();
    if (0 == memoryString.length()) {
      tempString = doComputation("0", displayString, "-");
    } else {
      tempString = doComputation(memoryString, displayString, "-");
    }
    assignMemoryString(tempString);
  }
  
  /**
   * Checks if String is valid and, if so, assigns it to the memory register.
   * 
   * @param s  the String to assign
   */
  private void assignMemoryString(final String s) {
    if (isError(s)) {
      displayString = s;
    } else {
      memoryString = s;
    }
  }
  /**
   * Guides 2-number calculations.  Updates the operator string, display 
   * string, and the pending calculation flag.  Performs calculation if 
   * possible.
   * 
   * @param keyVal  a designation of which key was pressed byt he user
   */
  private void doCalcOp(final char keyVal) {
    final char opChar = keyVal;
    // If there is no display value, the keystroke is deemed invalid and 
    // nothing is done.
    if (0 == displayString.length()) {
      return;
    }
    // If there is no operator value, "=" key presses are considered 
    // invalid.  If a calculation key is pressed, check that the display 
    // value is valid and if so, copy the display value to the operator.  
    // No calculation is done.
    if (0 == operatorString.length()) {
      if ("=" != opChar) {
        if (isError(displayString)) {
          calcChar = " ";
        } else {
          operatorString = displayString;
          calcChar = opChar;
        }
        clearDisplay = true;
      }
      return;
    }
    
    // There are operator and display values, so do the pending calculation.
    displayString = doComputation(operatorString, displayString, calcChar);
    // If "=" was pressed or result was invalid, reset pending calculation
    // flag and operator value.  Otherwise, set new calculation flag so 
    // calculations can be chained.
    if (("=" == opChar) || isError(displayString)) {
      calcChar = " ";
      operatorString = "";
    } else {
      calcChar = opChar;
      operatorString = displayString;
    }
    // Set the clear display flag
    clearDisplay = true;
  }
  
  /**
   * Performs the computations.
   * 
   * @param numStringA  the displayed value
   * @param numStringB  the stored value
   * @param opVal        the operation to be performed
   * @return            the solution as a string variable
   */
  private String doComputation(final String numStringA, 
      final String numStringB, final char opVal) {
    String valStringA = numStringA;
    String valStringB = numStringB;
    char opChar = opVal;
    Double valA = 0.0; 
    Double valB = 0.0;
    Double valAnswer = 0.0;
    // Make sure register strings are numbers
    if (valStringA.length() > 0 && valStringB.length() > 0) {
      try {
        valA = Double.parseDouble(numStringA);
        valB = Double.parseDouble(numStringB);
      } catch (final NumberFormatException e) {
        return nanErr;
      }
    } else {
      return "";
    }
    switch (opChar) {
      case "+":  // Addition
        valAnswer = valA + valB;
        break;
      case "-":  // Subtraction
        valAnswer = valA - valB;
        break;
      case "/":  // Division
        valAnswer = valA / valB;
        break;
      case "*":  // Multiplication
        valAnswer = valA * valB;
        break;
      default:  // Do nothing - this should never happen
        break;
    }
    // Convert answer to properly formatted string.
    return trimString(valAnswer.toString());
  }
  
  /**
   * Performs number conversion computations.
   * 
   * @param numString  the number to be converted
   * @param opVal    designation of the operation to be performed
   * @return      the converted value
   */
  private String doConvert(final String numString, final char opVal) {
    char opChar = opVal;
    String valString = numString;
    Double valA = 0.0; 
    Double valAnswer = 0.0;
    // Make sure String is a number.  If it is zero-length, assume the
    // keystroke was inadvertent and return "".
    if (valString.length() > 0) {
      try {
        valA = Double.parseDouble(valString);
      } catch (final NumberFormatException e) {
        return nanErr;
      }
    } else {
      return "";
    }
    switch (opChar) {
      case "Q":  // Square Root
        valAnswer = Math.sqrt(valA);
        break;
      case "I":  // Inverse
        valAnswer = 1.0 / valA;
        break;
      default:  // Do nothing - this should never happen
        break;
    }
    // Return properly formatted result String.
    return trimString(valAnswer.toString());
  }
  /**
   * Formats String to be displayed.
   * 
   * @param stringVal  a new string to be displayed
   * @return          the properly formatted and trimmed string
   */
  private String trimString(final String stringVal) {
    String returnString = stringVal;
    // Check if value is Not a Number
    if (returnString.equals("NaN")) {
      return nanErr;
    }
    
    // Check if value is infinity
    if (returnString.endsWith("Infinity")) {
      return infinityErr;
    }
    
    // Check if value is -0
    if (returnString.equals("-0.0")) {
      return "0";
    }
    
    // Trim unnecessary trailing .0
    if (returnString.endsWith(".0")) {
      returnString = returnString.substring(0, returnString.length() - 2);
    }
    
    // Check if string is too long to display
    if (returnString.length() > displaySize) {
      return tooLongErr;
    }
    return returnString;
  }
  
  /**
   * Tests if the String is an error message.
   * 
   * @param s  the String to be tested
   * @return  boolean true if this is an error message, false if not
   */
  private boolean isError(final String s) {
    String testString = s;
    if (0 == testString.length()) {
      return false;
    }
    char c = testString.charAt(0);
    return ((errChars.indexOf(c) == -1)) ? false : true;
  }
  /**
   * Override to provide useful information when the class toString method is
   * called.
   * 
   * @return  the information String
   * @see     java.lang.Object#toString()
   */
  @Override
  public final String toString() {
    return "komo.CalcEngine is the engine portion of the calculator utility";
  }
}