FileDocCategorySizeDatePackage
Logic.javaAPI DocAndroid 1.5 API6616Wed May 06 22:42:42 BST 2009com.android.calculator2

Logic.java

/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.calculator2;

import android.view.View;
import android.view.KeyEvent;
import android.widget.Button;
import android.widget.EditText;
import android.content.Context;
import android.content.res.Configuration;

import org.javia.arity.Symbols;
import org.javia.arity.SyntaxException;
import org.javia.arity.Util;

class Logic {
    private CalculatorDisplay mDisplay;
    private Symbols mSymbols = new Symbols();
    private History mHistory;
    private String  mResult = "";
    private Button mEqualButton;
    private final String mEnterString;
    private boolean mIsError = false;
    private final boolean mOrientationPortrait;
    private final int mLineLength;

    private static final int LINE_LENGTH_PORTRAIT = 14;
    private static final int LINE_LENGTH_LANDSCAPE = 21;
    private static final String INFINITY_UNICODE = "\u221e";

    // the two strings below are the result of Double.toString() for Infinity & NaN
    // they are not output to the user and don't require internationalization
    private static final String INFINITY = "Infinity"; 
    private static final String NAN      = "NaN";

    static final char MINUS = '\u2212';

    private final String mErrorString;

    Logic(Context context, History history, CalculatorDisplay display, Button equalButton) {
        mErrorString = context.getResources().getString(R.string.error);
        mOrientationPortrait = context.getResources().getConfiguration().orientation
            == Configuration.ORIENTATION_PORTRAIT;
        mLineLength = mOrientationPortrait ? LINE_LENGTH_PORTRAIT : LINE_LENGTH_LANDSCAPE;

        try {
            // in calculator we use log() for base-10,
            // unlike in arity-lib where log() is natural logarithm
            mSymbols.define(mSymbols.compileWithName("log(x)=log10(x)"));
        } catch (SyntaxException e) {
            throw new Error("" + e); //never
        }
        mHistory = history;
        mDisplay = display;
        mDisplay.setLogic(this);
        mEqualButton = equalButton;
        mEnterString = context.getText(R.string.enter).toString();

        clearWithHistory(false);
    }

    boolean eatHorizontalMove(boolean toLeft) {
        EditText editText = mDisplay.getEditText();
        int cursorPos = editText.getSelectionStart();
        return toLeft ? cursorPos == 0 : cursorPos >= editText.length(); 
    }

    private String getText() {
        return mDisplay.getText().toString();
    }

    void insert(String delta) {
        mDisplay.insert(delta);
    }

    private void setText(CharSequence text) {
        mDisplay.setText(text, CalculatorDisplay.Scroll.UP);
    }

    private void clearWithHistory(boolean scroll) {
        mDisplay.setText(mHistory.getText(), 
                         scroll ? CalculatorDisplay.Scroll.UP : CalculatorDisplay.Scroll.NONE);
        mResult = "";
        mIsError = false;
    }

    private void clear(boolean scroll) {
        mDisplay.setText("", scroll ? CalculatorDisplay.Scroll.UP : CalculatorDisplay.Scroll.NONE);
        cleared();
    }

    void cleared() {
        mResult = "";
        mIsError = false;
        updateHistory();
    }

    boolean acceptInsert(String delta) {
        String text = getText();
        return !mIsError &&
            (!mResult.equals(text) || 
             isOperator(delta) ||
             mDisplay.getSelectionStart() != text.length());
    }

    void onDelete() {
        if (getText().equals(mResult) || mIsError) {
            clear(false);
        } else {
            mDisplay.dispatchKeyEvent(new KeyEvent(0, KeyEvent.KEYCODE_DEL));
            mResult = "";
        }
    }

    void onClear() {
        clear(false);
    }

    void onEnter() {
        String text = getText();
        if (text.equals(mResult)) {
            clearWithHistory(false); //clear after an Enter on result
        } else {
            mHistory.enter(text);
            try {
                mResult = evaluate(text);
            } catch (SyntaxException e) {
                mIsError = true;
                mResult = mErrorString;
            }
            if (text.equals(mResult)) {
                //no need to show result, it is exactly what the user entered
                clearWithHistory(true);
            } else {
                setText(mResult);
                //mEqualButton.setText(mEnterString);
            }
        }
    }

    void onUp() {
        String text = getText();
        if (!text.equals(mResult)) {
            mHistory.update(text);
        }
        if (mHistory.moveToPrevious()) {
            mDisplay.setText(mHistory.getText(), CalculatorDisplay.Scroll.DOWN);
        }
    }

    void onDown() {
        String text = getText();
        if (!text.equals(mResult)) {
            mHistory.update(text);
        }
        if (mHistory.moveToNext()) {
            mDisplay.setText(mHistory.getText(), CalculatorDisplay.Scroll.UP);
        }
    }

    void updateHistory() {
        mHistory.update(getText());
    }

    private static final int ROUND_DIGITS = 1;
    String evaluate(String input) throws SyntaxException {
        if (input.trim().equals("")) {
            return "";
        }

        // drop final infix operators (they can only result in error)
        int size = input.length();
        while (size > 0 && isOperator(input.charAt(size - 1))) {
            input = input.substring(0, size - 1);
            --size;
        }

        String result = Util.doubleToString(mSymbols.eval(input), mLineLength, ROUND_DIGITS);
        if (result.equals(NAN)) { // treat NaN as Error
            mIsError = true;
            return mErrorString;
        }
        return result.replace('-', MINUS).replace(INFINITY, INFINITY_UNICODE);
    }

    static boolean isOperator(String text) {
        return text.length() == 1 && isOperator(text.charAt(0));
    }

    static boolean isOperator(char c) {
        //plus minus times div
        return "+\u2212\u00d7\u00f7/*".indexOf(c) != -1;
    }
}