1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.calculator2; 18 19 import android.view.KeyEvent; 20 import android.widget.Button; 21 import android.widget.EditText; 22 import android.content.Context; 23 24 import org.javia.arity.Symbols; 25 import org.javia.arity.SyntaxException; 26 import org.javia.arity.Util; 27 28 class Logic { 29 private CalculatorDisplay mDisplay; 30 private Symbols mSymbols = new Symbols(); 31 private History mHistory; 32 private String mResult = ""; 33 private boolean mIsError = false; 34 private int mLineLength = 0; 35 36 private static final String INFINITY_UNICODE = "\u221e"; 37 38 // the two strings below are the result of Double.toString() for Infinity & NaN 39 // they are not output to the user and don't require internationalization 40 private static final String INFINITY = "Infinity"; 41 private static final String NAN = "NaN"; 42 43 static final char MINUS = '\u2212'; 44 45 private final String mErrorString; 46 47 Logic(Context context, History history, CalculatorDisplay display, Button equalButton) { 48 mErrorString = context.getResources().getString(R.string.error); 49 mHistory = history; 50 mDisplay = display; 51 mDisplay.setLogic(this); 52 53 clearWithHistory(false); 54 } 55 56 void setLineLength(int nDigits) { 57 mLineLength = nDigits; 58 } 59 60 boolean eatHorizontalMove(boolean toLeft) { 61 EditText editText = mDisplay.getEditText(); 62 int cursorPos = editText.getSelectionStart(); 63 return toLeft ? cursorPos == 0 : cursorPos >= editText.length(); 64 } 65 66 private String getText() { 67 return mDisplay.getText().toString(); 68 } 69 70 void insert(String delta) { 71 mDisplay.insert(delta); 72 } 73 74 private void setText(CharSequence text) { 75 mDisplay.setText(text, CalculatorDisplay.Scroll.UP); 76 } 77 78 private void clearWithHistory(boolean scroll) { 79 mDisplay.setText(mHistory.getText(), 80 scroll ? CalculatorDisplay.Scroll.UP : CalculatorDisplay.Scroll.NONE); 81 mResult = ""; 82 mIsError = false; 83 } 84 85 private void clear(boolean scroll) { 86 mDisplay.setText("", scroll ? CalculatorDisplay.Scroll.UP : CalculatorDisplay.Scroll.NONE); 87 cleared(); 88 } 89 90 void cleared() { 91 mResult = ""; 92 mIsError = false; 93 updateHistory(); 94 } 95 96 boolean acceptInsert(String delta) { 97 String text = getText(); 98 return !mIsError && 99 (!mResult.equals(text) || 100 isOperator(delta) || 101 mDisplay.getSelectionStart() != text.length()); 102 } 103 104 void onDelete() { 105 if (getText().equals(mResult) || mIsError) { 106 clear(false); 107 } else { 108 mDisplay.dispatchKeyEvent(new KeyEvent(0, KeyEvent.KEYCODE_DEL)); 109 mResult = ""; 110 } 111 } 112 113 void onClear() { 114 clear(false); 115 } 116 117 void onEnter() { 118 String text = getText(); 119 if (text.equals(mResult)) { 120 clearWithHistory(false); //clear after an Enter on result 121 } else { 122 mHistory.enter(text); 123 try { 124 mResult = evaluate(text); 125 } catch (SyntaxException e) { 126 mIsError = true; 127 mResult = mErrorString; 128 } 129 if (text.equals(mResult)) { 130 //no need to show result, it is exactly what the user entered 131 clearWithHistory(true); 132 } else { 133 setText(mResult); 134 //mEqualButton.setText(mEnterString); 135 } 136 } 137 } 138 139 void onUp() { 140 String text = getText(); 141 if (!text.equals(mResult)) { 142 mHistory.update(text); 143 } 144 if (mHistory.moveToPrevious()) { 145 mDisplay.setText(mHistory.getText(), CalculatorDisplay.Scroll.DOWN); 146 } 147 } 148 149 void onDown() { 150 String text = getText(); 151 if (!text.equals(mResult)) { 152 mHistory.update(text); 153 } 154 if (mHistory.moveToNext()) { 155 mDisplay.setText(mHistory.getText(), CalculatorDisplay.Scroll.UP); 156 } 157 } 158 159 void updateHistory() { 160 mHistory.update(getText()); 161 } 162 163 private static final int ROUND_DIGITS = 1; 164 String evaluate(String input) throws SyntaxException { 165 if (input.trim().equals("")) { 166 return ""; 167 } 168 169 // drop final infix operators (they can only result in error) 170 int size = input.length(); 171 while (size > 0 && isOperator(input.charAt(size - 1))) { 172 input = input.substring(0, size - 1); 173 --size; 174 } 175 176 String result = Util.doubleToString(mSymbols.eval(input), mLineLength, ROUND_DIGITS); 177 if (result.equals(NAN)) { // treat NaN as Error 178 mIsError = true; 179 return mErrorString; 180 } 181 return result.replace('-', MINUS).replace(INFINITY, INFINITY_UNICODE); 182 } 183 184 static boolean isOperator(String text) { 185 return text.length() == 1 && isOperator(text.charAt(0)); 186 } 187 188 static boolean isOperator(char c) { 189 //plus minus times div 190 return "+\u2212\u00d7\u00f7/*".indexOf(c) != -1; 191 } 192 } 193