symbolic calculations added

This commit is contained in:
serso
2011-10-24 23:11:50 +04:00
parent a6caf0be93
commit 5abb206529
16 changed files with 270 additions and 123 deletions

View File

@@ -92,6 +92,13 @@ public class CalculatorActivity extends Activity implements FontSizeAdjuster, Sh
((DragButton) findViewById(R.id.leftButton)).setOnDragListener(toPositionOnDragListener);
dpclRegister.addListener(toPositionOnDragListener);
final DragButton equalsButton = (DragButton) findViewById(R.id.equalsButton);
if (equalsButton != null) {
final SimpleOnDragListener evalOnDragListener = new SimpleOnDragListener(new EvalDragProcessor(calculatorModel), dragPreferences);
equalsButton.setOnDragListener(evalOnDragListener);
dpclRegister.addListener(evalOnDragListener);
}
CalculatorEngine.instance.reset(this, preferences);
preferences.registerOnSharedPreferenceChangeListener(this);
@@ -389,10 +396,7 @@ public class CalculatorActivity extends Activity implements FontSizeAdjuster, Sh
}
calculatorModel = CalculatorModel.instance.init(this, preferences, CalculatorEngine.instance);
AndroidMessageRegistry.instance.init(this);
this.calculatorModel.evaluate();
}
@Override

View File

@@ -12,6 +12,7 @@ import android.util.AttributeSet;
import android.util.Log;
import android.widget.TextView;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.calculator.model.ParseException;
import org.solovyev.android.calculator.model.TextProcessor;
@@ -24,6 +25,9 @@ public class CalculatorDisplay extends TextView {
private boolean valid = true;
@NotNull
private JsclOperation jsclOperation = JsclOperation.numeric;
@NotNull
private final static TextProcessor<TextHighlighter.Result> textHighlighter = new TextHighlighter(Color.WHITE, true);
@@ -47,6 +51,15 @@ public class CalculatorDisplay extends TextView {
this.valid = valid;
}
public void setJsclOperation(@NotNull JsclOperation jsclOperation) {
this.jsclOperation = jsclOperation;
}
@NotNull
public JsclOperation getJsclOperation() {
return jsclOperation;
}
@Override
public void setText(CharSequence text, BufferType type) {
super.setText(text, type);

View File

@@ -6,7 +6,7 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.jscl.JsclOperation;
/**
* User: serso
@@ -18,45 +18,49 @@ public class CalculatorDisplayHistoryState {
private boolean valid = true;
@NotNull
private final EditorHistoryState editorHistoryState;
private EditorHistoryState editorHistoryState;
public CalculatorDisplayHistoryState() {
this.editorHistoryState = new EditorHistoryState();
@NotNull
private JsclOperation jsclOperation;
private CalculatorDisplayHistoryState() {
}
public CalculatorDisplayHistoryState(boolean valid) {
this.editorHistoryState = new EditorHistoryState();
this.valid = valid;
}
@NotNull
public static CalculatorDisplayHistoryState newInstance(@NotNull CalculatorDisplay display) {
final CalculatorDisplayHistoryState result = new CalculatorDisplayHistoryState();
public CalculatorDisplayHistoryState(int cursorPosition, @Nullable String text, boolean valid) {
this.editorHistoryState = new EditorHistoryState(cursorPosition, text);
this.valid = valid;
result.editorHistoryState = EditorHistoryState.newInstance(display);
result.valid = display.isValid();
result.jsclOperation = display.getJsclOperation();
return result;
}
public boolean isValid() {
return valid;
}
public void setValid(boolean valid) {
this.valid = valid;
}
@NotNull
public EditorHistoryState getEditorHistoryState() {
return editorHistoryState;
}
@NotNull
public JsclOperation getJsclOperation() {
return jsclOperation;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof CalculatorDisplayHistoryState)) return false;
if (o == null || getClass() != o.getClass()) return false;
CalculatorDisplayHistoryState that = (CalculatorDisplayHistoryState) o;
if (valid != that.valid) return false;
if (editorHistoryState != null ? !editorHistoryState.equals(that.editorHistoryState) : that.editorHistoryState != null)
return false;
if (!editorHistoryState.equals(that.editorHistoryState)) return false;
if (jsclOperation != that.jsclOperation) return false;
return true;
}
@@ -64,7 +68,8 @@ public class CalculatorDisplayHistoryState {
@Override
public int hashCode() {
int result = (valid ? 1 : 0);
result = 31 * result + (editorHistoryState != null ? editorHistoryState.hashCode() : 0);
result = 31 * result + editorHistoryState.hashCode();
result = 31 * result + jsclOperation.hashCode();
return result;
}
@@ -72,6 +77,7 @@ public class CalculatorDisplayHistoryState {
public String toString() {
return "CalculatorDisplayHistoryState{" +
"valid=" + valid +
"jsclOperation=" + jsclOperation +
", editorHistoryState=" + editorHistoryState +
'}';
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator;
/**
* User: serso
* Date: 10/24/11
* Time: 9:55 PM
*/
public interface CalculatorEngineControl {
void evaluate();
void simplify();
}

View File

@@ -12,6 +12,7 @@ import android.os.Bundle;
import android.view.*;
import android.widget.*;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.common.utils.Filter;
import org.solovyev.common.utils.FilterRule;
import org.solovyev.common.utils.FilterRulesChain;
@@ -53,14 +54,16 @@ public class CalculatorHistoryActivity extends ListActivity {
final int position,
final long id) {
final CalculatorHistoryState historyState = (CalculatorHistoryState) parent.getItemAtPosition(position);
CalculatorModel.instance.doTextOperation(new CalculatorModel.TextOperation() {
@Override
public void doOperation(@NotNull EditText editor) {
final EditorHistoryState editorState = ((CalculatorHistoryState) parent.getItemAtPosition(position)).getEditorState();
final EditorHistoryState editorState = historyState.getEditorState();
editor.setText(editorState.getText());
editor.setSelection(editorState.getCursorPosition());
}
}, false);
}, false, historyState.getDisplayState().getJsclOperation(), true);
CalculatorModel.instance.setCursorOnEnd();
@@ -108,10 +111,15 @@ public class CalculatorHistoryActivity extends ListActivity {
time.setText(new SimpleDateFormat().format(state.getTime()));
final TextView editor = (TextView) result.findViewById(R.id.history_item);
editor.setText(state.getEditorState().getText() + "=" + state.getDisplayState().getEditorHistoryState().getText());
editor.setText(state.getEditorState().getText() + getIdentitySign(state.getDisplayState().getJsclOperation()) + state.getDisplayState().getEditorHistoryState().getText());
return result;
}
@NotNull
private String getIdentitySign(@NotNull JsclOperation jsclOperation) {
return jsclOperation == JsclOperation.simplify ? "" : "=";
}
}
@Override

