/******************************************************************************* * Copyright (c) 2012 Laurent CARON * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Laurent CARON (laurent.caron at gmail dot com) - Initial API and implementation *******************************************************************************/ package org.mihalis.opal.calculator; import org.mihalis.opal.utils.ResourceManager; /** * This is the calculator engine */ class CalculatorEngine { static final String OPERATOR_PLUS = "+"; static final String OPERATOR_MINUS = "-"; static final String OPERATOR_MULTIPLY = "*"; static final String OPERATOR_DIVIDE = "/"; static final String CHARACTER_ZERO = "0"; static final String DOT_CHARACTER = "."; static final String EMPTY_STRING = ""; private enum DISPLAY_MODE { INPUT, RESULT, ERROR } private DISPLAY_MODE displayMode; private boolean clearOnNextDigit; private double lastNumber; private String lastOperator; private final CalculatorButtonsComposite composite; /** * Constructor * * @param calculator calculator widget associated to this engine */ CalculatorEngine(final CalculatorButtonsComposite composite) { this.composite = composite; } /** * Add a decimal point */ void addDecimalPoint() { this.displayMode = DISPLAY_MODE.INPUT; if (this.clearOnNextDigit) { setDisplayString(EMPTY_STRING); } final String inputString = getDisplayString(); if (inputString.indexOf(DOT_CHARACTER) < 0) { setDisplayString(inputString + DOT_CHARACTER); } } /** * @param value value to display */ private void setDisplayString(final String value) { this.composite.getDisplayArea().setText(value); this.composite.fireModifyListeners(); } /** * @param digit digit to add */ void addDigitToDisplay(final int digit) { if (this.clearOnNextDigit) { setDisplayString(EMPTY_STRING); } String inputString = getDisplayString(); if (inputString.indexOf(CHARACTER_ZERO) == 0) { inputString = inputString.substring(1); } if (!inputString.equals(CHARACTER_ZERO) || digit > 0) { setDisplayString(inputString + digit); } this.displayMode = DISPLAY_MODE.INPUT; this.clearOnNextDigit = false; } /** * Clear content */ void clearWholeContent() { setDisplayString(CHARACTER_ZERO); this.lastOperator = CHARACTER_ZERO; this.lastNumber = 0; this.displayMode = DISPLAY_MODE.INPUT; this.clearOnNextDigit = true; } /** * Clear result */ void clearResult() { setDisplayString(CHARACTER_ZERO); this.clearOnNextDigit = true; this.displayMode = DISPLAY_MODE.INPUT; } /** * Process backspace */ void processBackSpace() { if (this.displayMode != DISPLAY_MODE.ERROR) { setDisplayString(getDisplayString().substring(0, getDisplayString().length() - 1)); if (getDisplayString().length() < 1) { setDisplayString(CHARACTER_ZERO); } } } /** * @return the displayed value as string */ private String getDisplayString() { return this.composite.getDisplayArea().getText(); } /** * Process equals operation */ void processEquals() { double result = 0; if (this.displayMode != DISPLAY_MODE.ERROR) { try { result = processLastOperator(); displayResult(result); } catch (final DivideByZeroException e) { displayErrorMessage(ResourceManager.CALCULATOR_DIVIDE_BY_ZERO); } this.lastOperator = CHARACTER_ZERO; } } /** * @return the result of the last operation * @throws DivideByZeroException */ private double processLastOperator() throws DivideByZeroException { double result = 0; final double numberInDisplay = getDisplayedNumber(); if (this.lastOperator.equals(OPERATOR_DIVIDE)) { if (numberInDisplay == 0) { throw new DivideByZeroException(); } result = this.lastNumber / numberInDisplay; } if (this.lastOperator.equals(OPERATOR_MULTIPLY)) { result = this.lastNumber * numberInDisplay; } if (this.lastOperator.equals(OPERATOR_MINUS)) { result = this.lastNumber - numberInDisplay; } if (this.lastOperator.equals(OPERATOR_PLUS)) { result = this.lastNumber + numberInDisplay; } return result; } /** * @param result result to display */ private void displayResult(final double result) { if (Math.floor(result) == result) { setDisplayString(Integer.toString((int) result)); } else { setDisplayString(Double.toString(result)); } this.lastNumber = result; this.displayMode = DISPLAY_MODE.RESULT; this.clearOnNextDigit = true; } /** * @param errorMessage error message */ private void displayErrorMessage(final String errorMessage) { setDisplayString(ResourceManager.getLabel(errorMessage)); this.lastNumber = 0; this.displayMode = DISPLAY_MODE.ERROR; this.clearOnNextDigit = true; } /** * Process 1/x operation */ void processInverseOperation() { if (this.displayMode != DISPLAY_MODE.ERROR) { try { if (getDisplayedNumber() == 0) { displayErrorMessage(ResourceManager.CALCULATOR_DIVIDE_BY_ZERO); return; } final double result = 1 / getDisplayedNumber(); displayResult(result); } catch (final Exception ex) { displayErrorMessage(ResourceManager.CALCULATOR_DIVIDE_BY_ZERO); this.displayMode = DISPLAY_MODE.ERROR; } } } /** * @return the displayed number */ private double getDisplayedNumber() { final String input = getDisplayString(); return Double.parseDouble(input); } /** * @param operator operation to process */ void processOperation(final String operator) { if (this.displayMode != DISPLAY_MODE.ERROR) { final double numberInDisplay = getDisplayedNumber(); if (this.lastOperator != null && !this.lastOperator.equals(CHARACTER_ZERO)) { try { final double result = processLastOperator(); displayResult(result); this.lastNumber = result; } catch (final DivideByZeroException e) { } } else { this.lastNumber = numberInDisplay; } this.clearOnNextDigit = true; this.lastOperator = operator; } } /** * Process percentage operation */ void processPerCentageOperation() { if (this.displayMode != DISPLAY_MODE.ERROR) { try { final double result = getDisplayedNumber() / 100; displayResult(result); } catch (final Exception ex) { displayErrorMessage(ResourceManager.CALCULATOR_INVALID_VALUE); this.displayMode = DISPLAY_MODE.ERROR; } } } /** * Process +/- operation */ void processSignChange() { if (this.displayMode == DISPLAY_MODE.INPUT) { final String input = getDisplayString(); if (input.length() > 0 && !input.equals(CHARACTER_ZERO)) { if (input.indexOf(OPERATOR_MINUS) == 0) { setDisplayString(input.substring(1)); } else { setDisplayString(OPERATOR_MINUS + input); } } } else if (this.displayMode == DISPLAY_MODE.RESULT) { final double numberInDisplay = getDisplayedNumber(); if (numberInDisplay != 0) { displayResult(-numberInDisplay); } } } /** * Process square root operation */ void processSquareRootOperation() { if (this.displayMode != DISPLAY_MODE.ERROR) { try { if (getDisplayString().indexOf(OPERATOR_MINUS) == 0) { displayErrorMessage(ResourceManager.CALCULATOR_INVALID_VALUE); } final double result = Math.sqrt(getDisplayedNumber()); displayResult(result); } catch (final Exception ex) { displayErrorMessage(ResourceManager.CALCULATOR_INVALID_VALUE); this.displayMode = DISPLAY_MODE.ERROR; } } } }