Java/SWT JFace Eclipse/Calculator
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";
}
}