New architecture

This commit is contained in:
Sergey Solovyev
2012-09-22 19:24:27 +04:00
parent 3661774d9b
commit 233c685a49
68 changed files with 2787 additions and 2475 deletions

View File

@@ -4,17 +4,20 @@ import jscl.NumeralBase;
import jscl.math.Generic;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.history.CalculatorHistoryState;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.common.history.HistoryControl;
/**
* User: Solovyev_S
* Date: 20.09.12
* Time: 16:38
*/
public interface Calculator extends CalculatorEventContainer {
public interface Calculator extends CalculatorEventContainer, HistoryControl<CalculatorHistoryState> {
@NotNull
CalculatorEventDataId createFirstEventDataId();
void evaluate();
void simplify();
@NotNull
CalculatorEventDataId evaluate(@NotNull JsclOperation operation,
@@ -34,4 +37,5 @@ public interface Calculator extends CalculatorEventContainer {
@NotNull
CalculatorEventDataId fireCalculatorEvent(@NotNull CalculatorEventType calculatorEventType, @Nullable Object data, @NotNull Long sequenceId);
void init();
}

View File

@@ -29,7 +29,7 @@ public class CalculatorDisplayImpl implements CalculatorDisplay {
public CalculatorDisplayImpl(@NotNull Calculator calculator) {
this.calculator = calculator;
this.lastCalculatorEventData = CalculatorEventDataImpl.newInstance(calculator.createFirstEventDataId());
this.lastCalculatorEventData = CalculatorEventDataImpl.newInstance(CalculatorUtils.createFirstEventDataId());
this.calculator.addCalculatorEventListener(this);
}
@@ -138,11 +138,11 @@ public class CalculatorDisplayImpl implements CalculatorDisplay {
private void processCalculationCancelled(@NotNull CalculatorEvaluationEventData calculatorEventData) {
final String errorMessage = CalculatorMessages.getBundle().getString(CalculatorMessages.syntax_error);
this.setViewState(CalculatorDisplayViewStateImpl.newErrorState(calculatorEventData.getOperation(), errorMessage));
this.setViewStateForSequence(CalculatorDisplayViewStateImpl.newErrorState(calculatorEventData.getOperation(), errorMessage), calculatorEventData.getSequenceId());
}
private void processCalculationResult(@NotNull CalculatorEvaluationEventData calculatorEventData, @NotNull CalculatorOutput data) {
final String stringResult = data.getStringResult();
this.setViewState(CalculatorDisplayViewStateImpl.newValidState(calculatorEventData.getOperation(), data.getResult(), stringResult, 0));
this.setViewStateForSequence(CalculatorDisplayViewStateImpl.newValidState(calculatorEventData.getOperation(), data.getResult(), stringResult, 0), calculatorEventData.getSequenceId());
}
}

View File

@@ -2,13 +2,14 @@ package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.common.gui.CursorControl;
/**
* User: Solovyev_S
* Date: 21.09.12
* Time: 11:47
*/
public interface CalculatorEditor extends CalculatorEventListener/*, CursorControl*/ {
public interface CalculatorEditor extends CalculatorEventListener {
void setView(@Nullable CalculatorEditorView view);
@@ -48,6 +49,9 @@ public interface CalculatorEditor extends CalculatorEventListener/*, CursorContr
@NotNull
public CalculatorEditorViewState moveCursorRight();
@NotNull
CursorControl asCursorControl();
/*
**********************************************************************

View File

@@ -2,6 +2,7 @@ package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.common.gui.CursorControl;
/**
* User: Solovyev_S
@@ -22,6 +23,9 @@ public class CalculatorEditorImpl implements CalculatorEditor {
@NotNull
private final Calculator calculator;
@NotNull
private final CursorControlAdapter cursorControlAdapter = new CursorControlAdapter(this);
public CalculatorEditorImpl(@NotNull Calculator calculator) {
this.calculator = calculator;
this.calculator.addCalculatorEventListener(this);
@@ -65,12 +69,13 @@ public class CalculatorEditorImpl implements CalculatorEditor {
//To change body of implemented methods use File | Settings | File Templates.
}
@NotNull
public CalculatorEditorViewState setCursorOnStart() {
synchronized (viewLock) {
return newSelectionViewState(0);
}
}
/*
**********************************************************************
*
* SELECTION
*
**********************************************************************
*/
@NotNull
private CalculatorEditorViewState newSelectionViewState(int newSelection) {
@@ -83,6 +88,14 @@ public class CalculatorEditorImpl implements CalculatorEditor {
}
}
@NotNull
public CalculatorEditorViewState setCursorOnStart() {
synchronized (viewLock) {
return newSelectionViewState(0);
}
}
@NotNull
public CalculatorEditorViewState setCursorOnEnd() {
synchronized (viewLock) {
@@ -112,6 +125,20 @@ public class CalculatorEditorImpl implements CalculatorEditor {
}
}
@NotNull
@Override
public CursorControl asCursorControl() {
return cursorControlAdapter;
}
/*
**********************************************************************
*
* EDITOR ACTIONS
*
**********************************************************************
*/
@NotNull
@Override
public CalculatorEditorViewState erase() {
@@ -221,4 +248,34 @@ public class CalculatorEditorImpl implements CalculatorEditor {
result = Math.min(result, textLength);
return result;
}
private static final class CursorControlAdapter implements CursorControl {
@NotNull
private final CalculatorEditor calculatorEditor;
private CursorControlAdapter(@NotNull CalculatorEditor calculatorEditor) {
this.calculatorEditor = calculatorEditor;
}
@Override
public void setCursorOnStart() {
this.calculatorEditor.setCursorOnStart();
}
@Override
public void setCursorOnEnd() {
this.calculatorEditor.setCursorOnEnd();
}
@Override
public void moveCursorLeft() {
this.calculatorEditor.moveCursorLeft();
}
@Override
public void moveCursorRight() {
this.calculatorEditor.moveCursorRight();
}
}
}

View File

@@ -1,34 +1,39 @@
package org.solovyev.android.calculator;
import jscl.MathEngine;
import jscl.math.function.Function;
import jscl.math.function.IConstant;
import jscl.math.operator.Operator;
import org.jetbrains.annotations.NotNull;
import org.solovyev.common.math.MathRegistry;
/**
* User: Solovyev_S
* Date: 20.09.12
* Time: 12:43
*/
public interface JCalculatorEngine {
@NotNull
String getMultiplicationSign();
@NotNull
MathRegistry<IConstant> getVarsRegistry();
@NotNull
MathRegistry<Function> getFunctionsRegistry();
@NotNull
MathRegistry<Operator> getOperatorsRegistry();
@NotNull
MathRegistry<Operator> getPostfixFunctionsRegistry();
@NotNull
MathEngine getEngine();
}
package org.solovyev.android.calculator;
import jscl.MathEngine;
import jscl.math.function.Function;
import jscl.math.function.IConstant;
import jscl.math.operator.Operator;
import org.jetbrains.annotations.NotNull;
/**
* User: Solovyev_S
* Date: 20.09.12
* Time: 12:43
*/
public interface CalculatorEngine {
@NotNull
String getMultiplicationSign();
@NotNull
CalculatorMathRegistry<IConstant> getVarsRegistry();
@NotNull
CalculatorMathRegistry<Function> getFunctionsRegistry();
@NotNull
CalculatorMathRegistry<Operator> getOperatorsRegistry();
@NotNull
CalculatorMathRegistry<Operator> getPostfixFunctionsRegistry();
@NotNull
MathEngine getEngine();
void init();
void reset();
void softReset();
}

View File

@@ -1,7 +1,6 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.jscl.JsclOperation;
/**
@@ -60,4 +59,9 @@ public class CalculatorEvaluationEventDataImpl implements CalculatorEvaluationEv
public boolean isSameSequence(@NotNull CalculatorEventDataId that) {
return this.calculatorEventData.isSameSequence(that);
}
@Override
public boolean isAfterSequence(@NotNull CalculatorEventDataId that) {
return this.calculatorEventData.isAfterSequence(that);
}
}

View File

@@ -19,4 +19,6 @@ public interface CalculatorEventDataId {
boolean isAfter(@NotNull CalculatorEventDataId that);
boolean isSameSequence(@NotNull CalculatorEventDataId that);
boolean isAfterSequence(@NotNull CalculatorEventDataId that);
}

View File

@@ -47,6 +47,11 @@ class CalculatorEventDataIdImpl implements CalculatorEventDataId {
return !this.sequenceId.equals(NO_SEQUENCE) && this.sequenceId.equals(that.getSequenceId());
}
@Override
public boolean isAfterSequence(@NotNull CalculatorEventDataId that) {
return !this.sequenceId.equals(NO_SEQUENCE) && this.sequenceId > that.getSequenceId();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;

View File

@@ -1,7 +1,6 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: Solovyev_S
@@ -43,6 +42,11 @@ class CalculatorEventDataImpl implements CalculatorEventData {
return this.calculatorEventDataId.isSameSequence(that);
}
@Override
public boolean isAfterSequence(@NotNull CalculatorEventDataId that) {
return this.calculatorEventDataId.isAfterSequence(that);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;

View File

@@ -8,8 +8,11 @@ import jscl.math.Generic;
import jscl.text.ParseInterruptedException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.history.CalculatorHistory;
import org.solovyev.android.calculator.history.CalculatorHistoryState;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.calculator.text.TextProcessor;
import org.solovyev.common.history.HistoryAction;
import org.solovyev.common.msg.MessageRegistry;
import org.solovyev.common.msg.MessageType;
import org.solovyev.common.text.StringUtils;
@@ -29,13 +32,11 @@ import java.util.concurrent.atomic.AtomicLong;
*/
public class CalculatorImpl implements Calculator, CalculatorEventListener {
private static final long FIRST_ID = 0;
@NotNull
private final CalculatorEventContainer calculatorEventContainer = new ListCalculatorEventContainer();
@NotNull
private final AtomicLong counter = new AtomicLong(FIRST_ID);
private final AtomicLong counter = new AtomicLong(CalculatorUtils.FIRST_ID);
@NotNull
private final TextProcessor<PreparedExpression, String> preprocessor = ToJsclTextProcessor.getInstance();
@@ -93,10 +94,14 @@ public class CalculatorImpl implements Calculator, CalculatorEventListener {
**********************************************************************
*/
@NotNull
@Override
public CalculatorEventDataId createFirstEventDataId() {
return CalculatorEventDataIdImpl.newInstance(FIRST_ID, FIRST_ID);
public void evaluate() {
this.evaluate(JsclOperation.numeric, getEditor().getViewState().getText());
}
@Override
public void simplify() {
this.evaluate(JsclOperation.simplify, getEditor().getViewState().getText());
}
@NotNull
@@ -144,7 +149,7 @@ public class CalculatorImpl implements Calculator, CalculatorEventListener {
fireCalculatorEvent(newConversionEventData(sequenceId), CalculatorEventType.conversion_started, null);
final NumeralBase from = CalculatorLocatorImpl.getInstance().getCalculatorEngine().getEngine().getNumeralBase();
final NumeralBase from = CalculatorLocatorImpl.getInstance().getEngine().getEngine().getNumeralBase();
if (from != to) {
String fromString = generic.toString();
@@ -197,6 +202,12 @@ public class CalculatorImpl implements Calculator, CalculatorEventListener {
return eventDataId;
}
@Override
public void init() {
CalculatorLocatorImpl.getInstance().getEngine().init();
CalculatorLocatorImpl.getInstance().getHistory().load();
}
@NotNull
private CalculatorEventData newConversionEventData(@NotNull Long sequenceId) {
return CalculatorEventDataImpl.newInstance(nextEventDataId(sequenceId));
@@ -215,7 +226,7 @@ public class CalculatorImpl implements Calculator, CalculatorEventListener {
expression = expression.trim();
if ( StringUtils.isEmpty(expression) ) {
if (StringUtils.isEmpty(expression)) {
final CalculatorOutputImpl data = new CalculatorOutputImpl("", operation, Expression.valueOf(""));
fireCalculatorEvent(newCalculationEventData(operation, expression, sequenceId), CalculatorEventType.calculation_result, data);
} else {
@@ -330,12 +341,44 @@ public class CalculatorImpl implements Calculator, CalculatorEventListener {
final String newText = changeEventData.getNewState().getText();
final String oldText = changeEventData.getOldState().getText();
if ( !newText.equals(oldText) ) {
if (!newText.equals(oldText)) {
evaluate(JsclOperation.numeric, changeEventData.getNewState().getText(), calculatorEventData.getSequenceId());
}
}
}
@Override
public void doHistoryAction(@NotNull HistoryAction historyAction) {
final CalculatorHistory history = CalculatorLocatorImpl.getInstance().getHistory();
if (history.isActionAvailable(historyAction)) {
final CalculatorHistoryState newState = history.doAction(historyAction, getCurrentHistoryState());
if (newState != null) {
setCurrentHistoryState(newState);
}
}
}
@Override
public void setCurrentHistoryState(@NotNull CalculatorHistoryState editorHistoryState) {
editorHistoryState.setValuesFromHistory(getEditor(), getDisplay());
}
@NotNull
private CalculatorEditor getEditor() {
return CalculatorLocatorImpl.getInstance().getEditor();
}
@NotNull
@Override
public CalculatorHistoryState getCurrentHistoryState() {
return CalculatorHistoryState.newInstance(getEditor(), getDisplay());
}
@NotNull
private CalculatorDisplay getDisplay() {
return CalculatorLocatorImpl.getInstance().getDisplay();
}
public static final class ConversionException extends Exception {
private ConversionException() {
}

View File

@@ -18,4 +18,8 @@ public interface CalculatorKeyboard {
void clearButtonPressed();
void copyButtonPressed();
void moveCursorLeft();
void moveCursorRight();
}

View File

@@ -49,14 +49,14 @@ public class CalculatorKeyboardImpl implements CalculatorKeyboard {
}
}
final CalculatorEditor editor = CalculatorLocatorImpl.getInstance().getCalculatorEditor();
final CalculatorEditor editor = CalculatorLocatorImpl.getInstance().getEditor();
editor.insert(textToBeInserted.toString(), cursorPositionOffset);
}
}
@Override
public void roundBracketsButtonPressed() {
final CalculatorEditor editor = CalculatorLocatorImpl.getInstance().getCalculatorEditor();
final CalculatorEditor editor = CalculatorLocatorImpl.getInstance().getEditor();
CalculatorEditorViewState viewState = editor.getViewState();
final int cursorPosition = viewState.getSelection();
@@ -72,26 +72,36 @@ public class CalculatorKeyboardImpl implements CalculatorKeyboard {
@Override
public void pasteButtonPressed() {
final String text = CalculatorLocatorImpl.getInstance().getCalculatorClipboard().getText();
final String text = CalculatorLocatorImpl.getInstance().getClipboard().getText();
if (text != null) {
CalculatorLocatorImpl.getInstance().getCalculatorEditor().insert(text);
CalculatorLocatorImpl.getInstance().getEditor().insert(text);
}
}
@Override
public void clearButtonPressed() {
CalculatorLocatorImpl.getInstance().getCalculatorEditor().clear();
CalculatorLocatorImpl.getInstance().getEditor().clear();
}
@Override
public void copyButtonPressed() {
final CalculatorDisplayViewState displayViewState = CalculatorLocatorImpl.getInstance().getCalculatorDisplay().getViewState();
final CalculatorDisplayViewState displayViewState = CalculatorLocatorImpl.getInstance().getDisplay().getViewState();
if (displayViewState.isValid()) {
final CharSequence text = displayViewState.getText();
if (!StringUtils.isEmpty(text)) {
CalculatorLocatorImpl.getInstance().getCalculatorClipboard().setText(text);
CalculatorLocatorImpl.getInstance().getCalculatorNotifier().showMessage(CalculatorMessage.newInfoMessage(CalculatorMessages.result_copied));
CalculatorLocatorImpl.getInstance().getClipboard().setText(text);
CalculatorLocatorImpl.getInstance().getNotifier().showMessage(CalculatorMessage.newInfoMessage(CalculatorMessages.result_copied));
}
}
}
@Override
public void moveCursorLeft() {
CalculatorLocatorImpl.getInstance().getEditor().moveCursorLeft();
}
@Override
public void moveCursorRight() {
CalculatorLocatorImpl.getInstance().getEditor().moveCursorRight();
}
}

View File

@@ -1,6 +1,7 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.history.CalculatorHistory;
/**
* User: Solovyev_S
@@ -9,30 +10,33 @@ import org.jetbrains.annotations.NotNull;
*/
public interface CalculatorLocator {
@NotNull
JCalculatorEngine getCalculatorEngine();
void init(@NotNull Calculator calculator,
@NotNull CalculatorEngine engine,
@NotNull CalculatorClipboard clipboard,
@NotNull CalculatorNotifier notifier,
@NotNull CalculatorHistory history);
@NotNull
Calculator getCalculator();
@NotNull
CalculatorDisplay getCalculatorDisplay();
CalculatorEngine getEngine();
@NotNull
CalculatorEditor getCalculatorEditor();
void setCalculatorEngine(@NotNull JCalculatorEngine calculatorEngine);
CalculatorDisplay getDisplay();
@NotNull
CalculatorKeyboard getCalculatorKeyboard();
CalculatorEditor getEditor();
@NotNull
CalculatorClipboard getCalculatorClipboard();
void setCalculatorClipboard(@NotNull CalculatorClipboard calculatorClipboard);
CalculatorKeyboard getKeyboard();
@NotNull
CalculatorNotifier getCalculatorNotifier();
CalculatorClipboard getClipboard();
void setCalculatorNotifier(@NotNull CalculatorNotifier calculatorNotifier);
@NotNull
CalculatorNotifier getNotifier();
@NotNull
CalculatorHistory getHistory();
}

View File

@@ -1,6 +1,7 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.history.CalculatorHistory;
/**
* User: Solovyev_S
@@ -10,19 +11,22 @@ import org.jetbrains.annotations.NotNull;
public class CalculatorLocatorImpl implements CalculatorLocator {
@NotNull
private JCalculatorEngine calculatorEngine;
private CalculatorEngine calculatorEngine;
@NotNull
private final Calculator calculator = new CalculatorImpl();
private Calculator calculator;
@NotNull
private final CalculatorEditor calculatorEditor = new CalculatorEditorImpl(calculator);
private CalculatorEditor calculatorEditor;
@NotNull
private final CalculatorDisplay calculatorDisplay = new CalculatorDisplayImpl(calculator);
private CalculatorDisplay calculatorDisplay;
@NotNull
private final CalculatorKeyboard calculatorKeyboard = new CalculatorKeyboardImpl(calculator);
private CalculatorKeyboard calculatorKeyboard;
@NotNull
private CalculatorHistory calculatorHistory;
@NotNull
private CalculatorNotifier calculatorNotifier = new DummyCalculatorNotifier();
@@ -36,6 +40,24 @@ public class CalculatorLocatorImpl implements CalculatorLocator {
private CalculatorLocatorImpl() {
}
@Override
public void init(@NotNull Calculator calculator,
@NotNull CalculatorEngine engine,
@NotNull CalculatorClipboard clipboard,
@NotNull CalculatorNotifier notifier,
@NotNull CalculatorHistory history) {
this.calculator = calculator;
this.calculatorEngine = engine;
this.calculatorClipboard = clipboard;
this.calculatorNotifier = notifier;
this.calculatorHistory = history;
calculatorEditor = new CalculatorEditorImpl(this.calculator);
calculatorDisplay = new CalculatorDisplayImpl(this.calculator);
calculatorKeyboard = new CalculatorKeyboardImpl(this.calculator);
}
@NotNull
public static CalculatorLocator getInstance() {
return instance;
@@ -43,7 +65,7 @@ public class CalculatorLocatorImpl implements CalculatorLocator {
@NotNull
@Override
public JCalculatorEngine getCalculatorEngine() {
public CalculatorEngine getEngine() {
return calculatorEngine;
}
@@ -53,48 +75,39 @@ public class CalculatorLocatorImpl implements CalculatorLocator {
return this.calculator;
}
@Override
public void setCalculatorEngine(@NotNull JCalculatorEngine calculatorEngine) {
this.calculatorEngine = calculatorEngine;
}
@Override
@NotNull
public CalculatorDisplay getCalculatorDisplay() {
public CalculatorDisplay getDisplay() {
return calculatorDisplay;
}
@NotNull
@Override
public CalculatorEditor getCalculatorEditor() {
public CalculatorEditor getEditor() {
return calculatorEditor;
}
@Override
@NotNull
public CalculatorKeyboard getCalculatorKeyboard() {
public CalculatorKeyboard getKeyboard() {
return calculatorKeyboard;
}
@Override
@NotNull
public CalculatorClipboard getCalculatorClipboard() {
public CalculatorClipboard getClipboard() {
return calculatorClipboard;
}
@Override
public void setCalculatorClipboard(@NotNull CalculatorClipboard calculatorClipboard) {
this.calculatorClipboard = calculatorClipboard;
}
@Override
@NotNull
public CalculatorNotifier getCalculatorNotifier() {
public CalculatorNotifier getNotifier() {
return calculatorNotifier;
}
@Override
public void setCalculatorNotifier(@NotNull CalculatorNotifier calculatorNotifier) {
this.calculatorNotifier = calculatorNotifier;
@NotNull
public CalculatorHistory getHistory() {
return calculatorHistory;
}
}

View File

@@ -0,0 +1,30 @@
/*
* 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 org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.common.math.MathEntity;
import org.solovyev.common.math.MathRegistry;
/**
* User: serso
* Date: 10/30/11
* Time: 1:02 AM
*/
public interface CalculatorMathRegistry<T extends MathEntity> extends MathRegistry<T> {
@Nullable
String getDescription(@NotNull String mathEntityName);
@Nullable
String getCategory(@NotNull T mathEntity);
void load();
void save();
}

View File

@@ -0,0 +1,22 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
/**
* User: serso
* Date: 9/22/12
* Time: 7:13 PM
*/
public final class CalculatorUtils {
static final long FIRST_ID = 0;
private CalculatorUtils() {
throw new AssertionError();
}
@NotNull
public static CalculatorEventDataId createFirstEventDataId() {
return CalculatorEventDataIdImpl.newInstance(FIRST_ID, FIRST_ID);
}
}

View File

@@ -1,147 +1,147 @@
/*
* 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 jscl.math.function.IConstant;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.text.TextProcessor;
import org.solovyev.common.StartsWithFinder;
import org.solovyev.android.calculator.math.MathType;
import org.solovyev.common.collections.CollectionsUtils;
import org.solovyev.common.msg.MessageType;
import java.util.ArrayList;
import java.util.List;
public class ToJsclTextProcessor implements TextProcessor<PreparedExpression, String> {
@NotNull
private static final Integer MAX_DEPTH = 20;
@NotNull
private static final TextProcessor<PreparedExpression, String> instance = new ToJsclTextProcessor();
private ToJsclTextProcessor() {
}
@NotNull
public static TextProcessor<PreparedExpression, String> getInstance() {
return instance;
}
@Override
@NotNull
public PreparedExpression process(@NotNull String s) throws CalculatorParseException {
return processWithDepth(s, 0, new ArrayList<IConstant>());
}
private static PreparedExpression processWithDepth(@NotNull String s, int depth, @NotNull List<IConstant> undefinedVars) throws CalculatorParseException {
return replaceVariables(processExpression(s).toString(), depth, undefinedVars);
}
@NotNull
private static StringBuilder processExpression(@NotNull String s) throws CalculatorParseException {
final StartsWithFinder startsWithFinder = new StartsWithFinder(s, 0);
final StringBuilder result = new StringBuilder();
MathType.Result mathTypeResult = null;
MathType.Result mathTypeBefore;
final LiteNumberBuilder nb = new LiteNumberBuilder(CalculatorLocatorImpl.getInstance().getCalculatorEngine().getEngine());
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == ' ') continue;
startsWithFinder.setI(i);
mathTypeBefore = mathTypeResult == null ? null : mathTypeResult;
mathTypeResult = MathType.getType(s, i, nb.isHexMode());
nb.process(mathTypeResult);
if (mathTypeBefore != null) {
final MathType current = mathTypeResult.getMathType();
if (current.isNeedMultiplicationSignBefore(mathTypeBefore.getMathType())) {
result.append("*");
}
}
if (mathTypeBefore != null &&
(mathTypeBefore.getMathType() == MathType.function || mathTypeBefore.getMathType() == MathType.operator) &&
CollectionsUtils.find(MathType.openGroupSymbols, startsWithFinder) != null) {
throw new CalculatorParseException(i, s, new CalculatorMessage(CalculatorMessages.msg_005, MessageType.error, mathTypeBefore.getMatch()));
}
i = mathTypeResult.processToJscl(result, i);
}
return result;
}
@NotNull
private static PreparedExpression replaceVariables(@NotNull final String s, int depth, @NotNull List<IConstant> undefinedVars) throws CalculatorParseException {
if (depth >= MAX_DEPTH) {
throw new CalculatorParseException(s, new CalculatorMessage(CalculatorMessages.msg_006, MessageType.error));
} else {
depth++;
}
final StartsWithFinder startsWithFinder = new StartsWithFinder(s, 0);
final StringBuilder result = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
startsWithFinder.setI(i);
int offset = 0;
String functionName = CollectionsUtils.find(MathType.function.getTokens(), startsWithFinder);
if (functionName == null) {
String operatorName = CollectionsUtils.find(MathType.operator.getTokens(), startsWithFinder);
if (operatorName == null) {
String varName = CollectionsUtils.find(CalculatorLocatorImpl.getInstance().getCalculatorEngine().getVarsRegistry().getNames(), startsWithFinder);
if (varName != null) {
final IConstant var = CalculatorLocatorImpl.getInstance().getCalculatorEngine().getVarsRegistry().get(varName);
if (var != null) {
if (!var.isDefined()) {
undefinedVars.add(var);
result.append(varName);
offset = varName.length();
} else {
final String value = var.getValue();
assert value != null;
if ( var.getDoubleValue() != null ) {
//result.append(value);
// NOTE: append varName as JSCL engine will convert it to double if needed
result.append(varName);
} else {
result.append("(").append(processWithDepth(value, depth, undefinedVars)).append(")");
}
offset = varName.length();
}
}
}
} else {
result.append(operatorName);
offset = operatorName.length();
}
} else {
result.append(functionName);
offset = functionName.length();
}
if (offset == 0) {
result.append(s.charAt(i));
} else {
i += offset - 1;
}
}
return new PreparedExpression(result.toString(), undefinedVars);
}
}
/*
* 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 jscl.math.function.IConstant;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.text.TextProcessor;
import org.solovyev.common.StartsWithFinder;
import org.solovyev.android.calculator.math.MathType;
import org.solovyev.common.collections.CollectionsUtils;
import org.solovyev.common.msg.MessageType;
import java.util.ArrayList;
import java.util.List;
public class ToJsclTextProcessor implements TextProcessor<PreparedExpression, String> {
@NotNull
private static final Integer MAX_DEPTH = 20;
@NotNull
private static final TextProcessor<PreparedExpression, String> instance = new ToJsclTextProcessor();
private ToJsclTextProcessor() {
}
@NotNull
public static TextProcessor<PreparedExpression, String> getInstance() {
return instance;
}
@Override
@NotNull
public PreparedExpression process(@NotNull String s) throws CalculatorParseException {
return processWithDepth(s, 0, new ArrayList<IConstant>());
}
private static PreparedExpression processWithDepth(@NotNull String s, int depth, @NotNull List<IConstant> undefinedVars) throws CalculatorParseException {
return replaceVariables(processExpression(s).toString(), depth, undefinedVars);
}
@NotNull
private static StringBuilder processExpression(@NotNull String s) throws CalculatorParseException {
final StartsWithFinder startsWithFinder = new StartsWithFinder(s, 0);
final StringBuilder result = new StringBuilder();
MathType.Result mathTypeResult = null;
MathType.Result mathTypeBefore;
final LiteNumberBuilder nb = new LiteNumberBuilder(CalculatorLocatorImpl.getInstance().getEngine().getEngine());
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == ' ') continue;
startsWithFinder.setI(i);
mathTypeBefore = mathTypeResult == null ? null : mathTypeResult;
mathTypeResult = MathType.getType(s, i, nb.isHexMode());
nb.process(mathTypeResult);
if (mathTypeBefore != null) {
final MathType current = mathTypeResult.getMathType();
if (current.isNeedMultiplicationSignBefore(mathTypeBefore.getMathType())) {
result.append("*");
}
}
if (mathTypeBefore != null &&
(mathTypeBefore.getMathType() == MathType.function || mathTypeBefore.getMathType() == MathType.operator) &&
CollectionsUtils.find(MathType.openGroupSymbols, startsWithFinder) != null) {
throw new CalculatorParseException(i, s, new CalculatorMessage(CalculatorMessages.msg_005, MessageType.error, mathTypeBefore.getMatch()));
}
i = mathTypeResult.processToJscl(result, i);
}
return result;
}
@NotNull
private static PreparedExpression replaceVariables(@NotNull final String s, int depth, @NotNull List<IConstant> undefinedVars) throws CalculatorParseException {
if (depth >= MAX_DEPTH) {
throw new CalculatorParseException(s, new CalculatorMessage(CalculatorMessages.msg_006, MessageType.error));
} else {
depth++;
}
final StartsWithFinder startsWithFinder = new StartsWithFinder(s, 0);
final StringBuilder result = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
startsWithFinder.setI(i);
int offset = 0;
String functionName = CollectionsUtils.find(MathType.function.getTokens(), startsWithFinder);
if (functionName == null) {
String operatorName = CollectionsUtils.find(MathType.operator.getTokens(), startsWithFinder);
if (operatorName == null) {
String varName = CollectionsUtils.find(CalculatorLocatorImpl.getInstance().getEngine().getVarsRegistry().getNames(), startsWithFinder);
if (varName != null) {
final IConstant var = CalculatorLocatorImpl.getInstance().getEngine().getVarsRegistry().get(varName);
if (var != null) {
if (!var.isDefined()) {
undefinedVars.add(var);
result.append(varName);
offset = varName.length();
} else {
final String value = var.getValue();
assert value != null;
if ( var.getDoubleValue() != null ) {
//result.append(value);
// NOTE: append varName as JSCL engine will convert it to double if needed
result.append(varName);
} else {
result.append("(").append(processWithDepth(value, depth, undefinedVars)).append(")");
}
offset = varName.length();
}
}
}
} else {
result.append(operatorName);
offset = operatorName.length();
}
} else {
result.append(functionName);
offset = functionName.length();
}
if (offset == 0) {
result.append(s.charAt(i));
} else {
i += offset - 1;
}
}
return new PreparedExpression(result.toString(), undefinedVars);
}
}

View File

@@ -1,21 +1,33 @@
package org.solovyev.android.calculator.history;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.CalculatorEventListener;
import org.solovyev.common.history.HistoryHelper;
/**
* User: Solovyev_S
* Date: 20.09.12
* Time: 16:11
*/
public interface CalculatorHistory extends HistoryHelper<CalculatorHistoryState>, CalculatorEventListener {
void fromXml(@NotNull String xml);
String toXml();
void clearSavedHistory();
void removeSavedHistory(@NotNull CalculatorHistoryState historyState);
}
package org.solovyev.android.calculator.history;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.CalculatorEventListener;
import org.solovyev.common.history.HistoryHelper;
import java.util.List;
/**
* User: Solovyev_S
* Date: 20.09.12
* Time: 16:11
*/
public interface CalculatorHistory extends HistoryHelper<CalculatorHistoryState>, CalculatorEventListener {
void load();
void save();
void fromXml(@NotNull String xml);
String toXml();
void clearSavedHistory();
void removeSavedHistory(@NotNull CalculatorHistoryState historyState);
@NotNull
List<CalculatorHistoryState> getSavedHistory();
@NotNull
CalculatorHistoryState addSavedState(@NotNull CalculatorHistoryState historyState);
}

View File

@@ -31,29 +31,41 @@ public class CalculatorHistoryImpl implements CalculatorHistory {
private final List<CalculatorHistoryState> savedHistory = new ArrayList<CalculatorHistoryState>();
@NotNull
private volatile CalculatorEventDataId lastEventDataId = CalculatorLocatorImpl.getInstance().getCalculator().createFirstEventDataId();
private volatile CalculatorEventDataId lastEventDataId = CalculatorUtils.createFirstEventDataId();
@Nullable
private volatile CalculatorEditorViewState lastEditorViewState;
public CalculatorHistoryImpl(@NotNull Calculator calculator) {
calculator.addCalculatorEventListener(this);
}
@Override
public boolean isEmpty() {
return this.history.isEmpty();
synchronized (history) {
return this.history.isEmpty();
}
}
@Override
public CalculatorHistoryState getLastHistoryState() {
return this.history.getLastHistoryState();
synchronized (history) {
return this.history.getLastHistoryState();
}
}
@Override
public boolean isUndoAvailable() {
return history.isUndoAvailable();
synchronized (history) {
return history.isUndoAvailable();
}
}
@Override
public CalculatorHistoryState undo(@Nullable CalculatorHistoryState currentState) {
return history.undo(currentState);
synchronized (history) {
return history.undo(currentState);
}
}
@Override
@@ -63,40 +75,54 @@ public class CalculatorHistoryImpl implements CalculatorHistory {
@Override
public CalculatorHistoryState redo(@Nullable CalculatorHistoryState currentState) {
return history.redo(currentState);
synchronized (history) {
return history.redo(currentState);
}
}
@Override
public boolean isActionAvailable(@NotNull HistoryAction historyAction) {
return history.isActionAvailable(historyAction);
synchronized (history) {
return history.isActionAvailable(historyAction);
}
}
@Override
public CalculatorHistoryState doAction(@NotNull HistoryAction historyAction, @Nullable CalculatorHistoryState currentState) {
return history.doAction(historyAction, currentState);
synchronized (history) {
return history.doAction(historyAction, currentState);
}
}
@Override
public void addState(@Nullable CalculatorHistoryState currentState) {
history.addState(currentState);
synchronized (history) {
history.addState(currentState);
}
}
@NotNull
@Override
public List<CalculatorHistoryState> getStates() {
return history.getStates();
synchronized (history) {
return history.getStates();
}
}
@Override
public void clear() {
this.history.clear();
synchronized (history) {
this.history.clear();
}
}
@Override
@NotNull
public List<CalculatorHistoryState> getSavedHistory() {
return Collections.unmodifiableList(savedHistory);
}
@Override
@NotNull
public CalculatorHistoryState addSavedState(@NotNull CalculatorHistoryState historyState) {
if (historyState.isSaved()) {
@@ -113,6 +139,16 @@ public class CalculatorHistoryImpl implements CalculatorHistory {
}
}
@Override
public void load() {
// todo serso: create saved/loader class
}
@Override
public void save() {
// todo serso: create saved/loader class
}
@Override
public void fromXml(@NotNull String xml) {
clearSavedHistory();
@@ -148,20 +184,27 @@ public class CalculatorHistoryImpl implements CalculatorHistory {
if (calculatorEventData.isAfter(this.lastEventDataId)) {
final boolean sameSequence = calculatorEventData.isSameSequence(this.lastEventDataId);
this.lastEventDataId = calculatorEventData;
if (sameSequence || calculatorEventData.isAfterSequence(this.lastEventDataId)) {
this.lastEventDataId = calculatorEventData;
switch (calculatorEventType) {
case editor_state_changed:
final CalculatorEditorChangeEventData changeEventData = (CalculatorEditorChangeEventData) data;
lastEditorViewState = changeEventData.getNewState();
break;
case display_state_changed:
if (sameSequence) {
} else {
lastEditorViewState = null;
}
break;
switch (calculatorEventType) {
case editor_state_changed:
final CalculatorEditorChangeEventData editorChangeData = (CalculatorEditorChangeEventData) data;
lastEditorViewState = editorChangeData.getNewState();
break;
case display_state_changed:
if (sameSequence) {
if (lastEditorViewState != null) {
final CalculatorEditorViewState editorViewState = lastEditorViewState;
final CalculatorDisplayChangeEventData displayChangeData = (CalculatorDisplayChangeEventData) data;
final CalculatorDisplayViewState displayViewState = displayChangeData.getNewState();
addState(CalculatorHistoryState.newInstance(editorViewState, displayViewState));
}
} else {
lastEditorViewState = null;
}
break;
}
}
}
}

View File

@@ -1,70 +1,70 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.calculator.jscl;
import jscl.math.Generic;
import jscl.text.ParseException;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.CalculatorLocatorImpl;
import org.solovyev.android.calculator.text.DummyTextProcessor;
import org.solovyev.android.calculator.text.FromJsclSimplifyTextProcessor;
import org.solovyev.android.calculator.text.TextProcessor;
public enum JsclOperation {
simplify,
elementary,
numeric;
JsclOperation() {
}
@NotNull
public TextProcessor<String, Generic> getFromProcessor() {
switch (this) {
case simplify:
return FromJsclSimplifyTextProcessor.instance;
case elementary:
return DummyTextProcessor.instance;
case numeric:
return FromJsclNumericTextProcessor.instance;
default:
throw new UnsupportedOperationException();
}
}
@NotNull
public final String evaluate(@NotNull String expression) throws ParseException {
switch (this) {
case simplify:
return CalculatorLocatorImpl.getInstance().getCalculatorEngine().getEngine().simplify(expression);
case elementary:
return CalculatorLocatorImpl.getInstance().getCalculatorEngine().getEngine().elementary(expression);
case numeric:
return CalculatorLocatorImpl.getInstance().getCalculatorEngine().getEngine().evaluate(expression);
default:
throw new UnsupportedOperationException();
}
}
@NotNull
public final Generic evaluateGeneric(@NotNull String expression) throws ParseException {
switch (this) {
case simplify:
return CalculatorLocatorImpl.getInstance().getCalculatorEngine().getEngine().simplifyGeneric(expression);
case elementary:
return CalculatorLocatorImpl.getInstance().getCalculatorEngine().getEngine().elementaryGeneric(expression);
case numeric:
return CalculatorLocatorImpl.getInstance().getCalculatorEngine().getEngine().evaluateGeneric(expression);
default:
throw new UnsupportedOperationException();
}
}
}
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.calculator.jscl;
import jscl.math.Generic;
import jscl.text.ParseException;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.CalculatorLocatorImpl;
import org.solovyev.android.calculator.text.DummyTextProcessor;
import org.solovyev.android.calculator.text.FromJsclSimplifyTextProcessor;
import org.solovyev.android.calculator.text.TextProcessor;
public enum JsclOperation {
simplify,
elementary,
numeric;
JsclOperation() {
}
@NotNull
public TextProcessor<String, Generic> getFromProcessor() {
switch (this) {
case simplify:
return FromJsclSimplifyTextProcessor.instance;
case elementary:
return DummyTextProcessor.instance;
case numeric:
return FromJsclNumericTextProcessor.instance;
default:
throw new UnsupportedOperationException();
}
}
@NotNull
public final String evaluate(@NotNull String expression) throws ParseException {
switch (this) {
case simplify:
return CalculatorLocatorImpl.getInstance().getEngine().getEngine().simplify(expression);
case elementary:
return CalculatorLocatorImpl.getInstance().getEngine().getEngine().elementary(expression);
case numeric:
return CalculatorLocatorImpl.getInstance().getEngine().getEngine().evaluate(expression);
default:
throw new UnsupportedOperationException();
}
}
@NotNull
public final Generic evaluateGeneric(@NotNull String expression) throws ParseException {
switch (this) {
case simplify:
return CalculatorLocatorImpl.getInstance().getEngine().getEngine().simplifyGeneric(expression);
case elementary:
return CalculatorLocatorImpl.getInstance().getEngine().getEngine().elementaryGeneric(expression);
case numeric:
return CalculatorLocatorImpl.getInstance().getEngine().getEngine().evaluateGeneric(expression);
default:
throw new UnsupportedOperationException();
}
}
}

View File

@@ -1,445 +1,445 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.calculator.math;
import jscl.JsclMathEngine;
import jscl.NumeralBase;
import jscl.math.function.Constants;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.CalculatorLocatorImpl;
import org.solovyev.common.JPredicate;
import org.solovyev.common.StartsWithFinder;
import org.solovyev.android.calculator.CalculatorParseException;
import org.solovyev.common.collections.CollectionsUtils;
import java.util.*;
public enum MathType {
numeral_base(50, true, false, MathGroupType.number) {
private final List<String> tokens = new ArrayList<String>(10);
{
for (NumeralBase numeralBase : NumeralBase.values()) {
tokens.add(numeralBase.getJsclPrefix());
}
}
@NotNull
@Override
public List<String> getTokens() {
return tokens;
}
},
dot(200, true, true, MathGroupType.number, ".") {
@Override
public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) {
return super.isNeedMultiplicationSignBefore(mathTypeBefore) && mathTypeBefore != digit;
}
},
grouping_separator(250, false, false, MathGroupType.number, "'", " "){
@Override
public int processToJscl(@NotNull StringBuilder result, int i, @NotNull String match) throws CalculatorParseException {
return i;
}
},
power_10(300, false, false, MathGroupType.number, "E"),
postfix_function(400, false, true, MathGroupType.function) {
@NotNull
@Override
public List<String> getTokens() {
return CalculatorLocatorImpl.getInstance().getCalculatorEngine().getPostfixFunctionsRegistry().getNames();
}
},
unary_operation(500, false, false, MathGroupType.operation, "-", "="),
binary_operation(600, false, false, MathGroupType.operation, "-", "+", "*", "×", "", "/", "^") {
@Override
protected String getSubstituteToJscl(@NotNull String match) {
if (match.equals("×") || match.equals("")) {
return "*";
} else {
return null;
}
}
},
open_group_symbol(800, true, false, MathGroupType.other, "[", "(", "{") {
@Override
public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) {
return super.isNeedMultiplicationSignBefore(mathTypeBefore) && mathTypeBefore != function && mathTypeBefore != operator;
}
@Override
protected String getSubstituteToJscl(@NotNull String match) {
return "(";
}
},
close_group_symbol(900, false, true, MathGroupType.other, "]", ")", "}") {
@Override
public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) {
return false;
}
@Override
protected String getSubstituteToJscl(@NotNull String match) {
return ")";
}
},
function(1000, true, true, MathGroupType.function) {
@NotNull
@Override
public List<String> getTokens() {
return CalculatorLocatorImpl.getInstance().getCalculatorEngine().getFunctionsRegistry().getNames();
}
},
operator(1050, true, true, MathGroupType.function) {
@NotNull
@Override
public List<String> getTokens() {
return CalculatorLocatorImpl.getInstance().getCalculatorEngine().getOperatorsRegistry().getNames();
}
},
constant(1100, true, true, MathGroupType.other) {
@NotNull
@Override
public List<String> getTokens() {
return CalculatorLocatorImpl.getInstance().getCalculatorEngine().getVarsRegistry().getNames();
}
@Override
protected String getSubstituteFromJscl(@NotNull String match) {
return Constants.INF_2.getName().equals(match) ? MathType.INFINITY : super.getSubstituteFromJscl(match);
}
},
digit(1125, true, true, MathGroupType.number) {
private final List<String> tokens = new ArrayList<String>(16);
{
for (Character character : NumeralBase.hex.getAcceptableCharacters()) {
tokens.add(character.toString());
}
}
@Override
public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) {
return super.isNeedMultiplicationSignBefore(mathTypeBefore) && mathTypeBefore != digit && mathTypeBefore != dot /*&& mathTypeBefore != numeral_base*/;
}
@NotNull
@Override
public List<String> getTokens() {
return tokens;
}
},
comma(1150, false, false, MathGroupType.other, ","),
text(1200, false, false, MathGroupType.other) {
@Override
public int processToJscl(@NotNull StringBuilder result, int i, @NotNull String match) {
if (match.length() > 0) {
result.append(match.charAt(0));
}
return i;
}
@Override
public int processFromJscl(@NotNull StringBuilder result, int i, @NotNull String match) {
if (match.length() > 0) {
result.append(match.charAt(0));
}
return i;
}
};
public static enum MathGroupType {
function,
number,
operation,
other
}
@NotNull
private final List<String> tokens;
@NotNull
private final Integer priority;
private final boolean needMultiplicationSignBefore;
private final boolean needMultiplicationSignAfter;
@NotNull
private final MathGroupType groupType;
MathType(@NotNull Integer priority,
boolean needMultiplicationSignBefore,
boolean needMultiplicationSignAfter,
@NotNull MathGroupType groupType,
@NotNull String... tokens) {
this(priority, needMultiplicationSignBefore, needMultiplicationSignAfter, groupType, CollectionsUtils.asList(tokens));
}
MathType(@NotNull Integer priority,
boolean needMultiplicationSignBefore,
boolean needMultiplicationSignAfter,
@NotNull MathGroupType groupType,
@NotNull List<String> tokens) {
this.priority = priority;
this.needMultiplicationSignBefore = needMultiplicationSignBefore;
this.needMultiplicationSignAfter = needMultiplicationSignAfter;
this.groupType = groupType;
this.tokens = Collections.unmodifiableList(tokens);
}
@NotNull
public MathGroupType getGroupType() {
return groupType;
}
/* public static int getPostfixFunctionStart(@NotNull CharSequence s, int position) throws ParseException {
assert s.length() > position;
int numberOfOpenGroups = 0;
int result = position;
for (; result >= 0; result--) {
final MathType mathType = getType(s.toString(), result).getMathType();
if (CollectionsUtils.contains(mathType, digit, dot, grouping_separator, power_10)) {
// continue
} else if (mathType == close_group_symbol) {
numberOfOpenGroups++;
} else if (mathType == open_group_symbol) {
if (numberOfOpenGroups > 0) {
numberOfOpenGroups--;
} else {
break;
}
} else {
if (stop(s, numberOfOpenGroups, result)) break;
}
}
if (numberOfOpenGroups != 0){
throw new ParseException("Could not find start of prefix function!");
}
return result;
}
public static boolean stop(CharSequence s, int numberOfOpenGroups, int i) {
if (numberOfOpenGroups == 0) {
if (i > 0) {
final EndsWithFinder endsWithFinder = new EndsWithFinder(s);
endsWithFinder.setI(i + 1);
if (!CollectionsUtils.contains(function.getTokens(), FilterType.included, endsWithFinder)) {
MathType type = getType(s.toString(), i).getMathType();
if (type != constant) {
return true;
}
}
} else {
return true;
}
}
return false;
}*/
@NotNull
public List<String> getTokens() {
return tokens;
}
private boolean isNeedMultiplicationSignBefore() {
return needMultiplicationSignBefore;
}
private boolean isNeedMultiplicationSignAfter() {
return needMultiplicationSignAfter;
}
public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) {
return needMultiplicationSignBefore && mathTypeBefore.isNeedMultiplicationSignAfter();
}
public int processToJscl(@NotNull StringBuilder result, int i, @NotNull String match) throws CalculatorParseException {
final String substitute = getSubstituteToJscl(match);
result.append(substitute == null ? match : substitute);
return returnI(i, match);
}
protected int returnI(int i, @NotNull String match) {
if (match.length() > 1) {
return i + match.length() - 1;
} else {
return i;
}
}
public int processFromJscl(@NotNull StringBuilder result, int i, @NotNull String match) {
final String substitute = getSubstituteFromJscl(match);
result.append(substitute == null ? match : substitute);
return returnI(i, match);
}
@Nullable
protected String getSubstituteFromJscl(@NotNull String match) {
return null;
}
@Nullable
protected String getSubstituteToJscl(@NotNull String match) {
return null;
}
public static final List<String> openGroupSymbols = Arrays.asList("[]", "()", "{}");
public final static Character POWER_10 = 'E';
public static final String IMAGINARY_NUMBER = "i";
public static final String IMAGINARY_NUMBER_JSCL = "√(-1)";
public static final String PI = "π";
public static final String E = "e";
public static final String C = "c";
public static final Double C_VALUE = 299792458d;
public static final String G = "G";
public static final Double G_VALUE = 6.6738480E-11;
public static final String H_REDUCED = "h";
public static final Double H_REDUCED_VALUE = 6.6260695729E-34 / ( 2 * Math.PI );
public final static String NAN = "NaN";
public final static String INFINITY = "";
public final static String INFINITY_JSCL = "Infinity";
/**
* Method determines mathematical entity type for text substring starting from ith index
*
*
* @param text analyzed text
* @param i index which points to start of substring
* @param hexMode
* @return math entity type of substring starting from ith index of specified text
*/
@NotNull
public static Result getType(@NotNull String text, int i, boolean hexMode) {
if (i < 0) {
throw new IllegalArgumentException("I must be more or equals to 0.");
} else if (i >= text.length() && i != 0) {
throw new IllegalArgumentException("I must be less than size of text.");
} else if (i == 0 && text.length() == 0) {
return new Result(MathType.text, text);
}
final StartsWithFinder startsWithFinder = new StartsWithFinder(text, i);
for (MathType mathType : getMathTypesByPriority()) {
final String s = CollectionsUtils.find(mathType.getTokens(), startsWithFinder);
if (s != null) {
if ( s.length() == 1 ) {
if (hexMode || JsclMathEngine.instance.getNumeralBase() == NumeralBase.hex) {
final Character ch = s.charAt(0);
if ( NumeralBase.hex.getAcceptableCharacters().contains(ch) ) {
return new Result(MathType.digit, s);
}
}
}
return new Result(mathType, s);
}
}
return new Result(MathType.text, text.substring(i));
}
private static List<MathType> mathTypesByPriority;
@NotNull
private static List<MathType> getMathTypesByPriority() {
if (mathTypesByPriority == null) {
final List<MathType> result = CollectionsUtils.asList(MathType.values());
Collections.sort(result, new Comparator<MathType>() {
@Override
public int compare(MathType l, MathType r) {
return l.priority.compareTo(r.priority);
}
});
mathTypesByPriority = result;
}
return mathTypesByPriority;
}
public static class Result {
@NotNull
private final MathType mathType;
@NotNull
private final String match;
public Result(@NotNull MathType mathType, @NotNull String match) {
this.mathType = mathType;
this.match = match;
}
public int processToJscl(@NotNull StringBuilder result, int i) throws CalculatorParseException {
return mathType.processToJscl(result, i, match);
}
public int processFromJscl(@NotNull StringBuilder result, int i) {
return mathType.processFromJscl(result, i, match);
}
@NotNull
public String getMatch() {
return match;
}
@NotNull
public MathType getMathType() {
return mathType;
}
}
private static class EndsWithFinder implements JPredicate<String> {
private int i;
@NotNull
private final CharSequence targetString;
private EndsWithFinder(@NotNull CharSequence targetString) {
this.targetString = targetString;
}
@Override
public boolean apply(@Nullable String s) {
return targetString.subSequence(0, i).toString().endsWith(s);
}
public void setI(int i) {
this.i = i;
}
}
}
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.calculator.math;
import jscl.JsclMathEngine;
import jscl.NumeralBase;
import jscl.math.function.Constants;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.CalculatorLocatorImpl;
import org.solovyev.common.JPredicate;
import org.solovyev.common.StartsWithFinder;
import org.solovyev.android.calculator.CalculatorParseException;
import org.solovyev.common.collections.CollectionsUtils;
import java.util.*;
public enum MathType {
numeral_base(50, true, false, MathGroupType.number) {
private final List<String> tokens = new ArrayList<String>(10);
{
for (NumeralBase numeralBase : NumeralBase.values()) {
tokens.add(numeralBase.getJsclPrefix());
}
}
@NotNull
@Override
public List<String> getTokens() {
return tokens;
}
},
dot(200, true, true, MathGroupType.number, ".") {
@Override
public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) {
return super.isNeedMultiplicationSignBefore(mathTypeBefore) && mathTypeBefore != digit;
}
},
grouping_separator(250, false, false, MathGroupType.number, "'", " "){
@Override
public int processToJscl(@NotNull StringBuilder result, int i, @NotNull String match) throws CalculatorParseException {
return i;
}
},
power_10(300, false, false, MathGroupType.number, "E"),
postfix_function(400, false, true, MathGroupType.function) {
@NotNull
@Override
public List<String> getTokens() {
return CalculatorLocatorImpl.getInstance().getEngine().getPostfixFunctionsRegistry().getNames();
}
},
unary_operation(500, false, false, MathGroupType.operation, "-", "="),
binary_operation(600, false, false, MathGroupType.operation, "-", "+", "*", "×", "", "/", "^") {
@Override
protected String getSubstituteToJscl(@NotNull String match) {
if (match.equals("×") || match.equals("")) {
return "*";
} else {
return null;
}
}
},
open_group_symbol(800, true, false, MathGroupType.other, "[", "(", "{") {
@Override
public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) {
return super.isNeedMultiplicationSignBefore(mathTypeBefore) && mathTypeBefore != function && mathTypeBefore != operator;
}
@Override
protected String getSubstituteToJscl(@NotNull String match) {
return "(";
}
},
close_group_symbol(900, false, true, MathGroupType.other, "]", ")", "}") {
@Override
public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) {
return false;
}
@Override
protected String getSubstituteToJscl(@NotNull String match) {
return ")";
}
},
function(1000, true, true, MathGroupType.function) {
@NotNull
@Override
public List<String> getTokens() {
return CalculatorLocatorImpl.getInstance().getEngine().getFunctionsRegistry().getNames();
}
},
operator(1050, true, true, MathGroupType.function) {
@NotNull
@Override
public List<String> getTokens() {
return CalculatorLocatorImpl.getInstance().getEngine().getOperatorsRegistry().getNames();
}
},
constant(1100, true, true, MathGroupType.other) {
@NotNull
@Override
public List<String> getTokens() {
return CalculatorLocatorImpl.getInstance().getEngine().getVarsRegistry().getNames();
}
@Override
protected String getSubstituteFromJscl(@NotNull String match) {
return Constants.INF_2.getName().equals(match) ? MathType.INFINITY : super.getSubstituteFromJscl(match);
}
},
digit(1125, true, true, MathGroupType.number) {
private final List<String> tokens = new ArrayList<String>(16);
{
for (Character character : NumeralBase.hex.getAcceptableCharacters()) {
tokens.add(character.toString());
}
}
@Override
public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) {
return super.isNeedMultiplicationSignBefore(mathTypeBefore) && mathTypeBefore != digit && mathTypeBefore != dot /*&& mathTypeBefore != numeral_base*/;
}
@NotNull
@Override
public List<String> getTokens() {
return tokens;
}
},
comma(1150, false, false, MathGroupType.other, ","),
text(1200, false, false, MathGroupType.other) {
@Override
public int processToJscl(@NotNull StringBuilder result, int i, @NotNull String match) {
if (match.length() > 0) {
result.append(match.charAt(0));
}
return i;
}
@Override
public int processFromJscl(@NotNull StringBuilder result, int i, @NotNull String match) {
if (match.length() > 0) {
result.append(match.charAt(0));
}
return i;
}
};
public static enum MathGroupType {
function,
number,
operation,
other
}
@NotNull
private final List<String> tokens;
@NotNull
private final Integer priority;
private final boolean needMultiplicationSignBefore;
private final boolean needMultiplicationSignAfter;
@NotNull
private final MathGroupType groupType;
MathType(@NotNull Integer priority,
boolean needMultiplicationSignBefore,
boolean needMultiplicationSignAfter,
@NotNull MathGroupType groupType,
@NotNull String... tokens) {
this(priority, needMultiplicationSignBefore, needMultiplicationSignAfter, groupType, CollectionsUtils.asList(tokens));
}
MathType(@NotNull Integer priority,
boolean needMultiplicationSignBefore,
boolean needMultiplicationSignAfter,
@NotNull MathGroupType groupType,
@NotNull List<String> tokens) {
this.priority = priority;
this.needMultiplicationSignBefore = needMultiplicationSignBefore;
this.needMultiplicationSignAfter = needMultiplicationSignAfter;
this.groupType = groupType;
this.tokens = Collections.unmodifiableList(tokens);
}
@NotNull
public MathGroupType getGroupType() {
return groupType;
}
/* public static int getPostfixFunctionStart(@NotNull CharSequence s, int position) throws ParseException {
assert s.length() > position;
int numberOfOpenGroups = 0;
int result = position;
for (; result >= 0; result--) {
final MathType mathType = getType(s.toString(), result).getMathType();
if (CollectionsUtils.contains(mathType, digit, dot, grouping_separator, power_10)) {
// continue
} else if (mathType == close_group_symbol) {
numberOfOpenGroups++;
} else if (mathType == open_group_symbol) {
if (numberOfOpenGroups > 0) {
numberOfOpenGroups--;
} else {
break;
}
} else {
if (stop(s, numberOfOpenGroups, result)) break;
}
}
if (numberOfOpenGroups != 0){
throw new ParseException("Could not find start of prefix function!");
}
return result;
}
public static boolean stop(CharSequence s, int numberOfOpenGroups, int i) {
if (numberOfOpenGroups == 0) {
if (i > 0) {
final EndsWithFinder endsWithFinder = new EndsWithFinder(s);
endsWithFinder.setI(i + 1);
if (!CollectionsUtils.contains(function.getTokens(), FilterType.included, endsWithFinder)) {
MathType type = getType(s.toString(), i).getMathType();
if (type != constant) {
return true;
}
}
} else {
return true;
}
}
return false;
}*/
@NotNull
public List<String> getTokens() {
return tokens;
}
private boolean isNeedMultiplicationSignBefore() {
return needMultiplicationSignBefore;
}
private boolean isNeedMultiplicationSignAfter() {
return needMultiplicationSignAfter;
}
public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) {
return needMultiplicationSignBefore && mathTypeBefore.isNeedMultiplicationSignAfter();
}
public int processToJscl(@NotNull StringBuilder result, int i, @NotNull String match) throws CalculatorParseException {
final String substitute = getSubstituteToJscl(match);
result.append(substitute == null ? match : substitute);
return returnI(i, match);
}
protected int returnI(int i, @NotNull String match) {
if (match.length() > 1) {
return i + match.length() - 1;
} else {
return i;
}
}
public int processFromJscl(@NotNull StringBuilder result, int i, @NotNull String match) {
final String substitute = getSubstituteFromJscl(match);
result.append(substitute == null ? match : substitute);
return returnI(i, match);
}
@Nullable
protected String getSubstituteFromJscl(@NotNull String match) {
return null;
}
@Nullable
protected String getSubstituteToJscl(@NotNull String match) {
return null;
}
public static final List<String> openGroupSymbols = Arrays.asList("[]", "()", "{}");
public final static Character POWER_10 = 'E';
public static final String IMAGINARY_NUMBER = "i";
public static final String IMAGINARY_NUMBER_JSCL = "√(-1)";
public static final String PI = "π";
public static final String E = "e";
public static final String C = "c";
public static final Double C_VALUE = 299792458d;
public static final String G = "G";
public static final Double G_VALUE = 6.6738480E-11;
public static final String H_REDUCED = "h";
public static final Double H_REDUCED_VALUE = 6.6260695729E-34 / ( 2 * Math.PI );
public final static String NAN = "NaN";
public final static String INFINITY = "";
public final static String INFINITY_JSCL = "Infinity";
/**
* Method determines mathematical entity type for text substring starting from ith index
*
*
* @param text analyzed text
* @param i index which points to start of substring
* @param hexMode
* @return math entity type of substring starting from ith index of specified text
*/
@NotNull
public static Result getType(@NotNull String text, int i, boolean hexMode) {
if (i < 0) {
throw new IllegalArgumentException("I must be more or equals to 0.");
} else if (i >= text.length() && i != 0) {
throw new IllegalArgumentException("I must be less than size of text.");
} else if (i == 0 && text.length() == 0) {
return new Result(MathType.text, text);
}
final StartsWithFinder startsWithFinder = new StartsWithFinder(text, i);
for (MathType mathType : getMathTypesByPriority()) {
final String s = CollectionsUtils.find(mathType.getTokens(), startsWithFinder);
if (s != null) {
if ( s.length() == 1 ) {
if (hexMode || JsclMathEngine.instance.getNumeralBase() == NumeralBase.hex) {
final Character ch = s.charAt(0);
if ( NumeralBase.hex.getAcceptableCharacters().contains(ch) ) {
return new Result(MathType.digit, s);
}
}
}
return new Result(mathType, s);
}
}
return new Result(MathType.text, text.substring(i));
}
private static List<MathType> mathTypesByPriority;
@NotNull
private static List<MathType> getMathTypesByPriority() {
if (mathTypesByPriority == null) {
final List<MathType> result = CollectionsUtils.asList(MathType.values());
Collections.sort(result, new Comparator<MathType>() {
@Override
public int compare(MathType l, MathType r) {
return l.priority.compareTo(r.priority);
}
});
mathTypesByPriority = result;
}
return mathTypesByPriority;
}
public static class Result {
@NotNull
private final MathType mathType;
@NotNull
private final String match;
public Result(@NotNull MathType mathType, @NotNull String match) {
this.mathType = mathType;
this.match = match;
}
public int processToJscl(@NotNull StringBuilder result, int i) throws CalculatorParseException {
return mathType.processToJscl(result, i, match);
}
public int processFromJscl(@NotNull StringBuilder result, int i) {
return mathType.processFromJscl(result, i, match);
}
@NotNull
public String getMatch() {
return match;
}
@NotNull
public MathType getMathType() {
return mathType;
}
}
private static class EndsWithFinder implements JPredicate<String> {
private int i;
@NotNull
private final CharSequence targetString;
private EndsWithFinder(@NotNull CharSequence targetString) {
this.targetString = targetString;
}
@Override
public boolean apply(@Nullable String s) {
return targetString.subSequence(0, i).toString().endsWith(s);
}
public void setI(int i) {
this.i = i;
}
}
}

View File

@@ -1,94 +1,94 @@
package org.solovyev.android.calculator.text;
import jscl.math.Generic;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.CalculatorLocatorImpl;
import org.solovyev.android.calculator.math.MathType;
import org.solovyev.android.calculator.CalculatorParseException;
import java.util.Arrays;
import java.util.List;
/**
* User: serso
* Date: 10/20/11
* Time: 2:59 PM
*/
public class FromJsclSimplifyTextProcessor implements TextProcessor<String, Generic> {
public static final FromJsclSimplifyTextProcessor instance = new FromJsclSimplifyTextProcessor();
public FromJsclSimplifyTextProcessor() {
}
@NotNull
@Override
public String process(@NotNull Generic from) throws CalculatorParseException {
return removeMultiplicationSigns(from.toString());
}
public String process(@NotNull String s) {
return removeMultiplicationSigns(s);
}
@NotNull
private String removeMultiplicationSigns(String s) {
final StringBuilder sb = new StringBuilder();
MathType.Result mathTypeBefore;
MathType.Result mathType = null;
MathType.Result mathTypeAfter = null;
for (int i = 0; i < s.length(); i++) {
mathTypeBefore = mathType;
if (mathTypeAfter == null) {
mathType = MathType.getType(s, i, false);
} else {
mathType = mathTypeAfter;
}
char ch = s.charAt(i);
if (ch == '*') {
if (i + 1 < s.length()) {
mathTypeAfter = MathType.getType(s, i + 1, false);
} else {
mathTypeAfter = null;
}
if (needMultiplicationSign(mathTypeBefore == null ? null : mathTypeBefore.getMathType(), mathTypeAfter == null ? null : mathTypeAfter.getMathType())) {
sb.append(CalculatorLocatorImpl.getInstance().getCalculatorEngine().getMultiplicationSign());
}
} else {
if (mathType.getMathType() == MathType.constant || mathType.getMathType() == MathType.function || mathType.getMathType() == MathType.operator) {
sb.append(mathType.getMatch());
i += mathType.getMatch().length() - 1;
} else {
sb.append(ch);
}
mathTypeAfter = null;
}
}
return sb.toString();
}
private final List<MathType> mathTypes = Arrays.asList(MathType.function, MathType.constant);
private boolean needMultiplicationSign(@Nullable MathType mathTypeBefore, @Nullable MathType mathTypeAfter) {
if (mathTypeBefore == null || mathTypeAfter == null) {
return true;
} else if (mathTypes.contains(mathTypeBefore) || mathTypes.contains(mathTypeAfter)) {
return false;
} else if ( mathTypeBefore == MathType.close_group_symbol ) {
return false;
} else if ( mathTypeAfter == MathType.open_group_symbol ) {
return false;
}
return true;
}
}
package org.solovyev.android.calculator.text;
import jscl.math.Generic;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.CalculatorLocatorImpl;
import org.solovyev.android.calculator.math.MathType;
import org.solovyev.android.calculator.CalculatorParseException;
import java.util.Arrays;
import java.util.List;
/**
* User: serso
* Date: 10/20/11
* Time: 2:59 PM
*/
public class FromJsclSimplifyTextProcessor implements TextProcessor<String, Generic> {
public static final FromJsclSimplifyTextProcessor instance = new FromJsclSimplifyTextProcessor();
public FromJsclSimplifyTextProcessor() {
}
@NotNull
@Override
public String process(@NotNull Generic from) throws CalculatorParseException {
return removeMultiplicationSigns(from.toString());
}
public String process(@NotNull String s) {
return removeMultiplicationSigns(s);
}
@NotNull
private String removeMultiplicationSigns(String s) {
final StringBuilder sb = new StringBuilder();
MathType.Result mathTypeBefore;
MathType.Result mathType = null;
MathType.Result mathTypeAfter = null;
for (int i = 0; i < s.length(); i++) {
mathTypeBefore = mathType;
if (mathTypeAfter == null) {
mathType = MathType.getType(s, i, false);
} else {
mathType = mathTypeAfter;
}
char ch = s.charAt(i);
if (ch == '*') {
if (i + 1 < s.length()) {
mathTypeAfter = MathType.getType(s, i + 1, false);
} else {
mathTypeAfter = null;
}
if (needMultiplicationSign(mathTypeBefore == null ? null : mathTypeBefore.getMathType(), mathTypeAfter == null ? null : mathTypeAfter.getMathType())) {
sb.append(CalculatorLocatorImpl.getInstance().getEngine().getMultiplicationSign());
}
} else {
if (mathType.getMathType() == MathType.constant || mathType.getMathType() == MathType.function || mathType.getMathType() == MathType.operator) {
sb.append(mathType.getMatch());
i += mathType.getMatch().length() - 1;
} else {
sb.append(ch);
}
mathTypeAfter = null;
}
}
return sb.toString();
}
private final List<MathType> mathTypes = Arrays.asList(MathType.function, MathType.constant);
private boolean needMultiplicationSign(@Nullable MathType mathTypeBefore, @Nullable MathType mathTypeAfter) {
if (mathTypeBefore == null || mathTypeAfter == null) {
return true;
} else if (mathTypes.contains(mathTypeBefore) || mathTypes.contains(mathTypeAfter)) {
return false;
} else if ( mathTypeBefore == MathType.close_group_symbol ) {
return false;
} else if ( mathTypeAfter == MathType.open_group_symbol ) {
return false;
}
return true;
}
}