View File

@@ -34,7 +34,7 @@ import org.solovyev.common.utils.history.HistoryAction;
* Date: 9/12/11
* Time: 11:15 PM
*/
public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorHistoryState> {
public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorHistoryState>, CalculatorEngineControl {
instance;
@@ -51,6 +51,7 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
private CalculatorEngine calculatorEngine;
public CalculatorModel init(@NotNull final Activity activity, @NotNull SharedPreferences preferences, @NotNull CalculatorEngine calculator) {
Log.d(this.getClass().getName(), "CalculatorModel initialization with activity: " + activity);
this.calculatorEngine = calculator;
this.editor = (CalculatorEditor) activity.findViewById(R.id.calculatorEditor);
@@ -119,24 +120,29 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
}
public void doTextOperation(@NotNull TextOperation operation, boolean delayEvaluate) {
doTextOperation(operation, delayEvaluate, JsclOperation.numeric, false);
}
public void doTextOperation(@NotNull TextOperation operation, boolean delayEvaluate, @NotNull JsclOperation jsclOperation, boolean forceEval) {
final String editorStateBefore = this.editor.getText().toString();
Log.d(CalculatorModel.class.getName(), "Editor state changed before '" + editorStateBefore + "'");
operation.doOperation(this.editor);
//Log.d(CalculatorModel.class.getName(), "Doing text operation" + StringUtils.fromStackTrace(Thread.currentThread().getStackTrace()));
final String editorStateAfter = this.editor.getText().toString();
if (!editorStateBefore.equals(editorStateAfter)) {
if (forceEval ||!editorStateBefore.equals(editorStateAfter)) {
editor.redraw();
evaluate(delayEvaluate, editorStateAfter);
evaluate(delayEvaluate, editorStateAfter, jsclOperation);
}
}
@NotNull
private final static MutableObject<Runnable> pendingOperation = new MutableObject<Runnable>();
private void evaluate(boolean delayEvaluate, @NotNull final String expression) {
private void evaluate(boolean delayEvaluate, @NotNull final String expression, @NotNull final JsclOperation operation) {
final CalculatorHistoryState historyState = getCurrentHistoryState();
pendingOperation.setObject(new Runnable() {
@@ -147,7 +153,7 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
//lock all operations with history
if (pendingOperation.getObject() == this) {
// actually nothing shall be logged while text operations are done
evaluate(expression);
evaluate(expression, operation);
historyState.setDisplayState(getCurrentHistoryState().getDisplayState());
@@ -166,19 +172,27 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
}
}
@Override
public void evaluate() {
evaluate(false, this.editor.getText().toString());
evaluate(false, this.editor.getText().toString(), JsclOperation.numeric);
}
private void evaluate(@Nullable final String expression) {
@Override
public void simplify() {
evaluate(false, this.editor.getText().toString(), JsclOperation.simplify);
}
private void evaluate(@Nullable final String expression, @NotNull JsclOperation operation) {
if (!StringUtils.isEmpty(expression)) {
try {
Log.d(CalculatorModel.class.getName(), "Trying to evaluate: " + expression /*+ StringUtils.fromStackTrace(Thread.currentThread().getStackTrace())*/);
display.setText(calculatorEngine.evaluate(JsclOperation.numeric, expression));
Log.d(CalculatorModel.class.getName(), "Trying to evaluate '" + operation + "': " + expression /*+ StringUtils.fromStackTrace(Thread.currentThread().getStackTrace())*/);
final CalculatorEngine.Result result = calculatorEngine.evaluate(operation, expression);
display.setText(result.getResult());
display.setJsclOperation(result.getUserOperation());
} catch (EvalError e) {
handleEvaluationException(expression, display, e);
handleEvaluationException(expression, display, operation, e);
} catch (ParseException e) {
handleEvaluationException(expression, display, e);
handleEvaluationException(expression, display, operation, e);
}
} else {
this.display.setText("");
@@ -187,9 +201,13 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
this.display.redraw();
}
private void handleEvaluationException(@NotNull String expression, @NotNull CalculatorDisplay localDisplay, @NotNull Exception e) {
private void handleEvaluationException(@NotNull String expression,
@NotNull CalculatorDisplay localDisplay,
@NotNull JsclOperation operation,
@NotNull Exception e) {
Log.d(CalculatorModel.class.getName(), "Evaluation failed for : " + expression + ". Error message: " + e.getMessage());
localDisplay.setText(R.string.c_syntax_error);
localDisplay.setJsclOperation(operation);
localDisplay.setValid(false);
}
@@ -268,6 +286,7 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
private void setValuesFromHistory(@NotNull CalculatorDisplay display, CalculatorDisplayHistoryState editorHistoryState) {
setValuesFromHistory(display, editorHistoryState.getEditorHistoryState());
display.setValid(editorHistoryState.isValid());
display.setJsclOperation(editorHistoryState.getJsclOperation());
}
private void setValuesFromHistory(@NotNull TextView editText, EditorHistoryState editorHistoryState) {
@@ -286,22 +305,11 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
}
private EditorHistoryState getEditorHistoryState(@NotNull TextView textView) {
final EditorHistoryState result = new EditorHistoryState();
result.setText(String.valueOf(textView.getText()));
result.setCursorPosition(textView.getSelectionStart());
return result;
return EditorHistoryState.newInstance(textView);
}
private CalculatorDisplayHistoryState getCalculatorDisplayHistoryState(@NotNull CalculatorDisplay display) {
final CalculatorDisplayHistoryState result = new CalculatorDisplayHistoryState();
result.getEditorHistoryState().setText(String.valueOf(display.getText()));
result.getEditorHistoryState().setCursorPosition(display.getSelectionStart());
result.setValid(display.isValid());
return result;
return CalculatorDisplayHistoryState.newInstance(display);
}
@NotNull

View File

@@ -36,8 +36,10 @@ public class CursorDragProcessor implements SimpleOnDragListener.DragProcessor{
String text = ((DirectionDragButton) dragButton).getText(dragDirection);
if ("◀◀".equals(text)) {
cursorControl.setCursorOnStart();
result = true;
} else if ("▶▶".equals(text)) {
cursorControl.setCursorOnEnd();
result = true;
}
}

View File

@@ -5,25 +5,29 @@
package org.solovyev.android.calculator;
import android.widget.TextView;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class EditorHistoryState {
private int cursorPosition;
@Nullable
private String text;
public EditorHistoryState() {
}
public EditorHistoryState( int cursorPosition, @Nullable String text ) {
this.cursorPosition = cursorPosition;
this.text = text;
private EditorHistoryState() {
}
public void setText(@Nullable String text) {
this.text = text;
@NotNull
public static EditorHistoryState newInstance(@NotNull TextView textView) {
final EditorHistoryState result = new EditorHistoryState();
result.text = String.valueOf(textView.getText());
result.cursorPosition = textView.getSelectionStart();
return result;
}
@Nullable
@@ -31,10 +35,6 @@ public class EditorHistoryState {
return text;
}
public void setCursorPosition(int cursorPosition) {
this.cursorPosition = cursorPosition;
}
public int getCursorPosition() {
return cursorPosition;
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator;
import android.view.MotionEvent;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.view.widgets.DirectionDragButton;
import org.solovyev.android.view.widgets.DragButton;
import org.solovyev.android.view.widgets.DragDirection;
import org.solovyev.android.view.widgets.SimpleOnDragListener;
import org.solovyev.common.utils.Point2d;
/**
* User: serso
* Date: 10/24/11
* Time: 9:52 PM
*/
public class EvalDragProcessor implements SimpleOnDragListener.DragProcessor {
@NotNull
private final CalculatorEngineControl calculatorControl;
public EvalDragProcessor(@NotNull CalculatorEngineControl calculatorControl) {
this.calculatorControl = calculatorControl;
}
@Override
public boolean processDragEvent(@NotNull DragDirection dragDirection, @NotNull DragButton dragButton, @NotNull Point2d startPoint2d, @NotNull MotionEvent motionEvent) {
boolean result = false;
if (dragButton instanceof DirectionDragButton) {
String text = ((DirectionDragButton) dragButton).getText(dragDirection);
if ("".equals(text)) {
calculatorControl.simplify();
result = true;
}
}
return result;
}
}

View File

@@ -98,12 +98,35 @@ public enum CalculatorEngine {
}
}
public String evaluate(@NotNull JsclOperation operation,
public static class Result {
@NotNull
private String result;
@NotNull
private JsclOperation userOperation;
public Result(@NotNull String result, @NotNull JsclOperation userOperation) {
this.result = result;
this.userOperation = userOperation;
}
@NotNull
public String getResult() {
return result;
}
@NotNull
public JsclOperation getUserOperation() {
return userOperation;
}
}
public Result evaluate(@NotNull JsclOperation operation,
@NotNull String expression) throws EvalError, ParseException {
return evaluate(operation, expression, null);
}
public String evaluate(@NotNull JsclOperation operation,
public Result evaluate(@NotNull JsclOperation operation,
@NotNull String expression,
@Nullable MessageRegistry<AndroidMessage> mr) throws EvalError, ParseException {
synchronized (lock) {
@@ -190,7 +213,7 @@ public enum CalculatorEngine {
throw new ParseException("Too long calculation for: " + jsclExpression);
}
return operation.getFromProcessor().process(result);
return new Result(operation.getFromProcessor().process(result), operation);
}
}