Revert recent changes, see dev branch

This commit is contained in:
serso
2012-09-21 10:38:07 +04:00
parent cca775c2ac
commit db9b676cfa
89 changed files with 2161 additions and 3776 deletions

View File

@@ -19,13 +19,6 @@
<dependencies>
<!-- OWN -->
<dependency>
<groupId>org.solovyev.android</groupId>
<artifactId>calculatorpp-core</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>org.solovyev</groupId>
<artifactId>common-core</artifactId>
@@ -82,6 +75,13 @@
<dependency>
<groupId>org.solovyev</groupId>
<artifactId>jscl</artifactId>
<version>0.0.2</version>
<exclusions>
<exclusion>
<artifactId>xercesImpl</artifactId>
<groupId>xerces</groupId>
</exclusion>
</exclusions>
</dependency>
<!--OTHER-->
@@ -92,6 +92,13 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.sf.opencsv</groupId>
<artifactId>opencsv</artifactId>
@@ -102,6 +109,17 @@
<dependency>
<groupId>org.simpleframework</groupId>
<artifactId>simple-xml</artifactId>
<version>2.6.1</version>
<exclusions>
<exclusion>
<artifactId>stax-api</artifactId>
<groupId>stax</groupId>
</exclusion>
<exclusion>
<artifactId>xpp3</artifactId>
<groupId>xpp3</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
@@ -129,15 +147,10 @@
<version>11.0.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.intellij</groupId>
<artifactId>annotations</artifactId>
<version>7.0.3</version>
</dependency>
</dependencies>

View File

@@ -1,16 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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
-->
<org.solovyev.android.calculator.AndroidCalculatorDisplayView
xmlns:a="http://schemas.android.com/apk/res/android"
a:id="@+id/calculatorDisplay"
style="@style/display_style"
a:inputType="textMultiLine"
a:maxLines="3"
a:scrollHorizontally="false"
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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
-->
<org.solovyev.android.calculator.CalculatorDisplay
xmlns:a="http://schemas.android.com/apk/res/android"
a:id="@+id/calculatorDisplay"
style="@style/display_style"
a:inputType="textMultiLine"
a:maxLines="3"
a:scrollHorizontally="false"
a:scrollbars="none"/>

View File

@@ -0,0 +1,17 @@
package org.solovyev.android;
import org.jetbrains.annotations.NotNull;
/**
* User: serso
* Date: 4/21/12
* Time: 7:54 PM
*/
public interface Unit<V> {
@NotNull
V getValue();
@NotNull
UnitType<V> getUnitType();
}

View File

@@ -0,0 +1,42 @@
package org.solovyev.android;
import org.jetbrains.annotations.NotNull;
/**
* User: serso
* Date: 4/21/12
* Time: 7:53 PM
*/
public interface UnitConverter<T> {
boolean isSupported(@NotNull UnitType<?> from, @NotNull UnitType<T> to);
@NotNull
Unit<T> convert(@NotNull Unit<?> from, @NotNull UnitType<T> toType);
public static class Dummy implements UnitConverter<Object> {
@NotNull
private static final Dummy instance = new Dummy();
@NotNull
public static <T> UnitConverter<T> getInstance() {
return (UnitConverter<T>)instance;
}
private Dummy() {
}
@Override
public boolean isSupported(@NotNull UnitType<?> from, @NotNull UnitType<Object> to) {
return false;
}
@NotNull
@Override
public Unit<Object> convert(@NotNull Unit<?> from, @NotNull UnitType<Object> toType) {
throw new IllegalArgumentException();
}
}
}

View File

@@ -0,0 +1,42 @@
package org.solovyev.android;
import org.jetbrains.annotations.NotNull;
/**
* User: serso
* Date: 4/21/12
* Time: 8:01 PM
*/
public class UnitImpl<V> implements Unit<V> {
@NotNull
private V value;
@NotNull
private UnitType<V> unitType;
private UnitImpl() {
}
@NotNull
public static <V> Unit<V> newInstance(@NotNull V value, @NotNull UnitType<V> unitType) {
final UnitImpl<V> result = new UnitImpl<V>();
result.value = value;
result.unitType = unitType;
return result;
}
@NotNull
@Override
public V getValue() {
return this.value;
}
@NotNull
@Override
public UnitType<V> getUnitType() {
return unitType;
}
}

View File

@@ -0,0 +1,16 @@
package org.solovyev.android;
import org.jetbrains.annotations.NotNull;
/**
* User: serso
* Date: 4/21/12
* Time: 7:55 PM
*/
public interface UnitType<V> {
@NotNull
Class<V> getUnitValueClass();
boolean equals(@NotNull Object o);
}

View File

@@ -3,10 +3,10 @@ package org.solovyev.android.calculator;
import android.app.Activity;
import jscl.NumeralBase;
import org.jetbrains.annotations.NotNull;
import org.solovyev.math.units.Unit;
import org.solovyev.math.units.UnitConverter;
import org.solovyev.math.units.UnitImpl;
import org.solovyev.math.units.UnitType;
import org.solovyev.android.Unit;
import org.solovyev.android.UnitConverter;
import org.solovyev.android.UnitImpl;
import org.solovyev.android.UnitType;
import org.solovyev.android.view.drag.DirectionDragButton;
import org.solovyev.android.view.drag.DragDirection;

View File

@@ -32,7 +32,7 @@ import org.solovyev.android.AndroidUtils;
import org.solovyev.android.FontSizeAdjuster;
import org.solovyev.android.LocalBinder;
import org.solovyev.android.calculator.about.CalculatorReleaseNotesActivity;
import org.solovyev.android.calculator.history.AndroidCalculatorHistoryImpl;
import org.solovyev.android.calculator.history.CalculatorHistory;
import org.solovyev.android.calculator.history.CalculatorHistoryState;
import org.solovyev.android.calculator.model.CalculatorEngine;
import org.solovyev.android.calculator.view.AngleUnitsButton;
@@ -136,7 +136,7 @@ public class CalculatorActivity extends Activity implements FontSizeAdjuster, Sh
vibrator = (Vibrator) this.getSystemService(VIBRATOR_SERVICE);
AndroidCalculatorHistoryImpl.instance.load(this, preferences);
CalculatorHistory.instance.load(this, preferences);
calculatorModel = CalculatorModel.instance.init(this, preferences, CalculatorEngine.instance);
dpclRegister.clear();

View File

@@ -46,8 +46,6 @@ public class CalculatorApplication extends android.app.Application {
public void onCreate() {
super.onCreate();
CalculatorLocatorImpl.getInstance().setCalculatorEngine(CalculatorEngine.instance);
AdsController.getInstance().init(ADMOB_USER_ID, AD_FREE_PRODUCT_ID, new BillingController.IConfiguration() {
@Override

View File

@@ -15,9 +15,12 @@ import jscl.math.Generic;
import jscl.math.function.Constant;
import jscl.math.function.IConstant;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.calculator.model.CalculatorEngine;
import org.solovyev.android.calculator.text.TextProcessor;
import org.solovyev.android.calculator.model.CalculatorParseException;
import org.solovyev.android.calculator.model.TextProcessor;
import org.solovyev.android.calculator.model.ToJsclTextProcessor;
import org.solovyev.android.calculator.view.NumeralBaseConverterDialog;
import org.solovyev.android.calculator.view.TextHighlighter;
import org.solovyev.android.calculator.view.UnitConverterViewBuilder;
@@ -35,9 +38,9 @@ import java.util.Set;
* Date: 9/17/11
* Time: 10:58 PM
*/
public class AndroidCalculatorDisplayView extends AutoResizeTextView implements CalculatorDisplayView {
public class CalculatorDisplay extends AutoResizeTextView implements ICalculatorDisplay{
private static enum ConversionMenuItem implements AMenuItem<CalculatorDisplayView> {
private static enum ConversionMenuItem implements AMenuItem<CalculatorDisplay> {
convert_to_bin(NumeralBase.bin),
convert_to_dec(NumeralBase.dec),
convert_to_hex(NumeralBase.hex);
@@ -70,27 +73,23 @@ public class AndroidCalculatorDisplayView extends AutoResizeTextView implements
}
@Override
public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) {
public void onClick(@NotNull CalculatorDisplay data, @NotNull Context context) {
final NumeralBase fromNumeralBase = CalculatorEngine.instance.getEngine().getNumeralBase();
final Generic lastResult = CalculatorLocatorImpl.getInstance().getCalculatorDisplay().getViewState().getResult();
String to;
try {
to = convert(data.getGenericResult());
if (lastResult != null) {
String to;
try {
to = convert(lastResult);
// add prefix
if (fromNumeralBase != toNumeralBase) {
to = toNumeralBase.getJsclPrefix() + to;
}
} catch (UnitConverterViewBuilder.ConversionException e) {
to = context.getString(R.string.c_error);
// add prefix
if (fromNumeralBase != toNumeralBase) {
to = toNumeralBase.getJsclPrefix() + to;
}
data.setText(to);
//data.redraw();
} catch (UnitConverterViewBuilder.ConversionException e) {
to = context.getString(R.string.c_error);
}
data.setText(to);
data.redraw();
}
@NotNull
@@ -114,18 +113,18 @@ public class AndroidCalculatorDisplayView extends AutoResizeTextView implements
}
}
public static enum MenuItem implements LabeledMenuItem<CalculatorDisplayView> {
public static enum MenuItem implements LabeledMenuItem<CalculatorDisplay> {
copy(R.string.c_copy) {
@Override
public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) {
public void onClick(@NotNull CalculatorDisplay data, @NotNull Context context) {
CalculatorModel.copyResult(context, data);
}
},
convert_to_bin(R.string.convert_to_bin) {
@Override
public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) {
public void onClick(@NotNull CalculatorDisplay data, @NotNull Context context) {
ConversionMenuItem.convert_to_bin.onClick(data, context);
}
@@ -137,7 +136,7 @@ public class AndroidCalculatorDisplayView extends AutoResizeTextView implements
convert_to_dec(R.string.convert_to_dec) {
@Override
public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) {
public void onClick(@NotNull CalculatorDisplay data, @NotNull Context context) {
ConversionMenuItem.convert_to_dec.onClick(data, context);
}
@@ -149,7 +148,7 @@ public class AndroidCalculatorDisplayView extends AutoResizeTextView implements
convert_to_hex(R.string.convert_to_hex) {
@Override
public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) {
public void onClick(@NotNull CalculatorDisplay data, @NotNull Context context) {
ConversionMenuItem.convert_to_hex.onClick(data, context);
}
@@ -161,11 +160,8 @@ public class AndroidCalculatorDisplayView extends AutoResizeTextView implements
convert(R.string.c_convert) {
@Override
public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) {
final Generic result = data.getState().getResult();
if (result != null) {
new NumeralBaseConverterDialog(result.toString()).show(context);
}
public void onClick(@NotNull CalculatorDisplay data, @NotNull Context context) {
new NumeralBaseConverterDialog(data.getGenericResult().toString()).show(context);
}
@Override
@@ -174,10 +170,10 @@ public class AndroidCalculatorDisplayView extends AutoResizeTextView implements
}
},
plot(R.string.c_plot) {
plot(R.string.c_plot) {
@Override
public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) {
final Generic generic = data.getState().getResult();
public void onClick(@NotNull CalculatorDisplay data, @NotNull Context context) {
final Generic generic = data.getGenericResult();
assert generic != null;
final Constant constant = CollectionsUtils.getFirstCollectionElement(getNotSystemConstants(generic));
@@ -185,18 +181,18 @@ public class AndroidCalculatorDisplayView extends AutoResizeTextView implements
CalculatorActivityLauncher.plotGraph(context, generic, constant);
}
@Override
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
boolean result = false;
@Override
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
boolean result = false;
if (operation == JsclOperation.simplify) {
if (operation == JsclOperation.simplify) {
if (getNotSystemConstants(generic).size() == 1) {
result = true;
}
}
result = true;
}
}
return result;
}
return result;
}
@NotNull
private Set<Constant> getNotSystemConstants(@NotNull Generic generic) {
@@ -213,104 +209,140 @@ public class AndroidCalculatorDisplayView extends AutoResizeTextView implements
}
};
private final int captionId;
private final int captionId;
MenuItem(int captionId) {
this.captionId = captionId;
}
MenuItem(int captionId) {
this.captionId = captionId;
}
public final boolean isItemVisible(@NotNull CalculatorDisplayViewState displayViewState) {
//noinspection ConstantConditions
return displayViewState.isValid() && displayViewState.getResult() != null && isItemVisibleFor(displayViewState.getResult(), displayViewState.getOperation());
}
public final boolean isItemVisible(@NotNull CalculatorDisplay display) {
//noinspection ConstantConditions
return display.isValid() && display.getGenericResult() != null && isItemVisibleFor(display.getGenericResult(), display.getJsclOperation());
}
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
return true;
}
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
return true;
}
@NotNull
@Override
public String getCaption(@NotNull Context context) {
return context.getString(captionId);
}
}
@NotNull
@Override
public String getCaption(@NotNull Context context) {
return context.getString(captionId);
}
}
@NotNull
private CalculatorDisplayViewState state = CalculatorDisplayViewStateImpl.newDefaultInstance();
private boolean valid = true;
@NotNull
private final static TextProcessor<TextHighlighter.Result, String> textHighlighter = new TextHighlighter(Color.WHITE, false, CalculatorEngine.instance.getEngine());
@Nullable
private String errorMessage;
public AndroidCalculatorDisplayView(Context context) {
super(context);
}
@NotNull
private JsclOperation jsclOperation = JsclOperation.numeric;
public AndroidCalculatorDisplayView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@NotNull
private final static TextProcessor<TextHighlighter.Result, String> textHighlighter = new TextHighlighter(Color.WHITE, false, CalculatorEngine.instance.getEngine());
public AndroidCalculatorDisplayView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Nullable
private Generic genericResult;
public boolean isValid() {
return this.state.isValid();
}
public CalculatorDisplay(Context context) {
super(context);
}
public CalculatorDisplay(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void setState(@NotNull CalculatorDisplayViewState state) {
this.state = state;
if ( state.isValid() ) {
setTextColor(getResources().getColor(R.color.default_text_color));
setText(state.getStringResult());
} else {
setTextColor(getResources().getColor(R.color.display_error_text_color));
setText(state.getErrorMessage());
}
}
public CalculatorDisplay(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@NotNull
@Override
public CalculatorDisplayViewState getState() {
return this.state;
}
@Override
public boolean isValid() {
return valid;
}
@Override
public void setText(CharSequence text, BufferType type) {
super.setText(text, type);
}
@Override
public void setValid(boolean valid) {
this.valid = valid;
if (valid) {
errorMessage = null;
setTextColor(getResources().getColor(R.color.default_text_color));
} else {
setTextColor(getResources().getColor(R.color.display_error_text_color));
}
}
public synchronized void redraw() {
if (isValid()) {
String text = getText().toString();
@Override
@Nullable
public String getErrorMessage() {
return errorMessage;
}
Log.d(this.getClass().getName(), text);
@Override
public void setErrorMessage(@Nullable String errorMessage) {
this.errorMessage = errorMessage;
}
try {
TextHighlighter.Result result = textHighlighter.process(text);
text = result.toString();
} catch (CalculatorParseException e) {
Log.e(this.getClass().getName(), e.getMessage(), e);
}
@Override
public void setJsclOperation(@NotNull JsclOperation jsclOperation) {
this.jsclOperation = jsclOperation;
}
Log.d(this.getClass().getName(), text);
super.setText(Html.fromHtml(text), BufferType.EDITABLE);
}
@Override
@NotNull
public JsclOperation getJsclOperation() {
return jsclOperation;
}
// todo serso: think where to move it (keep in mind org.solovyev.android.view.AutoResizeTextView.resetTextSize())
setAddEllipsis(false);
setMinTextSize(10);
resizeText();
}
@Override
public void setText(CharSequence text, BufferType type) {
super.setText(text, type);
@Override
public int getSelection() {
return this.getSelectionStart();
}
setValid(true);
}
@Override
public void setSelection(int selection) {
// not supported by TextView
}
public synchronized void redraw() {
if (isValid()) {
String text = getText().toString();
Log.d(this.getClass().getName(), text);
try {
TextHighlighter.Result result = textHighlighter.process(text);
text = result.toString();
} catch (CalculatorParseException e) {
Log.e(this.getClass().getName(), e.getMessage(), e);
}
Log.d(this.getClass().getName(), text);
super.setText(Html.fromHtml(text), BufferType.EDITABLE);
}
// todo serso: think where to move it (keep in mind org.solovyev.android.view.AutoResizeTextView.resetTextSize())
setAddEllipsis(false);
setMinTextSize(10);
resizeText();
}
@Override
public void setGenericResult(@Nullable Generic genericResult) {
this.genericResult = genericResult;
}
@Override
@Nullable
public Generic getGenericResult() {
return genericResult;
}
@Override
public int getSelection() {
return this.getSelectionStart();
}
@Override
public void setSelection(int selection) {
// not supported by TextView
}
}

View File

@@ -16,7 +16,8 @@ import android.view.ContextMenu;
import android.widget.EditText;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.model.CalculatorEngine;
import org.solovyev.android.calculator.text.TextProcessor;
import org.solovyev.android.calculator.model.CalculatorParseException;
import org.solovyev.android.calculator.model.TextProcessor;
import org.solovyev.android.calculator.view.TextHighlighter;
import org.solovyev.common.collections.CollectionsUtils;

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

@@ -20,12 +20,14 @@ import android.widget.Toast;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.CursorControl;
import org.solovyev.android.calculator.history.AndroidCalculatorHistoryImpl;
import org.solovyev.android.calculator.history.CalculatorHistory;
import org.solovyev.android.calculator.history.CalculatorHistoryState;
import org.solovyev.android.calculator.history.TextViewEditorAdapter;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.calculator.math.MathType;
import org.solovyev.android.calculator.model.CalculatorEngine;
import org.solovyev.android.calculator.model.CalculatorEvalException;
import org.solovyev.android.calculator.model.CalculatorParseException;
import org.solovyev.android.history.HistoryControl;
import org.solovyev.android.menu.AMenuBuilder;
import org.solovyev.android.menu.MenuImpl;
@@ -53,7 +55,7 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
private CalculatorEditor editor;
@NotNull
private AndroidCalculatorDisplayView display;
private CalculatorDisplay display;
@NotNull
private CalculatorEngine calculatorEngine;
@@ -66,10 +68,10 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
this.editor.init(preferences);
preferences.registerOnSharedPreferenceChangeListener(editor);
this.display = (AndroidCalculatorDisplayView) activity.findViewById(R.id.calculatorDisplay);
this.display = (CalculatorDisplay) activity.findViewById(R.id.calculatorDisplay);
this.display.setOnClickListener(new CalculatorDisplayOnClickListener(activity));
final CalculatorHistoryState lastState = AndroidCalculatorHistoryImpl.instance.getLastHistoryState();
final CalculatorHistoryState lastState = CalculatorHistory.instance.getLastHistoryState();
if (lastState == null) {
saveHistoryState();
} else {
@@ -97,9 +99,8 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
copyResult(context, display);
}
public static void copyResult(@NotNull Context context, @NotNull final CalculatorDisplayView display) {
final CalculatorDisplayViewState displayViewState = display.getState();
if (displayViewState.isValid()) {
public static void copyResult(@NotNull Context context, @NotNull final CalculatorDisplay display) {
if (display.isValid()) {
final CharSequence text = display.getText();
if (!StringUtils.isEmpty(text)) {
final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE);
@@ -110,7 +111,7 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
}
private void saveHistoryState() {
AndroidCalculatorHistoryImpl.instance.addState(getCurrentHistoryState());
CalculatorHistory.instance.addState(getCurrentHistoryState());
}
public void setCursorOnStart() {
@@ -196,14 +197,14 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
if (delayEvaluate) {
if (historyState == null) {
AndroidCalculatorHistoryImpl.instance.addState(localHistoryState);
CalculatorHistory.instance.addState(localHistoryState);
}
// todo serso: this is not correct - operation is processing still in the same thread
new Handler().postDelayed(pendingOperation.getObject(), EVAL_DELAY_MILLIS);
} else {
pendingOperation.getObject().run();
if (historyState == null) {
AndroidCalculatorHistoryImpl.instance.addState(localHistoryState);
CalculatorHistory.instance.addState(localHistoryState);
}
}
}
@@ -229,16 +230,16 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
if (!StringUtils.isEmpty(expression)) {
try {
Log.d(CalculatorModel.class.getName(), "Trying to evaluate '" + operation + "': " + expression /*+ StringUtils.fromStackTrace(Thread.currentThread().getStackTrace())*/);
final CalculatorOutput result = calculatorEngine.evaluate(operation, expression);
final CalculatorEngine.Result result = calculatorEngine.evaluate(operation, expression);
// todo serso: second condition might replaced with expression.equals(this.editor.getText().toString()) ONLY if expression will be formatted with text highlighter
if (currentRunner == pendingOperation.getObject() && this.editor.getText().length() > 0) {
display.setText(result.getStringResult());
display.setText(result.getResult());
} else {
display.setText("");
}
display.setJsclOperation(result.getOperation());
display.setGenericResult(result.getResult());
display.setJsclOperation(result.getUserOperation());
display.setGenericResult(result.getGenericResult());
} catch (CalculatorParseException e) {
handleEvaluationException(expression, display, operation, e);
} catch (CalculatorEvalException e) {
@@ -256,7 +257,7 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
}
private void handleEvaluationException(@NotNull String expression,
@NotNull AndroidCalculatorDisplayView localDisplay,
@NotNull CalculatorDisplay localDisplay,
@NotNull JsclOperation operation,
@NotNull Message e) {
Log.d(CalculatorModel.class.getName(), "Evaluation failed for : " + expression + ". Error message: " + e);
@@ -330,9 +331,9 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
@Override
public void doHistoryAction(@NotNull HistoryAction historyAction) {
synchronized (AndroidCalculatorHistoryImpl.instance) {
if (AndroidCalculatorHistoryImpl.instance.isActionAvailable(historyAction)) {
final CalculatorHistoryState newState = AndroidCalculatorHistoryImpl.instance.doAction(historyAction, getCurrentHistoryState());
synchronized (CalculatorHistory.instance) {
if (CalculatorHistory.instance.isActionAvailable(historyAction)) {
final CalculatorHistoryState newState = CalculatorHistory.instance.doAction(historyAction, getCurrentHistoryState());
if (newState != null) {
setCurrentHistoryState(newState);
}
@@ -342,7 +343,7 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
@Override
public void setCurrentHistoryState(@NotNull CalculatorHistoryState editorHistoryState) {
synchronized (AndroidCalculatorHistoryImpl.instance) {
synchronized (CalculatorHistory.instance) {
Log.d(this.getClass().getName(), "Saved history found: " + editorHistoryState);
editorHistoryState.setValuesFromHistory(new TextViewEditorAdapter(this.editor), this.display);
@@ -362,13 +363,13 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
@Override
@NotNull
public CalculatorHistoryState getCurrentHistoryState() {
synchronized (AndroidCalculatorHistoryImpl.instance) {
synchronized (CalculatorHistory.instance) {
return CalculatorHistoryState.newInstance(new TextViewEditorAdapter(this.editor), this.display);
}
}
@NotNull
public AndroidCalculatorDisplayView getDisplay() {
public CalculatorDisplay getDisplay() {
return display;
}
@@ -383,15 +384,13 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
@Override
public void onClick(View v) {
if (v instanceof CalculatorDisplayView) {
final CalculatorDisplay cd = CalculatorLocatorImpl.getInstance().getCalculatorDisplay();
if (v instanceof CalculatorDisplay) {
final CalculatorDisplay cd = (CalculatorDisplay) v;
final CalculatorDisplayViewState displayViewState = cd.getViewState();
if (displayViewState.isValid()) {
final List<AndroidCalculatorDisplayView.MenuItem> filteredMenuItems = new ArrayList<AndroidCalculatorDisplayView.MenuItem>(AndroidCalculatorDisplayView.MenuItem.values().length);
for (AndroidCalculatorDisplayView.MenuItem menuItem : AndroidCalculatorDisplayView.MenuItem.values()) {
if (menuItem.isItemVisible(displayViewState)) {
if (cd.isValid()) {
final List<CalculatorDisplay.MenuItem> filteredMenuItems = new ArrayList<CalculatorDisplay.MenuItem>(CalculatorDisplay.MenuItem.values().length);
for (CalculatorDisplay.MenuItem menuItem : CalculatorDisplay.MenuItem.values()) {
if (menuItem.isItemVisible(cd)) {
filteredMenuItems.add(menuItem);
}
}
@@ -401,7 +400,7 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
}
} else {
final String errorMessage = displayViewState.getErrorMessage();
final String errorMessage = cd.getErrorMessage();
if (errorMessage != null) {
showEvaluationError(activity, errorMessage);
}

View File

@@ -0,0 +1,40 @@
/*
* 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.Generic;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.history.Editor;
import org.solovyev.android.calculator.jscl.JsclOperation;
/**
* User: serso
* Date: 12/17/11
* Time: 9:45 PM
*/
public interface ICalculatorDisplay extends Editor{
boolean isValid();
void setValid(boolean valid);
@Nullable
String getErrorMessage();
void setErrorMessage(@Nullable String errorMessage);
void setJsclOperation(@NotNull JsclOperation jsclOperation);
@NotNull
JsclOperation getJsclOperation();
void setGenericResult(@Nullable Generic genericResult);
@Nullable
Generic getGenericResult();
}

View File

@@ -157,7 +157,7 @@ public abstract class AbstractHistoryActivity extends ListActivity {
boolean result = false;
try {
historyState.setSaved(true);
if ( CollectionsUtils.contains(historyState, AndroidCalculatorHistoryImpl.instance.getSavedHistory(), new Equalizer<CalculatorHistoryState>() {
if ( CollectionsUtils.contains(historyState, CalculatorHistory.instance.getSavedHistory(), new Equalizer<CalculatorHistoryState>() {
@Override
public boolean equals(@Nullable CalculatorHistoryState first, @Nullable CalculatorHistoryState second) {
return first != null && second != null &&

View File

@@ -0,0 +1,81 @@
/*
* 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.history;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.Transient;
import java.util.Date;
/**
* User: serso
* Date: 10/15/11
* Time: 1:45 PM
*/
public class AbstractHistoryState implements Cloneable{
@Element
private long time = new Date().getTime();
@Element(required = false)
@Nullable
private String comment;
@Transient
private boolean saved;
@Transient
private int id = 0;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
@Nullable
public String getComment() {
return comment;
}
public void setComment(@Nullable String comment) {
this.comment = comment;
}
public boolean isSaved() {
return saved;
}
public void setSaved(boolean saved) {
this.saved = saved;
}
@Override
protected AbstractHistoryState clone() {
AbstractHistoryState clone;
try {
clone = (AbstractHistoryState)super.clone();
} catch (CloneNotSupportedException e) {
throw new UnsupportedOperationException(e);
}
return clone;
}
}

View File

@@ -1,18 +0,0 @@
package org.solovyev.android.calculator.history;
import android.content.Context;
import android.content.SharedPreferences;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: Solovyev_S
* Date: 20.09.12
* Time: 16:07
*/
public interface AndroidCalculatorHistory extends CalculatorHistory {
void load(@Nullable Context context, @Nullable SharedPreferences preferences);
void save(@NotNull Context context);
}

View File

@@ -1,145 +0,0 @@
/*
* 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.history;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.R;
import org.solovyev.common.history.HistoryAction;
import java.util.List;
/**
* User: serso
* Date: 10/9/11
* Time: 6:35 PM
*/
public enum AndroidCalculatorHistoryImpl implements AndroidCalculatorHistory {
instance;
@NotNull
private final CalculatorHistoryImpl calculatorHistory = new CalculatorHistoryImpl();
@Override
public void load(@Nullable Context context, @Nullable SharedPreferences preferences) {
if (context != null && preferences != null) {
final String value = preferences.getString(context.getString(R.string.p_calc_history), null);
calculatorHistory.fromXml(value);
}
}
@Override
public void save(@NotNull Context context) {
final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
final SharedPreferences.Editor editor = settings.edit();
editor.putString(context.getString(R.string.p_calc_history), calculatorHistory.toXml());
editor.commit();
}
public void clearSavedHistory(@NotNull Context context) {
calculatorHistory.clearSavedHistory();
save(context);
}
public void removeSavedHistory(@NotNull CalculatorHistoryState historyState, @NotNull Context context) {
historyState.setSaved(false);
calculatorHistory.removeSavedHistory(historyState);
save(context);
}
@Override
public boolean isEmpty() {
return calculatorHistory.isEmpty();
}
@Override
public CalculatorHistoryState getLastHistoryState() {
return calculatorHistory.getLastHistoryState();
}
@Override
public boolean isUndoAvailable() {
return calculatorHistory.isUndoAvailable();
}
@Override
public CalculatorHistoryState undo(@Nullable CalculatorHistoryState currentState) {
return calculatorHistory.undo(currentState);
}
@Override
public boolean isRedoAvailable() {
return calculatorHistory.isRedoAvailable();
}
@Override
public CalculatorHistoryState redo(@Nullable CalculatorHistoryState currentState) {
return calculatorHistory.redo(currentState);
}
@Override
public boolean isActionAvailable(@NotNull HistoryAction historyAction) {
return calculatorHistory.isActionAvailable(historyAction);
}
@Override
public CalculatorHistoryState doAction(@NotNull HistoryAction historyAction, @Nullable CalculatorHistoryState currentState) {
return calculatorHistory.doAction(historyAction, currentState);
}
@Override
public void addState(@Nullable CalculatorHistoryState currentState) {
calculatorHistory.addState(currentState);
}
@NotNull
@Override
public List<CalculatorHistoryState> getStates() {
return calculatorHistory.getStates();
}
@Override
public void clear() {
calculatorHistory.clear();
}
@NotNull
public List<CalculatorHistoryState> getSavedHistory() {
return calculatorHistory.getSavedHistory();
}
@NotNull
public CalculatorHistoryState addSavedState(@NotNull CalculatorHistoryState historyState) {
return calculatorHistory.addSavedState(historyState);
}
@Override
public void fromXml(@NotNull String xml) {
calculatorHistory.fromXml(xml);
}
@Override
public String toXml() {
return calculatorHistory.toXml();
}
@Override
public void clearSavedHistory() {
calculatorHistory.clearSavedHistory();
}
@Override
public void removeSavedHistory(@NotNull CalculatorHistoryState historyState) {
calculatorHistory.removeSavedHistory(historyState);
}
}

View File

@@ -0,0 +1,138 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.calculator.history;
import jscl.math.Generic;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root;
import org.simpleframework.xml.Transient;
import org.solovyev.android.calculator.ICalculatorDisplay;
import org.solovyev.android.calculator.jscl.JsclOperation;
/**
* User: serso
* Date: 9/17/11
* Time: 11:05 PM
*/
@Root
public class CalculatorDisplayHistoryState implements Cloneable {
@Transient
private boolean valid = true;
@Transient
@Nullable
private String errorMessage = null;
@Element
@NotNull
private EditorHistoryState editorState;
@Element
@NotNull
private JsclOperation jsclOperation;
@Transient
@Nullable
private Generic genericResult;
private CalculatorDisplayHistoryState() {
// for xml
}
@NotNull
public static CalculatorDisplayHistoryState newInstance(@NotNull ICalculatorDisplay display) {
final CalculatorDisplayHistoryState result = new CalculatorDisplayHistoryState();
result.editorState = EditorHistoryState.newInstance(display);
result.valid = display.isValid();
result.jsclOperation = display.getJsclOperation();
result.genericResult = display.getGenericResult();
result.errorMessage = display.getErrorMessage();
return result;
}
public void setValuesFromHistory(@NotNull ICalculatorDisplay display) {
this.getEditorState().setValuesFromHistory(display);
display.setValid(this.isValid());
display.setErrorMessage(this.getErrorMessage());
display.setJsclOperation(this.getJsclOperation());
display.setGenericResult(this.getGenericResult());
}
public boolean isValid() {
return valid;
}
@NotNull
public EditorHistoryState getEditorState() {
return editorState;
}
@NotNull
public JsclOperation getJsclOperation() {
return jsclOperation;
}
@Nullable
public String getErrorMessage() {
return errorMessage;
}
@Nullable
public Generic getGenericResult() {
return genericResult;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CalculatorDisplayHistoryState that = (CalculatorDisplayHistoryState) o;
if (!editorState.equals(that.editorState)) return false;
if (jsclOperation != that.jsclOperation) return false;
return true;
}
@Override
public int hashCode() {
int result = editorState.hashCode();
result = 31 * result + jsclOperation.hashCode();
return result;
}
@Override
public String toString() {
return "CalculatorDisplayHistoryState{" +
"valid=" + valid +
", errorMessage='" + errorMessage + '\'' +
", editorHistoryState=" + editorState +
", jsclOperation=" + jsclOperation +
'}';
}
@Override
protected CalculatorDisplayHistoryState clone() {
try {
final CalculatorDisplayHistoryState clone = (CalculatorDisplayHistoryState) super.clone();
clone.editorState = this.editorState.clone();
return clone;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,149 @@
/*
* 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.history;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.R;
import org.solovyev.common.history.HistoryAction;
import org.solovyev.common.history.HistoryHelper;
import org.solovyev.common.history.SimpleHistoryHelper;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* User: serso
* Date: 10/9/11
* Time: 6:35 PM
*/
public enum CalculatorHistory implements HistoryHelper<CalculatorHistoryState> {
instance;
// todo serso: not synchronized
private int counter = 0;
@NotNull
private final HistoryHelper<CalculatorHistoryState> history = new SimpleHistoryHelper<CalculatorHistoryState>();
@NotNull
private final List<CalculatorHistoryState> savedHistory = new ArrayList<CalculatorHistoryState> ();
@Override
public boolean isEmpty() {
return this.history.isEmpty();
}
@Override
public CalculatorHistoryState getLastHistoryState() {
return this.history.getLastHistoryState();
}
@Override
public boolean isUndoAvailable() {
return history.isUndoAvailable();
}
@Override
public CalculatorHistoryState undo(@Nullable CalculatorHistoryState currentState) {
return history.undo(currentState);
}
@Override
public boolean isRedoAvailable() {
return history.isRedoAvailable();
}
@Override
public CalculatorHistoryState redo(@Nullable CalculatorHistoryState currentState) {
return history.redo(currentState);
}
@Override
public boolean isActionAvailable(@NotNull HistoryAction historyAction) {
return history.isActionAvailable(historyAction);
}
@Override
public CalculatorHistoryState doAction(@NotNull HistoryAction historyAction, @Nullable CalculatorHistoryState currentState) {
return history.doAction(historyAction, currentState);
}
@Override
public void addState(@Nullable CalculatorHistoryState currentState) {
history.addState(currentState);
}
@NotNull
@Override
public List<CalculatorHistoryState> getStates() {
return history.getStates();
}
@Override
public void clear() {
this.history.clear();
}
public void load(@Nullable Context context, @Nullable SharedPreferences preferences) {
if (context != null && preferences != null) {
final String value = preferences.getString(context.getString(R.string.p_calc_history), null);
this.savedHistory.clear();
HistoryUtils.fromXml(value, this.savedHistory);
for (CalculatorHistoryState historyState : savedHistory) {
historyState.setSaved(true);
historyState.setId(counter++);
}
}
}
public void save(@NotNull Context context) {
final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
final SharedPreferences.Editor editor = settings.edit();
editor.putString(context.getString(R.string.p_calc_history), HistoryUtils.toXml(this.savedHistory));
editor.commit();
}
@NotNull
public List<CalculatorHistoryState> getSavedHistory() {
return Collections.unmodifiableList(savedHistory);
}
@NotNull
public CalculatorHistoryState addSavedState(@NotNull CalculatorHistoryState historyState) {
if (historyState.isSaved()) {
return historyState;
} else {
final CalculatorHistoryState savedState = historyState.clone();
savedState.setId(counter++);
savedState.setSaved(true);
savedHistory.add(savedState);
return savedState;
}
}
public void clearSavedHistory(@NotNull Context context) {
this.savedHistory.clear();
save(context);
}
public void removeSavedHistory(@NotNull CalculatorHistoryState historyState, @NotNull Context context) {
historyState.setSaved(false);
this.savedHistory.remove(historyState);
save(context);
}
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.calculator.history;
import org.jetbrains.annotations.NotNull;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root;
import org.solovyev.android.calculator.ICalculatorDisplay;
/**
* User: serso
* Date: 9/11/11
* Time: 12:16 AM
*/
@Root
public class CalculatorHistoryState extends AbstractHistoryState {
@Element
@NotNull
private EditorHistoryState editorState;
@Element
@NotNull
private CalculatorDisplayHistoryState displayState;
private CalculatorHistoryState() {
// for xml
}
private CalculatorHistoryState(@NotNull EditorHistoryState editorState,
@NotNull CalculatorDisplayHistoryState displayState) {
this.editorState = editorState;
this.displayState = displayState;
}
public static CalculatorHistoryState newInstance(@NotNull Editor editor, @NotNull ICalculatorDisplay display) {
final EditorHistoryState editorHistoryState = EditorHistoryState.newInstance(editor);
final CalculatorDisplayHistoryState displayHistoryState = CalculatorDisplayHistoryState.newInstance(display);
return new CalculatorHistoryState(editorHistoryState, displayHistoryState);
}
@NotNull
public EditorHistoryState getEditorState() {
return editorState;
}
public void setEditorState(@NotNull EditorHistoryState editorState) {
this.editorState = editorState;
}
@NotNull
public CalculatorDisplayHistoryState getDisplayState() {
return displayState;
}
public void setDisplayState(@NotNull CalculatorDisplayHistoryState displayState) {
this.displayState = displayState;
}
@Override
public String toString() {
return "CalculatorHistoryState{" +
"editorState=" + editorState +
", displayState=" + displayState +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CalculatorHistoryState that = (CalculatorHistoryState) o;
if (this.isSaved() != that.isSaved()) return false;
if (this.getId() != that.getId()) return false;
if (!displayState.equals(that.displayState)) return false;
if (!editorState.equals(that.editorState)) return false;
return true;
}
@Override
public int hashCode() {
int result = Boolean.valueOf(isSaved()).hashCode();
result = 31 * result + getId();
result = 31 * result + editorState.hashCode();
result = 31 * result + displayState.hashCode();
return result;
}
public void setValuesFromHistory(@NotNull Editor editor, @NotNull ICalculatorDisplay display) {
this.getEditorState().setValuesFromHistory(editor);
this.getDisplayState().setValuesFromHistory(display);
}
@Override
protected CalculatorHistoryState clone() {
final CalculatorHistoryState clone = (CalculatorHistoryState)super.clone();
clone.editorState = this.editorState.clone();
clone.displayState = this.displayState.clone();
return clone;
}
}

View File

@@ -0,0 +1,27 @@
/*
* 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.history;
import org.jetbrains.annotations.Nullable;
/**
* User: serso
* Date: 12/17/11
* Time: 9:37 PM
*/
public interface Editor {
@Nullable
CharSequence getText();
void setText(@Nullable CharSequence text);
int getSelection();
void setSelection(int selection);
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.calculator.history;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root;
@Root
public class EditorHistoryState implements Cloneable{
@Element
private int cursorPosition;
@Element(required = false)
@Nullable
private String text;
private EditorHistoryState() {
// for xml
}
@NotNull
public static EditorHistoryState newInstance(@NotNull Editor editor) {
final EditorHistoryState result = new EditorHistoryState();
result.text = String.valueOf(editor.getText());
result.cursorPosition = editor.getSelection();
return result;
}
public void setValuesFromHistory(@NotNull Editor editor) {
editor.setText(this.getText());
editor.setSelection(this.getCursorPosition());
}
@Nullable
public String getText() {
return text;
}
public int getCursorPosition() {
return cursorPosition;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof EditorHistoryState)) return false;
EditorHistoryState that = (EditorHistoryState) o;
if (cursorPosition != that.cursorPosition) return false;
if (text != null ? !text.equals(that.text) : that.text != null) return false;
return true;
}
@Override
public int hashCode() {
int result = cursorPosition;
result = 31 * result + (text != null ? text.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "EditorHistoryState{" +
"cursorPosition=" + cursorPosition +
", text='" + text + '\'' +
'}';
}
@Override
protected EditorHistoryState clone() {
try {
return (EditorHistoryState)super.clone();
} catch (CloneNotSupportedException e) {
throw new UnsupportedOperationException(e);
}
}
}

View File

@@ -0,0 +1,33 @@
/*
* 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.history;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.Root;
import java.util.ArrayList;
import java.util.List;
/**
* User: serso
* Date: 12/17/11
* Time: 9:30 PM
*/
@Root
public class History {
@ElementList
private List<CalculatorHistoryState> historyItems = new ArrayList<CalculatorHistoryState>();
public History() {
}
public List<CalculatorHistoryState> getHistoryItems() {
return historyItems;
}
}

View File

@@ -26,12 +26,12 @@ public class HistoryActivityTab extends AbstractHistoryActivity {
@NotNull
@Override
protected List<CalculatorHistoryState> getHistoryItems() {
return new ArrayList<CalculatorHistoryState>(AndroidCalculatorHistoryImpl.instance.getStates());
return new ArrayList<CalculatorHistoryState>(CalculatorHistory.instance.getStates());
}
@Override
protected void clearHistory() {
AndroidCalculatorHistoryImpl.instance.clear();
CalculatorHistory.instance.clear();
getAdapter().clear();
}
}

View File

@@ -96,7 +96,7 @@ public enum HistoryItemMenuItem implements LabeledMenuItem<HistoryItemMenuData>
final CalculatorHistoryState historyState = data.getHistoryState();
if (historyState.isSaved()) {
data.getAdapter().remove(historyState);
AndroidCalculatorHistoryImpl.instance.removeSavedHistory(historyState, context);
CalculatorHistory.instance.removeSavedHistory(historyState, context);
Toast.makeText(context, context.getText(R.string.c_history_was_removed), Toast.LENGTH_LONG).show();
data.getAdapter().notifyDataSetChanged();
}
@@ -122,14 +122,14 @@ public enum HistoryItemMenuItem implements LabeledMenuItem<HistoryItemMenuData>
@Override
public void onClick(DialogInterface dialog, int which) {
if (save) {
final CalculatorHistoryState savedHistoryItem = AndroidCalculatorHistoryImpl.instance.addSavedState(historyState);
final CalculatorHistoryState savedHistoryItem = CalculatorHistory.instance.addSavedState(historyState);
savedHistoryItem.setComment(comment.getText().toString());
AndroidCalculatorHistoryImpl.instance.save(context);
CalculatorHistory.instance.save(context);
// we don't need to add element to the adapter as adapter of another activity must be updated and not this
//data.getAdapter().add(savedHistoryItem);
} else {
historyState.setComment(comment.getText().toString());
AndroidCalculatorHistoryImpl.instance.save(context);
CalculatorHistory.instance.save(context);
}
data.getAdapter().notifyDataSetChanged();
Toast.makeText(context, context.getText(R.string.c_history_saved), Toast.LENGTH_LONG).show();

View File

@@ -0,0 +1,61 @@
/*
* 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.history;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;
import java.io.StringWriter;
import java.util.List;
/**
* User: serso
* Date: 12/17/11
* Time: 9:59 PM
*/
class HistoryUtils {
// not intended for instantiation
private HistoryUtils() {
throw new AssertionError();
}
public static void fromXml(@Nullable String xml, @NotNull List<CalculatorHistoryState> historyItems) {
if (xml != null) {
final Serializer serializer = new Persister();
try {
final History history = serializer.read(History.class, xml);
for (CalculatorHistoryState historyItem : history.getHistoryItems()) {
historyItems.add(historyItem);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
@NotNull
public static String toXml(@NotNull List<CalculatorHistoryState> historyItems) {
final History history = new History();
for (CalculatorHistoryState historyState : historyItems) {
if (historyState.isSaved()) {
history.getHistoryItems().add(historyState);
}
}
final StringWriter xml = new StringWriter();
final Serializer serializer = new Persister();
try {
serializer.write(history, xml);
} catch (Exception e) {
throw new RuntimeException(e);
}
return xml.toString();
}
}

View File

@@ -26,12 +26,12 @@ public class SavedHistoryActivityTab extends AbstractHistoryActivity {
@NotNull
@Override
protected List<CalculatorHistoryState> getHistoryItems() {
return new ArrayList<CalculatorHistoryState>(AndroidCalculatorHistoryImpl.instance.getSavedHistory());
return new ArrayList<CalculatorHistoryState>(CalculatorHistory.instance.getSavedHistory());
}
@Override
protected void clearHistory() {
AndroidCalculatorHistoryImpl.instance.clearSavedHistory(this);
CalculatorHistory.instance.clearSavedHistory(this);
getAdapter().clear();
}
}

View File

@@ -10,7 +10,6 @@ import android.widget.EditText;
import android.widget.TextView;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.Editor;
/**
* User: serso

View File

@@ -0,0 +1,28 @@
/*
* 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.jscl;
import jscl.math.Generic;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.model.CalculatorParseException;
import org.solovyev.android.calculator.model.TextProcessor;
/**
* User: serso
* Date: 10/6/11
* Time: 9:48 PM
*/
class FromJsclNumericTextProcessor implements TextProcessor<String, Generic> {
public static final FromJsclNumericTextProcessor instance = new FromJsclNumericTextProcessor();
@NotNull
@Override
public String process(@NotNull Generic numeric) throws CalculatorParseException {
return numeric.toString().replace("*", "");
}
}

View File

@@ -0,0 +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.model.CalculatorEngine;
import org.solovyev.android.calculator.model.DummyTextProcessor;
import org.solovyev.android.calculator.model.FromJsclSimplifyTextProcessor;
import org.solovyev.android.calculator.model.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 CalculatorEngine.instance.getEngine().simplify(expression);
case elementary:
return CalculatorEngine.instance.getEngine().elementary(expression);
case numeric:
return CalculatorEngine.instance.getEngine().evaluate(expression);
default:
throw new UnsupportedOperationException();
}
}
@NotNull
public final Generic evaluateGeneric(@NotNull String expression) throws ParseException {
switch (this) {
case simplify:
return CalculatorEngine.instance.getEngine().simplifyGeneric(expression);
case elementary:
return CalculatorEngine.instance.getEngine().elementaryGeneric(expression);
case numeric:
return CalculatorEngine.instance.getEngine().evaluateGeneric(expression);
default:
throw new UnsupportedOperationException();
}
}
}

View File

@@ -0,0 +1,448 @@
/*
* 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.common.JPredicate;
import org.solovyev.common.StartsWithFinder;
import org.solovyev.android.calculator.model.CalculatorEngine;
import org.solovyev.android.calculator.model.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()) {
final String jsclPrefix = numeralBase.getJsclPrefix();
if (jsclPrefix != null) {
tokens.add(jsclPrefix);
}
}
}
@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 CalculatorEngine.instance.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 CalculatorEngine.instance.getFunctionsRegistry().getNames();
}
},
operator(1050, true, true, MathGroupType.function) {
@NotNull
@Override
public List<String> getTokens() {
return CalculatorEngine.instance.getOperatorsRegistry().getNames();
}
},
constant(1100, true, true, MathGroupType.other) {
@NotNull
@Override
public List<String> getTokens() {
return CalculatorEngine.instance.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

@@ -0,0 +1,85 @@
/*
* 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.model;
import jscl.MathEngine;
import jscl.NumeralBase;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.math.MathType;
import org.solovyev.common.text.StringUtils;
/**
* User: serso
* Date: 12/15/11
* Time: 9:01 PM
*/
public abstract class AbstractNumberBuilder {
@NotNull
protected final MathEngine engine;
@Nullable
protected StringBuilder numberBuilder = null;
@Nullable
protected NumeralBase nb;
protected AbstractNumberBuilder(@NotNull MathEngine engine) {
this.engine = engine;
this.nb = engine.getNumeralBase();
}
/**
* Method determines if we can continue to process current number
*
* @param mathTypeResult current math type result
* @return true if we can continue of processing of current number, if false - new number should be constructed
*/
protected boolean canContinue(@NotNull MathType.Result mathTypeResult) {
boolean result = mathTypeResult.getMathType().getGroupType() == MathType.MathGroupType.number &&
!spaceBefore(mathTypeResult) &&
numeralBaseCheck(mathTypeResult) &&
numeralBaseInTheStart(mathTypeResult.getMathType()) || isSignAfterE(mathTypeResult);
return result;
}
private boolean spaceBefore(@NotNull MathType.Result mathTypeResult) {
return numberBuilder == null && StringUtils.isEmpty(mathTypeResult.getMatch().trim());
}
private boolean numeralBaseInTheStart(@NotNull MathType mathType) {
return mathType != MathType.numeral_base || numberBuilder == null;
}
private boolean numeralBaseCheck(@NotNull MathType.Result mathType) {
return mathType.getMathType() != MathType.digit || getNumeralBase().getAcceptableCharacters().contains(mathType.getMatch().charAt(0));
}
private boolean isSignAfterE(@NotNull MathType.Result mathTypeResult) {
if (!isHexMode()) {
if ("-".equals(mathTypeResult.getMatch()) || "+".equals(mathTypeResult.getMatch())) {
final StringBuilder localNb = numberBuilder;
if (localNb != null && localNb.length() > 0) {
if (localNb.charAt(localNb.length() - 1) == MathType.POWER_10) {
return true;
}
}
}
}
return false;
}
public boolean isHexMode() {
return nb == NumeralBase.hex || (nb == null && engine.getNumeralBase() == NumeralBase.hex);
}
@NotNull
protected NumeralBase getNumeralBase() {
return nb == null ? engine.getNumeralBase() : nb;
}
}

View File

@@ -1,404 +1,425 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.calculator.model;
import android.content.Context;
import android.content.SharedPreferences;
import jscl.*;
import jscl.math.Generic;
import jscl.math.function.Function;
import jscl.math.function.IConstant;
import jscl.math.operator.Operator;
import jscl.text.ParseInterruptedException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.*;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.calculator.text.TextProcessor;
import org.solovyev.android.msg.AndroidMessage;
import org.solovyev.android.prefs.BooleanPreference;
import org.solovyev.android.prefs.Preference;
import org.solovyev.android.prefs.StringPreference;
import org.solovyev.common.MutableObject;
import org.solovyev.common.msg.MessageRegistry;
import org.solovyev.common.msg.MessageType;
import org.solovyev.common.text.EnumMapper;
import org.solovyev.common.text.NumberMapper;
import org.solovyev.common.text.StringUtils;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* User: serso
* Date: 9/12/11
* Time: 11:38 PM
*/
public enum CalculatorEngine implements JCalculatorEngine {
instance;
private static final String GROUPING_SEPARATOR_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_calc_grouping_separator";
private static final String MULTIPLICATION_SIGN_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_calc_multiplication_sign";
private static final String MULTIPLICATION_SIGN_DEFAULT = "×";
private static final String MAX_CALCULATION_TIME_P_KEY = "calculation.max_calculation_time";
private static final String MAX_CALCULATION_TIME_DEFAULT = "5";
private static final String SCIENCE_NOTATION_P_KEY = "calculation.output.science_notation";
private static final boolean SCIENCE_NOTATION_DEFAULT = false;
private static final String ROUND_RESULT_P_KEY = "org.solovyev.android.calculator.CalculatorModel_round_result";
private static final boolean ROUND_RESULT_DEFAULT = true;
private static final String RESULT_PRECISION_P_KEY = "org.solovyev.android.calculator.CalculatorModel_result_precision";
private static final String RESULT_PRECISION_DEFAULT = "5";
private static final String NUMERAL_BASES_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_numeral_bases";
private static final String NUMERAL_BASES_DEFAULT = "dec";
private static final String ANGLE_UNITS_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_angle_units";
private static final String ANGLE_UNITS_DEFAULT = "deg";
public static class Preferences {
public static final Preference<String> groupingSeparator = StringPreference.newInstance(GROUPING_SEPARATOR_P_KEY, JsclMathEngine.GROUPING_SEPARATOR_DEFAULT);
public static final Preference<String> multiplicationSign = StringPreference.newInstance(MULTIPLICATION_SIGN_P_KEY, MULTIPLICATION_SIGN_DEFAULT);
public static final Preference<Integer> precision = StringPreference.newInstance(RESULT_PRECISION_P_KEY, RESULT_PRECISION_DEFAULT, new NumberMapper<Integer>(Integer.class));
public static final Preference<Boolean> roundResult = new BooleanPreference(ROUND_RESULT_P_KEY, ROUND_RESULT_DEFAULT);
public static final Preference<NumeralBase> numeralBase = StringPreference.newInstance(NUMERAL_BASES_P_KEY, NUMERAL_BASES_DEFAULT, EnumMapper.newInstance(NumeralBase.class));
public static final Preference<AngleUnit> angleUnit = StringPreference.newInstance(ANGLE_UNITS_P_KEY, ANGLE_UNITS_DEFAULT, EnumMapper.newInstance(AngleUnit.class));
public static final Preference<Boolean> scienceNotation = new BooleanPreference(SCIENCE_NOTATION_P_KEY, SCIENCE_NOTATION_DEFAULT);
public static final Preference<Integer> maxCalculationTime = StringPreference.newInstance(MAX_CALCULATION_TIME_P_KEY, MAX_CALCULATION_TIME_DEFAULT, new NumberMapper<Integer>(Integer.class));
private static final List<String> preferenceKeys = new ArrayList<String>();
static {
preferenceKeys.add(groupingSeparator.getKey());
preferenceKeys.add(multiplicationSign.getKey());
preferenceKeys.add(precision.getKey());
preferenceKeys.add(roundResult.getKey());
preferenceKeys.add(numeralBase.getKey());
preferenceKeys.add(angleUnit.getKey());
preferenceKeys.add(scienceNotation.getKey());
preferenceKeys.add(maxCalculationTime.getKey());
}
@NotNull
public static List<String> getPreferenceKeys() {
return Collections.unmodifiableList(preferenceKeys);
}
}
@NotNull
private final Object lock = new Object();
@NotNull
private MathEngine engine = JsclMathEngine.instance;
@NotNull
public final TextProcessor<PreparedExpression, String> preprocessor = ToJsclTextProcessor.getInstance();
@NotNull
private final AndroidMathRegistry<IConstant> varsRegistry = new AndroidVarsRegistryImpl(engine.getConstantsRegistry());
@NotNull
private final AndroidMathRegistry<jscl.math.function.Function> functionsRegistry = new AndroidFunctionsMathRegistry(engine.getFunctionsRegistry());
@NotNull
private final AndroidMathRegistry<Operator> operatorsRegistry = new AndroidOperatorsMathRegistry(engine.getOperatorsRegistry());
private final AndroidMathRegistry<Operator> postfixFunctionsRegistry = new AndroidPostfixFunctionsRegistry(engine.getPostfixFunctionsRegistry());
@Nullable
private ThreadKiller threadKiller = new AndroidThreadKiller();
// calculation thread timeout in seconds, after timeout thread would be interrupted
private int timeout = Integer.valueOf(MAX_CALCULATION_TIME_DEFAULT);
@NotNull
private String multiplicationSign = MULTIPLICATION_SIGN_DEFAULT;
CalculatorEngine() {
this.engine.setRoundResult(true);
this.engine.setUseGroupingSeparator(true);
}
@Override
@NotNull
public String getMultiplicationSign() {
return multiplicationSign;
}
public void setMultiplicationSign(@NotNull String multiplicationSign) {
this.multiplicationSign = multiplicationSign;
}
public CalculatorOutput evaluate(@NotNull JsclOperation operation,
@NotNull String expression) throws CalculatorParseException, CalculatorEvalException {
return evaluate(operation, expression, null);
}
public CalculatorOutput evaluate(@NotNull final JsclOperation operation,
@NotNull String expression,
@Nullable MessageRegistry mr) throws CalculatorParseException, CalculatorEvalException {
synchronized (lock) {
final StringBuilder sb = new StringBuilder();
final PreparedExpression preparedExpression = preprocessor.process(expression);
sb.append(preparedExpression);
//Log.d(CalculatorEngine.class.getName(), "Preprocessed expression: " + preparedExpression);
/*if (operation == JsclOperation.numeric && preparedExpression.isExistsUndefinedVar()) {
operation = JsclOperation.simplify;
if (mr != null) {
final String undefinedVars = CollectionsUtils.formatValue(preparedExpression.getUndefinedVars(), ", ", new Formatter<Var>() {
@Override
public String formatValue(@Nullable Var var) throws IllegalArgumentException {
return var != null ? var.getName() : "";
}
});
mr.addMessage(new AndroidMessage(R.string.c_simplify_instead_of_numeric, MessageType.info, undefinedVars));
}
}*/
final String jsclExpression = sb.toString();
final MutableObject<Generic> calculationResult = new MutableObject<Generic>(null);
final MutableObject<CalculatorParseException> parseException = new MutableObject<CalculatorParseException>(null);
final MutableObject<CalculatorEvalException> evalException = new MutableObject<CalculatorEvalException>(null);
final MutableObject<Thread> calculationThread = new MutableObject<Thread>(null);
final CountDownLatch latch = new CountDownLatch(1);
new Thread(new Runnable() {
@Override
public void run() {
final Thread thread = Thread.currentThread();
try {
//Log.d(CalculatorEngine.class.getName(), "Calculation thread started work: " + thread.getName());
//System.out.println(jsclExpression);
calculationThread.setObject(thread);
final Generic genericResult = operation.evaluateGeneric(jsclExpression);
// NOTE: toString() method must be called here as ArithmeticOperationException may occur in it (just to avoid later check!)
genericResult.toString();
calculationResult.setObject(genericResult);
} catch (AbstractJsclArithmeticException e) {
evalException.setObject(new CalculatorEvalException(e, e, jsclExpression));
} catch (ArithmeticException e) {
//System.out.println(e.getMessage());
final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_1, MessageType.error, CalculatorApplication.getInstance(), e.getMessage());
parseException.setObject(new CalculatorParseException(jsclExpression, androidMessage));
} catch (StackOverflowError e) {
//System.out.println(StringUtils.fromStackTrace(e.getStackTrace()));
final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_2, MessageType.error, CalculatorApplication.getInstance());
parseException.setObject(new CalculatorParseException(jsclExpression, androidMessage));
} catch (jscl.text.ParseException e) {
//System.out.println(e.getMessage());
parseException.setObject(new CalculatorParseException(e));
} catch (ParseInterruptedException e) {
//System.out.println(e.getMessage());
// do nothing - we ourselves interrupt the calculations
} finally {
//Log.d(CalculatorEngine.class.getName(), "Calculation thread ended work: " + thread.getName());
calculationThread.setObject(null);
latch.countDown();
}
}
}).start();
try {
//Log.d(CalculatorEngine.class.getName(), "Main thread is waiting: " + Thread.currentThread().getName());
latch.await(timeout, TimeUnit.SECONDS);
//Log.d(CalculatorEngine.class.getName(), "Main thread got up: " + Thread.currentThread().getName());
final CalculatorParseException parseExceptionObject = parseException.getObject();
final CalculatorEvalException evalExceptionObject = evalException.getObject();
final Object calculationResultLocal = calculationResult.getObject();
final Thread calculationThreadLocal = calculationThread.getObject();
if (calculationThreadLocal != null) {
if (threadKiller != null) {
threadKiller.killThread(calculationThreadLocal);
}
//calculationThreadLocal.stop();
}
if (parseExceptionObject != null || evalExceptionObject != null) {
if (operation == JsclOperation.numeric &&
(preparedExpression.isExistsUndefinedVar() || (evalExceptionObject != null && evalExceptionObject.getCause() instanceof NumeralBaseException))) {
return evaluate(JsclOperation.simplify, expression, mr);
}
if (parseExceptionObject != null) {
throw parseExceptionObject;
} else {
throw evalExceptionObject;
}
}
if (calculationResultLocal == null) {
final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_3, MessageType.error, CalculatorApplication.getInstance());
throw new CalculatorParseException(jsclExpression, androidMessage);
}
} catch (InterruptedException e) {
final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_4, MessageType.error, CalculatorApplication.getInstance());
throw new CalculatorParseException(jsclExpression, androidMessage);
}
final Generic genericResult = calculationResult.getObject();
return new CalculatorOutputImpl(operation.getFromProcessor().process(genericResult), operation, genericResult);
}
}
public void setPrecision(int precision) {
this.getEngine().setPrecision(precision);
}
public void setRoundResult(boolean roundResult) {
this.getEngine().setRoundResult(roundResult);
}
public void init(@Nullable Context context, @Nullable SharedPreferences preferences) {
synchronized (lock) {
reset(context, preferences);
}
}
public void reset(@Nullable Context context, @Nullable SharedPreferences preferences) {
synchronized (lock) {
softReset(context, preferences);
varsRegistry.load(context, preferences);
functionsRegistry.load(context, preferences);
operatorsRegistry.load(context, preferences);
postfixFunctionsRegistry.load(context, preferences);
}
}
public void softReset(@Nullable Context context, @Nullable SharedPreferences preferences) {
synchronized (lock) {
if (preferences != null) {
this.setPrecision(Preferences.precision.getPreference(preferences));
this.setRoundResult(Preferences.roundResult.getPreference(preferences));
this.setAngleUnits(getAngleUnitsFromPrefs(preferences));
this.setNumeralBase(getNumeralBaseFromPrefs(preferences));
this.setMultiplicationSign(Preferences.multiplicationSign.getPreference(preferences));
this.setScienceNotation(Preferences.scienceNotation.getPreference(preferences));
this.setTimeout(Preferences.maxCalculationTime.getPreference(preferences));
final String groupingSeparator = Preferences.groupingSeparator.getPreference(preferences);
if (StringUtils.isEmpty(groupingSeparator)) {
this.getEngine().setUseGroupingSeparator(false);
} else {
this.getEngine().setUseGroupingSeparator(true);
this.getEngine().setGroupingSeparator(groupingSeparator.charAt(0));
}
}
}
}
@NotNull
public NumeralBase getNumeralBaseFromPrefs(@NotNull SharedPreferences preferences) {
return Preferences.numeralBase.getPreference(preferences);
}
@NotNull
public AngleUnit getAngleUnitsFromPrefs(@NotNull SharedPreferences preferences) {
return Preferences.angleUnit.getPreference(preferences);
}
//for tests only
void setDecimalGroupSymbols(@NotNull DecimalFormatSymbols decimalGroupSymbols) {
synchronized (lock) {
this.getEngine().setDecimalGroupSymbols(decimalGroupSymbols);
}
}
@Override
@NotNull
public AndroidMathRegistry<IConstant> getVarsRegistry() {
return varsRegistry;
}
@Override
@NotNull
public AndroidMathRegistry<Function> getFunctionsRegistry() {
return functionsRegistry;
}
@Override
@NotNull
public AndroidMathRegistry<Operator> getOperatorsRegistry() {
return operatorsRegistry;
}
@Override
@NotNull
public AndroidMathRegistry<Operator> getPostfixFunctionsRegistry() {
return postfixFunctionsRegistry;
}
@Override
@NotNull
public MathEngine getEngine() {
return engine;
}
// package protected for tests
void setTimeout(int timeout) {
this.timeout = timeout;
}
public void setAngleUnits(@NotNull AngleUnit angleUnits) {
getEngine().setAngleUnits(angleUnits);
}
public void setScienceNotation(boolean scienceNotation) {
getEngine().setScienceNotation(scienceNotation);
}
public void setNumeralBase(@NotNull NumeralBase numeralBase) {
getEngine().setNumeralBase(numeralBase);
}
// for tests only
void setThreadKiller(@Nullable ThreadKiller threadKiller) {
this.threadKiller = threadKiller;
}
private static interface ThreadKiller {
void killThread(@NotNull Thread thread);
}
private static class AndroidThreadKiller implements ThreadKiller {
@Override
public void killThread(@NotNull Thread thread) {
thread.setPriority(Thread.MIN_PRIORITY);
thread.interrupt();
}
}
public static class ThreadKillerImpl implements ThreadKiller {
@Override
public void killThread(@NotNull Thread thread) {
thread.setPriority(Thread.MIN_PRIORITY);
thread.stop();
}
}
}
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.calculator.model;
import android.content.Context;
import android.content.SharedPreferences;
import jscl.*;
import jscl.math.Generic;
import jscl.math.function.Function;
import jscl.math.function.IConstant;
import jscl.math.operator.Operator;
import jscl.text.ParseInterruptedException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.CalculatorApplication;
import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.prefs.BooleanPreference;
import org.solovyev.android.prefs.Preference;
import org.solovyev.android.prefs.StringPreference;
import org.solovyev.common.MutableObject;
import org.solovyev.common.msg.MessageRegistry;
import org.solovyev.common.text.EnumMapper;
import org.solovyev.common.text.NumberMapper;
import org.solovyev.common.text.StringUtils;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* User: serso
* Date: 9/12/11
* Time: 11:38 PM
*/
public enum CalculatorEngine {
instance;
private static final String GROUPING_SEPARATOR_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_calc_grouping_separator";
private static final String MULTIPLICATION_SIGN_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_calc_multiplication_sign";
private static final String MULTIPLICATION_SIGN_DEFAULT = "×";
private static final String MAX_CALCULATION_TIME_P_KEY = "calculation.max_calculation_time";
private static final String MAX_CALCULATION_TIME_DEFAULT = "5";
private static final String SCIENCE_NOTATION_P_KEY = "calculation.output.science_notation";
private static final boolean SCIENCE_NOTATION_DEFAULT = false;
private static final String ROUND_RESULT_P_KEY = "org.solovyev.android.calculator.CalculatorModel_round_result";
private static final boolean ROUND_RESULT_DEFAULT = true;
private static final String RESULT_PRECISION_P_KEY = "org.solovyev.android.calculator.CalculatorModel_result_precision";
private static final String RESULT_PRECISION_DEFAULT = "5";
private static final String NUMERAL_BASES_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_numeral_bases";
private static final String NUMERAL_BASES_DEFAULT = "dec";
private static final String ANGLE_UNITS_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_angle_units";
private static final String ANGLE_UNITS_DEFAULT = "deg";
public static class Preferences {
public static final Preference<String> groupingSeparator = StringPreference.newInstance(GROUPING_SEPARATOR_P_KEY, JsclMathEngine.GROUPING_SEPARATOR_DEFAULT);
public static final Preference<String> multiplicationSign = StringPreference.newInstance(MULTIPLICATION_SIGN_P_KEY, MULTIPLICATION_SIGN_DEFAULT);
public static final Preference<Integer> precision = StringPreference.newInstance(RESULT_PRECISION_P_KEY, RESULT_PRECISION_DEFAULT, new NumberMapper<Integer>(Integer.class));
public static final Preference<Boolean> roundResult = new BooleanPreference(ROUND_RESULT_P_KEY, ROUND_RESULT_DEFAULT);
public static final Preference<NumeralBase> numeralBase = StringPreference.newInstance(NUMERAL_BASES_P_KEY, NUMERAL_BASES_DEFAULT, EnumMapper.newInstance(NumeralBase.class));
public static final Preference<AngleUnit> angleUnit = StringPreference.newInstance(ANGLE_UNITS_P_KEY, ANGLE_UNITS_DEFAULT, EnumMapper.newInstance(AngleUnit.class));
public static final Preference<Boolean> scienceNotation = new BooleanPreference(SCIENCE_NOTATION_P_KEY, SCIENCE_NOTATION_DEFAULT);
public static final Preference<Integer> maxCalculationTime = StringPreference.newInstance(MAX_CALCULATION_TIME_P_KEY, MAX_CALCULATION_TIME_DEFAULT, new NumberMapper<Integer>(Integer.class));
private static final List<String> preferenceKeys = new ArrayList<String>();
static {
preferenceKeys.add(groupingSeparator.getKey());
preferenceKeys.add(multiplicationSign.getKey());
preferenceKeys.add(precision.getKey());
preferenceKeys.add(roundResult.getKey());
preferenceKeys.add(numeralBase.getKey());
preferenceKeys.add(angleUnit.getKey());
preferenceKeys.add(scienceNotation.getKey());
preferenceKeys.add(maxCalculationTime.getKey());
}
@NotNull
public static List<String> getPreferenceKeys() {
return Collections.unmodifiableList(preferenceKeys);
}
}
@NotNull
private final Object lock = new Object();
@NotNull
private MathEngine engine = JsclMathEngine.instance;
@NotNull
public final TextProcessor<PreparedExpression, String> preprocessor = ToJsclTextProcessor.getInstance();
@NotNull
private final AndroidMathRegistry<IConstant> varsRegistry = new AndroidVarsRegistryImpl(engine.getConstantsRegistry());
@NotNull
private final AndroidMathRegistry<jscl.math.function.Function> functionsRegistry = new AndroidFunctionsMathRegistry(engine.getFunctionsRegistry());
@NotNull
private final AndroidMathRegistry<Operator> operatorsRegistry = new AndroidOperatorsMathRegistry(engine.getOperatorsRegistry());
private final AndroidMathRegistry<Operator> postfixFunctionsRegistry = new AndroidPostfixFunctionsRegistry(engine.getPostfixFunctionsRegistry());
@Nullable
private ThreadKiller threadKiller = new AndroidThreadKiller();
// calculation thread timeout in seconds, after timeout thread would be interrupted
private int timeout = Integer.valueOf(MAX_CALCULATION_TIME_DEFAULT);
@NotNull
private String multiplicationSign = MULTIPLICATION_SIGN_DEFAULT;
CalculatorEngine() {
this.engine.setRoundResult(true);
this.engine.setUseGroupingSeparator(true);
}
@NotNull
public String getMultiplicationSign() {
return multiplicationSign;
}
public void setMultiplicationSign(@NotNull String multiplicationSign) {
this.multiplicationSign = multiplicationSign;
}
public static class Result {
@NotNull
private Generic genericResult;
@NotNull
private String result;
@NotNull
private JsclOperation userOperation;
public Result(@NotNull String result, @NotNull JsclOperation userOperation, @NotNull Generic genericResult) {
this.result = result;
this.userOperation = userOperation;
this.genericResult = genericResult;
}
@NotNull
public String getResult() {
return result;
}
@NotNull
public JsclOperation getUserOperation() {
return userOperation;
}
@NotNull
public Generic getGenericResult() {
return genericResult;
}
}
public Result evaluate(@NotNull JsclOperation operation,
@NotNull String expression) throws CalculatorParseException, CalculatorEvalException {
return evaluate(operation, expression, null);
}
public Result evaluate(@NotNull final JsclOperation operation,
@NotNull String expression,
@Nullable MessageRegistry mr) throws CalculatorParseException, CalculatorEvalException {
synchronized (lock) {
final StringBuilder sb = new StringBuilder();
final PreparedExpression preparedExpression = preprocessor.process(expression);
sb.append(preparedExpression);
//Log.d(CalculatorEngine.class.getName(), "Preprocessed expression: " + preparedExpression);
/*if (operation == JsclOperation.numeric && preparedExpression.isExistsUndefinedVar()) {
operation = JsclOperation.simplify;
if (mr != null) {
final String undefinedVars = CollectionsUtils.formatValue(preparedExpression.getUndefinedVars(), ", ", new Formatter<Var>() {
@Override
public String formatValue(@Nullable Var var) throws IllegalArgumentException {
return var != null ? var.getName() : "";
}
});
mr.addMessage(new AndroidMessage(R.string.c_simplify_instead_of_numeric, MessageType.info, undefinedVars));
}
}*/
final String jsclExpression = sb.toString();
final MutableObject<Generic> calculationResult = new MutableObject<Generic>(null);
final MutableObject<CalculatorParseException> parseException = new MutableObject<CalculatorParseException>(null);
final MutableObject<CalculatorEvalException> evalException = new MutableObject<CalculatorEvalException>(null);
final MutableObject<Thread> calculationThread = new MutableObject<Thread>(null);
final CountDownLatch latch = new CountDownLatch(1);
new Thread(new Runnable() {
@Override
public void run() {
final Thread thread = Thread.currentThread();
try {
//Log.d(CalculatorEngine.class.getName(), "Calculation thread started work: " + thread.getName());
//System.out.println(jsclExpression);
calculationThread.setObject(thread);
final Generic genericResult = operation.evaluateGeneric(jsclExpression);
// NOTE: toString() method must be called here as ArithmeticOperationException may occur in it (just to avoid later check!)
genericResult.toString();
calculationResult.setObject(genericResult);
} catch (AbstractJsclArithmeticException e) {
evalException.setObject(new CalculatorEvalException(e, e, jsclExpression));
} catch (ArithmeticException e) {
//System.out.println(e.getMessage());
parseException.setObject(new CalculatorParseException(R.string.msg_1, CalculatorApplication.getInstance(), jsclExpression, e.getMessage()));
} catch (StackOverflowError e) {
//System.out.println(StringUtils.fromStackTrace(e.getStackTrace()));
parseException.setObject(new CalculatorParseException(R.string.msg_2, CalculatorApplication.getInstance(), jsclExpression));
} catch (jscl.text.ParseException e) {
//System.out.println(e.getMessage());
parseException.setObject(new CalculatorParseException(e));
} catch (ParseInterruptedException e) {
//System.out.println(e.getMessage());
// do nothing - we ourselves interrupt the calculations
} finally {
//Log.d(CalculatorEngine.class.getName(), "Calculation thread ended work: " + thread.getName());
calculationThread.setObject(null);
latch.countDown();
}
}
}).start();
try {
//Log.d(CalculatorEngine.class.getName(), "Main thread is waiting: " + Thread.currentThread().getName());
latch.await(timeout, TimeUnit.SECONDS);
//Log.d(CalculatorEngine.class.getName(), "Main thread got up: " + Thread.currentThread().getName());
final CalculatorParseException parseExceptionObject = parseException.getObject();
final CalculatorEvalException evalExceptionObject = evalException.getObject();
final Object calculationResultLocal = calculationResult.getObject();
final Thread calculationThreadLocal = calculationThread.getObject();
if (calculationThreadLocal != null) {
if (threadKiller != null) {
threadKiller.killThread(calculationThreadLocal);
}
//calculationThreadLocal.stop();
}
if (parseExceptionObject != null || evalExceptionObject != null) {
if (operation == JsclOperation.numeric &&
(preparedExpression.isExistsUndefinedVar() || (evalExceptionObject != null && evalExceptionObject.getCause() instanceof NumeralBaseException))) {
return evaluate(JsclOperation.simplify, expression, mr);
}
if (parseExceptionObject != null) {
throw parseExceptionObject;
} else {
throw evalExceptionObject;
}
}
if (calculationResultLocal == null) {
throw new CalculatorParseException(R.string.msg_3, CalculatorApplication.getInstance(), jsclExpression);
}
} catch (InterruptedException e) {
throw new CalculatorParseException(R.string.msg_4, CalculatorApplication.getInstance(), jsclExpression);
}
final Generic genericResult = calculationResult.getObject();
return new Result(operation.getFromProcessor().process(genericResult), operation, genericResult);
}
}
public void setPrecision(int precision) {
this.getEngine().setPrecision(precision);
}
public void setRoundResult(boolean roundResult) {
this.getEngine().setRoundResult(roundResult);
}
public void init(@Nullable Context context, @Nullable SharedPreferences preferences) {
synchronized (lock) {
reset(context, preferences);
}
}
public void reset(@Nullable Context context, @Nullable SharedPreferences preferences) {
synchronized (lock) {
softReset(context, preferences);
varsRegistry.load(context, preferences);
functionsRegistry.load(context, preferences);
operatorsRegistry.load(context, preferences);
postfixFunctionsRegistry.load(context, preferences);
}
}
public void softReset(@Nullable Context context, @Nullable SharedPreferences preferences) {
synchronized (lock) {
if (preferences != null) {
this.setPrecision(Preferences.precision.getPreference(preferences));
this.setRoundResult(Preferences.roundResult.getPreference(preferences));
this.setAngleUnits(getAngleUnitsFromPrefs(preferences));
this.setNumeralBase(getNumeralBaseFromPrefs(preferences));
this.setMultiplicationSign(Preferences.multiplicationSign.getPreference(preferences));
this.setScienceNotation(Preferences.scienceNotation.getPreference(preferences));
this.setTimeout(Preferences.maxCalculationTime.getPreference(preferences));
final String groupingSeparator = Preferences.groupingSeparator.getPreference(preferences);
if (StringUtils.isEmpty(groupingSeparator)) {
this.getEngine().setUseGroupingSeparator(false);
} else {
this.getEngine().setUseGroupingSeparator(true);
this.getEngine().setGroupingSeparator(groupingSeparator.charAt(0));
}
}
}
}
@NotNull
public NumeralBase getNumeralBaseFromPrefs(@NotNull SharedPreferences preferences) {
return Preferences.numeralBase.getPreference(preferences);
}
@NotNull
public AngleUnit getAngleUnitsFromPrefs(@NotNull SharedPreferences preferences) {
return Preferences.angleUnit.getPreference(preferences);
}
//for tests only
void setDecimalGroupSymbols(@NotNull DecimalFormatSymbols decimalGroupSymbols) {
synchronized (lock) {
this.getEngine().setDecimalGroupSymbols(decimalGroupSymbols);
}
}
@NotNull
public AndroidMathRegistry<IConstant> getVarsRegistry() {
return varsRegistry;
}
@NotNull
public AndroidMathRegistry<Function> getFunctionsRegistry() {
return functionsRegistry;
}
@NotNull
public AndroidMathRegistry<Operator> getOperatorsRegistry() {
return operatorsRegistry;
}
@NotNull
public AndroidMathRegistry<Operator> getPostfixFunctionsRegistry() {
return postfixFunctionsRegistry;
}
@NotNull
public MathEngine getEngine() {
return engine;
}
// package protected for tests
void setTimeout(int timeout) {
this.timeout = timeout;
}
public void setAngleUnits(@NotNull AngleUnit angleUnits) {
getEngine().setAngleUnits(angleUnits);
}
public void setScienceNotation(boolean scienceNotation) {
getEngine().setScienceNotation(scienceNotation);
}
public void setNumeralBase(@NotNull NumeralBase numeralBase) {
getEngine().setNumeralBase(numeralBase);
}
// for tests only
void setThreadKiller(@Nullable ThreadKiller threadKiller) {
this.threadKiller = threadKiller;
}
private static interface ThreadKiller {
void killThread(@NotNull Thread thread);
}
private static class AndroidThreadKiller implements ThreadKiller {
@Override
public void killThread(@NotNull Thread thread) {
thread.setPriority(Thread.MIN_PRIORITY);
thread.interrupt();
}
}
public static class ThreadKillerImpl implements ThreadKiller {
@Override
public void killThread(@NotNull Thread thread) {
thread.setPriority(Thread.MIN_PRIORITY);
thread.stop();
}
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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.model;
import jscl.AbstractJsclArithmeticException;
import org.jetbrains.annotations.NotNull;
import org.solovyev.common.exceptions.SersoException;
import org.solovyev.common.msg.Message;
import org.solovyev.common.msg.MessageType;
import java.util.List;
import java.util.Locale;
/**
* User: serso
* Date: 12/8/11
* Time: 1:27 AM
*/
public class CalculatorEvalException extends SersoException implements Message {
@NotNull
private final Message message;
@NotNull
private final String expression;
public CalculatorEvalException(@NotNull Message message, @NotNull Throwable cause, String expression) {
super(cause);
this.message = message;
this.expression = expression;
}
@NotNull
public String getExpression() {
return expression;
}
@NotNull
@Override
public String getMessageCode() {
return this.message.getMessageCode();
}
@NotNull
@Override
public List<Object> getParameters() {
return this.message.getParameters();
}
@NotNull
@Override
public MessageType getMessageType() {
return this.message.getMessageType();
}
@Override
@NotNull
public String getLocalizedMessage() {
return this.message.getLocalizedMessage(Locale.getDefault());
}
@NotNull
@Override
public String getLocalizedMessage(@NotNull Locale locale) {
return this.message.getLocalizedMessage(locale);
}
}

View File

@@ -0,0 +1,91 @@
/*
* 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.model;
import android.app.Application;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.msg.AndroidMessage;
import org.solovyev.common.exceptions.SersoException;
import org.solovyev.common.msg.Message;
import org.solovyev.common.msg.MessageType;
import java.util.List;
import java.util.Locale;
/**
* User: serso
* Date: 10/6/11
* Time: 9:25 PM
*/
public class CalculatorParseException extends SersoException implements Message {
@NotNull
private final Message message;
@NotNull
private final String expression;
@Nullable
private final Integer position;
public CalculatorParseException(@NotNull jscl.text.ParseException jsclParseException) {
this.message = jsclParseException;
this.expression = jsclParseException.getExpression();
this.position = jsclParseException.getPosition();
}
public CalculatorParseException(@NotNull Integer messageId, @NotNull Application application, @Nullable Integer position, @NotNull String expression, Object... parameters) {
this.message = new AndroidMessage(messageId, MessageType.error, application, parameters);
this.expression = expression;
this.position = position;
}
public CalculatorParseException(@NotNull Integer messageId, @NotNull Application application, @NotNull String expression, Object... parameters) {
this(messageId, application, null, expression, parameters);
}
@NotNull
public String getExpression() {
return expression;
}
@Nullable
public Integer getPosition() {
return position;
}
@NotNull
@Override
public String getMessageCode() {
return this.message.getMessageCode();
}
@NotNull
@Override
public List<Object> getParameters() {
return this.message.getParameters();
}
@NotNull
@Override
public MessageType getMessageType() {
return this.message.getMessageType();
}
@Override
@Nullable
public String getLocalizedMessage() {
return this.message.getLocalizedMessage(Locale.getDefault());
}
@NotNull
@Override
public String getLocalizedMessage(@NotNull Locale locale) {
return this.message.getLocalizedMessage(locale);
}
}

View File

@@ -0,0 +1,26 @@
/*
* 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.model;
import jscl.math.Generic;
import org.jetbrains.annotations.NotNull;
/**
* User: serso
* Date: 10/18/11
* Time: 10:39 PM
*/
public enum DummyTextProcessor implements TextProcessor<String, Generic> {
instance;
@NotNull
@Override
public String process(@NotNull Generic s) throws CalculatorParseException {
return s.toString();
}
}

View File

@@ -0,0 +1,92 @@
package org.solovyev.android.calculator.model;
import jscl.math.Generic;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.math.MathType;
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(CalculatorEngine.instance.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;
}
}

View File

@@ -0,0 +1,55 @@
/*
* 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.model;
import jscl.MathEngine;
import jscl.NumeralBase;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.math.MathType;
/**
* User: serso
* Date: 12/15/11
* Time: 8:33 PM
*/
public class LiteNumberBuilder extends AbstractNumberBuilder {
public LiteNumberBuilder(@NotNull MathEngine engine) {
super(engine);
this.nb = engine.getNumeralBase();
}
public void process(@NotNull MathType.Result mathTypeResult) {
if (canContinue(mathTypeResult)) {
// let's continue building number
if (numberBuilder == null) {
// if new number => create new builder
numberBuilder = new StringBuilder();
}
if (mathTypeResult.getMathType() != MathType.numeral_base) {
// just add matching string
numberBuilder.append(mathTypeResult.getMatch());
} else {
// set explicitly numeral base (do not include it into number)
nb = NumeralBase.getByPrefix(mathTypeResult.getMatch());
}
} else {
// process current number (and go to the next one)
if (numberBuilder != null) {
numberBuilder = null;
// must set default numeral base (exit numeral base mode)
nb = engine.getNumeralBase();
}
}
}
}

View File

@@ -0,0 +1,202 @@
/*
* 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.model;
import jscl.MathContext;
import jscl.MathEngine;
import jscl.NumeralBase;
import jscl.math.numeric.Real;
import jscl.text.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.math.MathType;
import org.solovyev.common.MutableObject;
import java.util.ArrayList;
import java.util.List;
/**
* User: serso
* Date: 10/23/11
* Time: 2:57 PM
*/
public class NumberBuilder extends AbstractNumberBuilder {
public NumberBuilder(@NotNull MathEngine engine) {
super(engine);
}
/**
* Method replaces number in text according to some rules (e.g. formatting)
*
* @param text text where number can be replaced
* @param mathTypeResult math type result of current token
* @param offset offset between new number length and old number length (newNumberLength - oldNumberLength)
*
*
* @return new math type result (as one can be changed due to substituting of number with constant)
*/
@NotNull
public MathType.Result process(@NotNull StringBuilder text, @NotNull MathType.Result mathTypeResult, @Nullable MutableObject<Integer> offset) {
final MathType.Result possibleResult;
if (canContinue(mathTypeResult)) {
// let's continue building number
if (numberBuilder == null) {
// if new number => create new builder
numberBuilder = new StringBuilder();
}
if (mathTypeResult.getMathType() != MathType.numeral_base) {
// just add matching string
numberBuilder.append(mathTypeResult.getMatch());
} else {
// set explicitly numeral base (do not include it into number)
nb = NumeralBase.getByPrefix(mathTypeResult.getMatch());
}
possibleResult = null;
} else {
// process current number (and go to the next one)
possibleResult = processNumber(text, offset);
}
return possibleResult == null ? mathTypeResult : possibleResult;
}
/**
* Method replaces number in text according to some rules (e.g. formatting)
*
* @param text text where number can be replaced
* @param offset offset between new number length and old number length (newNumberLength - oldNumberLength)
*
* @return new math type result (as one can be changed due to substituting of number with constant)
*/
@Nullable
public MathType.Result processNumber(@NotNull StringBuilder text, @Nullable MutableObject<Integer> offset) {
// total number of trimmed chars
int trimmedChars = 0;
String number = null;
// save numeral base (as later it might be replaced)
final NumeralBase localNb = getNumeralBase();
if (numberBuilder != null) {
try {
number = numberBuilder.toString();
// let's get rid of unnecessary characters (grouping separators, + after E)
final List<String> tokens = new ArrayList<String>();
tokens.addAll(MathType.grouping_separator.getTokens());
// + after E can be omitted: 10+E = 10E (NOTE: - cannot be omitted )
tokens.add("+");
for (String groupingSeparator : tokens) {
final String trimmedNumber = number.replace(groupingSeparator, "");
trimmedChars += number.length() - trimmedNumber.length();
number = trimmedNumber;
}
// check if number still valid
toDouble(number, getNumeralBase(), engine);
} catch (NumberFormatException e) {
// number is not valid => stop
number = null;
}
numberBuilder = null;
// must set default numeral base (exit numeral base mode)
nb = engine.getNumeralBase();
}
return replaceNumberInText(text, number, trimmedChars, offset, localNb, engine);
}
@Nullable
private static MathType.Result replaceNumberInText(@NotNull StringBuilder text,
@Nullable String number,
int trimmedChars,
@Nullable MutableObject<Integer> offset,
@NotNull NumeralBase nb,
@NotNull final MathEngine engine) {
MathType.Result result = null;
if (number != null) {
// in any case remove old number from text
final int oldNumberLength = number.length() + trimmedChars;
text.delete(text.length() - oldNumberLength, text.length());
final String newNumber = formatNumber(number, nb, engine);
if (offset != null) {
// register offset between old number and new number
offset.setObject(newNumber.length() - oldNumberLength);
}
text.append(newNumber);
}
return result;
}
@NotNull
private static String formatNumber(@NotNull String number, @NotNull NumeralBase nb, @NotNull MathEngine engine) {
String result;
int indexOfDot = number.indexOf('.');
if (indexOfDot < 0) {
int indexOfE;
if (nb == NumeralBase.hex) {
indexOfE = -1;
} else {
indexOfE = number.indexOf(MathType.POWER_10);
}
if (indexOfE < 0) {
result = engine.addGroupingSeparators(nb, number);
} else {
final String partBeforeE;
if (indexOfE != 0) {
partBeforeE = engine.addGroupingSeparators(nb, number.substring(0, indexOfE));
} else {
partBeforeE = "";
}
result = partBeforeE + number.substring(indexOfE);
}
} else {
final String integerPart;
if (indexOfDot != 0) {
integerPart = engine.addGroupingSeparators(nb, number.substring(0, indexOfDot));
} else {
integerPart = "";
}
result = integerPart + number.substring(indexOfDot);
}
return result;
}
@NotNull
private static Double toDouble(@NotNull String s, @NotNull NumeralBase nb, @NotNull final MathContext mc) throws NumberFormatException {
final NumeralBase defaultNb = mc.getNumeralBase();
try {
mc.setNumeralBase(nb);
try {
return JsclIntegerParser.parser.parse(Parser.Parameters.newInstance(s, new MutableInt(0), mc), null).content().doubleValue();
} catch (ParseException e) {
try {
return ((Real) DoubleParser.parser.parse(Parser.Parameters.newInstance(s, new MutableInt(0), mc), null).content()).doubleValue();
} catch (ParseException e1) {
throw new NumberFormatException();
}
}
} finally {
mc.setNumeralBase(defaultNb);
}
}
}

View File

@@ -0,0 +1,65 @@
/*
* 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.model;
import jscl.math.function.IConstant;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* User: serso
* Date: 10/18/11
* Time: 10:07 PM
*/
public class PreparedExpression implements CharSequence{
@NotNull
private String expression;
@NotNull
private List<IConstant> undefinedVars;
public PreparedExpression(@NotNull String expression, @NotNull List<IConstant> undefinedVars) {
this.expression = expression;
this.undefinedVars = undefinedVars;
}
@NotNull
public String getExpression() {
return expression;
}
public boolean isExistsUndefinedVar() {
return !this.undefinedVars.isEmpty();
}
@NotNull
public List<IConstant> getUndefinedVars() {
return undefinedVars;
}
@Override
public int length() {
return expression.length();
}
@Override
public char charAt(int i) {
return expression.charAt(i);
}
@Override
public CharSequence subSequence(int i, int i1) {
return expression.subSequence(i, i1);
}
@Override
public String toString() {
return this.expression;
}
}

View File

@@ -0,0 +1,14 @@
package org.solovyev.android.calculator.model;
import org.jetbrains.annotations.NotNull;
/**
* User: serso
* Date: 9/26/11
* Time: 12:12 PM
*/
public interface TextProcessor<TO extends CharSequence, FROM> {
@NotNull
TO process(@NotNull FROM from) throws CalculatorParseException;
}

View File

@@ -0,0 +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.model;
import jscl.math.function.IConstant;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.CalculatorApplication;
import org.solovyev.android.calculator.R;
import org.solovyev.common.StartsWithFinder;
import org.solovyev.android.calculator.math.MathType;
import org.solovyev.common.collections.CollectionsUtils;
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(CalculatorEngine.instance.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(R.string.msg_5, CalculatorApplication.getInstance(), i, s, 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(R.string.msg_6, CalculatorApplication.getInstance(), s);
} 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(CalculatorEngine.instance.getVarsRegistry().getNames(), startsWithFinder);
if (varName != null) {
final IConstant var = CalculatorEngine.instance.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

@@ -35,9 +35,9 @@ import org.achartengine.tools.ZoomListener;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.CalculatorParseException;
import org.solovyev.android.calculator.PreparedExpression;
import org.solovyev.android.calculator.ToJsclTextProcessor;
import org.solovyev.android.calculator.model.CalculatorParseException;
import org.solovyev.android.calculator.model.PreparedExpression;
import org.solovyev.android.calculator.model.ToJsclTextProcessor;
import org.solovyev.common.MutableObject;
import java.io.Serializable;

View File

@@ -6,14 +6,14 @@ import android.view.View;
import android.view.WindowManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.math.units.Unit;
import org.solovyev.math.units.UnitImpl;
import org.solovyev.android.Unit;
import org.solovyev.android.UnitImpl;
import org.solovyev.android.calculator.AndroidNumeralBase;
import org.solovyev.android.calculator.CalculatorModel;
import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.model.CalculatorEngine;
import org.solovyev.android.calculator.CalculatorParseException;
import org.solovyev.android.calculator.ToJsclTextProcessor;
import org.solovyev.android.calculator.model.CalculatorParseException;
import org.solovyev.android.calculator.model.ToJsclTextProcessor;
import org.solovyev.common.MutableObject;
import org.solovyev.common.text.StringUtils;

View File

@@ -9,13 +9,8 @@ package org.solovyev.android.calculator.view;
import jscl.MathContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.AbstractNumberBuilder;
import org.solovyev.android.calculator.CalculatorParseException;
import org.solovyev.android.calculator.LiteNumberBuilder;
import org.solovyev.android.calculator.NumberBuilder;
import org.solovyev.android.calculator.math.MathType;
import org.solovyev.android.calculator.model.*;
import org.solovyev.android.calculator.text.TextProcessor;
import org.solovyev.common.MutableObject;
import java.util.HashMap;

View File

@@ -10,10 +10,10 @@ import android.view.ViewGroup;
import android.widget.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.math.units.Unit;
import org.solovyev.math.units.UnitConverter;
import org.solovyev.math.units.UnitImpl;
import org.solovyev.math.units.UnitType;
import org.solovyev.android.Unit;
import org.solovyev.android.UnitConverter;
import org.solovyev.android.UnitImpl;
import org.solovyev.android.UnitType;
import org.solovyev.android.calculator.R;
import org.solovyev.android.view.ViewBuilder;
import org.solovyev.android.view.ViewFromLayoutBuilder;

View File

@@ -4,8 +4,6 @@ import junit.framework.Assert;
import org.jetbrains.annotations.NotNull;
import org.junit.Test;
import org.solovyev.android.calculator.AndroidNumeralBase;
import org.solovyev.math.units.Unit;
import org.solovyev.math.units.UnitConverter;
import java.util.Date;
import java.util.Random;

View File

@@ -12,7 +12,7 @@ import jscl.NumeralBase;
import junit.framework.Assert;
import org.junit.Test;
import org.solovyev.android.calculator.model.CalculatorEngine;
import org.solovyev.android.calculator.text.TextProcessor;
import org.solovyev.android.calculator.model.TextProcessor;
import org.solovyev.android.calculator.view.TextHighlighter;
import java.util.Date;

View File

@@ -1,321 +1,320 @@
/*
* 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.history;
import jscl.math.Generic;
import junit.framework.Assert;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.junit.Test;
import org.solovyev.android.calculator.CalculatorDisplay;
import org.solovyev.android.calculator.Editor;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.common.equals.CollectionEqualizer;
import org.solovyev.common.equals.EqualsTool;
import org.solovyev.common.history.HistoryHelper;
import org.solovyev.common.history.SimpleHistoryHelper;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* User: serso
* Date: 12/17/11
* Time: 10:01 PM
*/
public class HistoryUtilsTest {
@Test
public void testFromXml() throws Exception {
}
private static final String emptyHistory = "<history>\n" +
" <historyItems class=\"java.util.ArrayList\"/>\n" +
"</history>";
private static final String toXml1 = "<history>\n" +
" <historyItems class=\"java.util.ArrayList\">\n" +
" <calculatorHistoryState>\n" +
" <time>100000000</time>\n" +
" <editorState>\n" +
" <cursorPosition>3</cursorPosition>\n" +
" <text>1+1</text>\n" +
" </editorState>\n" +
" <displayState>\n" +
" <editorState>\n" +
" <cursorPosition>1</cursorPosition>\n" +
" <text>Error</text>\n" +
" </editorState>\n" +
" <jsclOperation>simplify</jsclOperation>\n" +
" </displayState>\n" +
" </calculatorHistoryState>\n" +
" </historyItems>\n" +
"</history>";
private static final String toXml2 = "<history>\n" +
" <historyItems class=\"java.util.ArrayList\">\n" +
" <calculatorHistoryState>\n" +
" <time>100000000</time>\n" +
" <editorState>\n" +
" <cursorPosition>3</cursorPosition>\n" +
" <text>1+1</text>\n" +
" </editorState>\n" +
" <displayState>\n" +
" <editorState>\n" +
" <cursorPosition>1</cursorPosition>\n" +
" <text>Error</text>\n" +
" </editorState>\n" +
" <jsclOperation>simplify</jsclOperation>\n" +
" </displayState>\n" +
" </calculatorHistoryState>\n" +
" <calculatorHistoryState>\n" +
" <time>100000000</time>\n" +
" <editorState>\n" +
" <cursorPosition>2</cursorPosition>\n" +
" <text>5/6</text>\n" +
" </editorState>\n" +
" <displayState>\n" +
" <editorState>\n" +
" <cursorPosition>3</cursorPosition>\n" +
" <text>5/6</text>\n" +
" </editorState>\n" +
" <jsclOperation>numeric</jsclOperation>\n" +
" </displayState>\n" +
" </calculatorHistoryState>\n" +
" <calculatorHistoryState>\n" +
" <time>100000000</time>\n" +
" <editorState>\n" +
" <cursorPosition>1</cursorPosition>\n" +
" <text>null</text>\n" +
" </editorState>\n" +
" <displayState>\n" +
" <editorState>\n" +
" <cursorPosition>1</cursorPosition>\n" +
" <text>Error</text>\n" +
" </editorState>\n" +
" <jsclOperation>elementary</jsclOperation>\n" +
" </displayState>\n" +
" </calculatorHistoryState>\n" +
" <calculatorHistoryState>\n" +
" <time>100000000</time>\n" +
" <editorState>\n" +
" <cursorPosition>0</cursorPosition>\n" +
" <text>4+5/35sin(41)+dfdsfsdfs</text>\n" +
" </editorState>\n" +
" <displayState>\n" +
" <editorState>\n" +
" <cursorPosition>1</cursorPosition>\n" +
" <text>4+5/35sin(41)+dfdsfsdfs</text>\n" +
" </editorState>\n" +
" <jsclOperation>numeric</jsclOperation>\n" +
" </displayState>\n" +
" </calculatorHistoryState>\n" +
" </historyItems>\n" +
"</history>";
@Test
public void testToXml() throws Exception {
final Date date = new Date(100000000);
HistoryHelper<CalculatorHistoryState> history = new SimpleHistoryHelper<CalculatorHistoryState>();
CalculatorDisplay calculatorDisplay = new TestCalculatorDisplay();
calculatorDisplay.setErrorMessage("error_msg1");
calculatorDisplay.setText("Error");
calculatorDisplay.setSelection(1);
calculatorDisplay.setJsclOperation(JsclOperation.simplify);
Editor calculatorEditor = new TestEditor();
calculatorEditor.setSelection(3);
calculatorEditor.setText("1+1");
CalculatorHistoryState state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay);
state.setTime(date.getTime());
history.addState(state);
Assert.assertEquals(emptyHistory, HistoryUtils.toXml(history.getStates()));
state.setSaved(true);
Assert.assertEquals(toXml1, HistoryUtils.toXml(history.getStates()));
calculatorDisplay = new TestCalculatorDisplay();
calculatorDisplay.setErrorMessage(null);
calculatorDisplay.setText("5/6");
calculatorDisplay.setSelection(3);
calculatorDisplay.setJsclOperation(JsclOperation.numeric);
calculatorEditor = new TestEditor();
calculatorEditor.setSelection(2);
calculatorEditor.setText("5/6");
state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay);
state.setSaved(true);
state.setTime(date.getTime());
history.addState(state);
calculatorDisplay = new TestCalculatorDisplay();
calculatorDisplay.setErrorMessage("error_msg2");
calculatorDisplay.setText("Error");
calculatorDisplay.setSelection(1);
calculatorDisplay.setJsclOperation(JsclOperation.elementary);
calculatorEditor = new TestEditor();
calculatorEditor.setSelection(1);
calculatorEditor.setText(null);
state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay);
state.setSaved(true);
state.setTime(date.getTime());
history.addState(state);
calculatorDisplay = new TestCalculatorDisplay();
calculatorDisplay.setErrorMessage(null);
calculatorDisplay.setText("4+5/35sin(41)+dfdsfsdfs");
calculatorDisplay.setSelection(1);
calculatorDisplay.setJsclOperation(JsclOperation.numeric);
calculatorEditor = new TestEditor();
calculatorEditor.setSelection(0);
calculatorEditor.setText("4+5/35sin(41)+dfdsfsdfs");
state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay);
state.setSaved(true);
state.setTime(date.getTime());
history.addState(state);
String xml = HistoryUtils.toXml(history.getStates());
Assert.assertEquals(toXml2, xml);
final List<CalculatorHistoryState> fromXml = new ArrayList<CalculatorHistoryState>();
final HistoryHelper<CalculatorHistoryState> historyFromXml = new SimpleHistoryHelper<CalculatorHistoryState>();
HistoryUtils.fromXml(xml, fromXml);
for (CalculatorHistoryState historyState : fromXml) {
historyFromXml.addState(historyState);
}
Assert.assertEquals(history.getStates().size(), historyFromXml.getStates().size());
for (CalculatorHistoryState historyState : history.getStates()) {
historyState.setId(0);
historyState.setSaved(true);
}
for (CalculatorHistoryState historyState : historyFromXml.getStates()) {
historyState.setId(0);
historyState.setSaved(true);
}
Assert.assertTrue(EqualsTool.areEqual(history.getStates(), historyFromXml.getStates(), new CollectionEqualizer<CalculatorHistoryState>(null)));
}
private static class TestCalculatorDisplay implements CalculatorDisplay {
@NotNull
private final TestEditor testEditor = new TestEditor();
private boolean valid;
private String errorMessage;
private JsclOperation operation;
private Generic genericResult;
@Override
public boolean isValid() {
return this.valid;
}
@Override
public void setValid(boolean valid) {
this.valid = valid;
}
@Override
public String getErrorMessage() {
return this.errorMessage;
}
@Override
public void setErrorMessage(@Nullable String errorMessage) {
this.errorMessage = errorMessage;
}
@Override
public void setJsclOperation(@NotNull JsclOperation jsclOperation) {
this.operation = jsclOperation;
}
@NotNull
@Override
public JsclOperation getJsclOperation() {
return this.operation;
}
@Override
public void setGenericResult(@Nullable Generic genericResult) {
this.genericResult = genericResult;
}
@Override
public Generic getGenericResult() {
return this.genericResult;
}
@Override
public CharSequence getText() {
return this.testEditor.getText();
}
@Override
public void setText(@Nullable CharSequence text) {
this.testEditor.setText(text);
}
@Override
public int getSelection() {
return this.testEditor.getSelection();
}
@Override
public void setSelection(int selection) {
this.testEditor.setSelection(selection);
}
}
private static class TestEditor implements Editor {
@Nullable
private CharSequence text;
private int selection;
@Nullable
@Override
public CharSequence getText() {
return this.text;
}
@Override
public void setText(@Nullable CharSequence text) {
this.text = text;
}
@Override
public int getSelection() {
return this.selection;
}
@Override
public void setSelection(int selection) {
this.selection = selection;
}
}
}
/*
* 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.history;
import jscl.math.Generic;
import junit.framework.Assert;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.junit.Test;
import org.solovyev.android.calculator.ICalculatorDisplay;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.common.equals.CollectionEqualizer;
import org.solovyev.common.equals.EqualsTool;
import org.solovyev.common.history.HistoryHelper;
import org.solovyev.common.history.SimpleHistoryHelper;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* User: serso
* Date: 12/17/11
* Time: 10:01 PM
*/
public class HistoryUtilsTest {
@Test
public void testFromXml() throws Exception {
}
private static final String emptyHistory = "<history>\n" +
" <historyItems class=\"java.util.ArrayList\"/>\n" +
"</history>";
private static final String toXml1 = "<history>\n" +
" <historyItems class=\"java.util.ArrayList\">\n" +
" <calculatorHistoryState>\n" +
" <time>100000000</time>\n" +
" <editorState>\n" +
" <cursorPosition>3</cursorPosition>\n" +
" <text>1+1</text>\n" +
" </editorState>\n" +
" <displayState>\n" +
" <editorState>\n" +
" <cursorPosition>1</cursorPosition>\n" +
" <text>Error</text>\n" +
" </editorState>\n" +
" <jsclOperation>simplify</jsclOperation>\n" +
" </displayState>\n" +
" </calculatorHistoryState>\n" +
" </historyItems>\n" +
"</history>";
private static final String toXml2 = "<history>\n" +
" <historyItems class=\"java.util.ArrayList\">\n" +
" <calculatorHistoryState>\n" +
" <time>100000000</time>\n" +
" <editorState>\n" +
" <cursorPosition>3</cursorPosition>\n" +
" <text>1+1</text>\n" +
" </editorState>\n" +
" <displayState>\n" +
" <editorState>\n" +
" <cursorPosition>1</cursorPosition>\n" +
" <text>Error</text>\n" +
" </editorState>\n" +
" <jsclOperation>simplify</jsclOperation>\n" +
" </displayState>\n" +
" </calculatorHistoryState>\n" +
" <calculatorHistoryState>\n" +
" <time>100000000</time>\n" +
" <editorState>\n" +
" <cursorPosition>2</cursorPosition>\n" +
" <text>5/6</text>\n" +
" </editorState>\n" +
" <displayState>\n" +
" <editorState>\n" +
" <cursorPosition>3</cursorPosition>\n" +
" <text>5/6</text>\n" +
" </editorState>\n" +
" <jsclOperation>numeric</jsclOperation>\n" +
" </displayState>\n" +
" </calculatorHistoryState>\n" +
" <calculatorHistoryState>\n" +
" <time>100000000</time>\n" +
" <editorState>\n" +
" <cursorPosition>1</cursorPosition>\n" +
" <text>null</text>\n" +
" </editorState>\n" +
" <displayState>\n" +
" <editorState>\n" +
" <cursorPosition>1</cursorPosition>\n" +
" <text>Error</text>\n" +
" </editorState>\n" +
" <jsclOperation>elementary</jsclOperation>\n" +
" </displayState>\n" +
" </calculatorHistoryState>\n" +
" <calculatorHistoryState>\n" +
" <time>100000000</time>\n" +
" <editorState>\n" +
" <cursorPosition>0</cursorPosition>\n" +
" <text>4+5/35sin(41)+dfdsfsdfs</text>\n" +
" </editorState>\n" +
" <displayState>\n" +
" <editorState>\n" +
" <cursorPosition>1</cursorPosition>\n" +
" <text>4+5/35sin(41)+dfdsfsdfs</text>\n" +
" </editorState>\n" +
" <jsclOperation>numeric</jsclOperation>\n" +
" </displayState>\n" +
" </calculatorHistoryState>\n" +
" </historyItems>\n" +
"</history>";
@Test
public void testToXml() throws Exception {
final Date date = new Date(100000000);
HistoryHelper<CalculatorHistoryState> history = new SimpleHistoryHelper<CalculatorHistoryState>();
ICalculatorDisplay calculatorDisplay = new TestCalculatorDisplay();
calculatorDisplay.setErrorMessage("error_msg1");
calculatorDisplay.setText("Error");
calculatorDisplay.setSelection(1);
calculatorDisplay.setJsclOperation(JsclOperation.simplify);
Editor calculatorEditor = new TestEditor();
calculatorEditor.setSelection(3);
calculatorEditor.setText("1+1");
CalculatorHistoryState state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay);
state.setTime(date.getTime());
history.addState(state);
Assert.assertEquals(emptyHistory, HistoryUtils.toXml(history.getStates()));
state.setSaved(true);
Assert.assertEquals(toXml1, HistoryUtils.toXml(history.getStates()));
calculatorDisplay = new TestCalculatorDisplay();
calculatorDisplay.setErrorMessage(null);
calculatorDisplay.setText("5/6");
calculatorDisplay.setSelection(3);
calculatorDisplay.setJsclOperation(JsclOperation.numeric);
calculatorEditor = new TestEditor();
calculatorEditor.setSelection(2);
calculatorEditor.setText("5/6");
state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay);
state.setSaved(true);
state.setTime(date.getTime());
history.addState(state);
calculatorDisplay = new TestCalculatorDisplay();
calculatorDisplay.setErrorMessage("error_msg2");
calculatorDisplay.setText("Error");
calculatorDisplay.setSelection(1);
calculatorDisplay.setJsclOperation(JsclOperation.elementary);
calculatorEditor = new TestEditor();
calculatorEditor.setSelection(1);
calculatorEditor.setText(null);
state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay);
state.setSaved(true);
state.setTime(date.getTime());
history.addState(state);
calculatorDisplay = new TestCalculatorDisplay();
calculatorDisplay.setErrorMessage(null);
calculatorDisplay.setText("4+5/35sin(41)+dfdsfsdfs");
calculatorDisplay.setSelection(1);
calculatorDisplay.setJsclOperation(JsclOperation.numeric);
calculatorEditor = new TestEditor();
calculatorEditor.setSelection(0);
calculatorEditor.setText("4+5/35sin(41)+dfdsfsdfs");
state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay);
state.setSaved(true);
state.setTime(date.getTime());
history.addState(state);
String xml = HistoryUtils.toXml(history.getStates());
Assert.assertEquals(toXml2, xml);
final List<CalculatorHistoryState> fromXml = new ArrayList<CalculatorHistoryState>();
final HistoryHelper<CalculatorHistoryState> historyFromXml = new SimpleHistoryHelper<CalculatorHistoryState>();
HistoryUtils.fromXml(xml, fromXml);
for (CalculatorHistoryState historyState : fromXml) {
historyFromXml.addState(historyState);
}
Assert.assertEquals(history.getStates().size(), historyFromXml.getStates().size());
for (CalculatorHistoryState historyState : history.getStates()) {
historyState.setId(0);
historyState.setSaved(true);
}
for (CalculatorHistoryState historyState : historyFromXml.getStates()) {
historyState.setId(0);
historyState.setSaved(true);
}
Assert.assertTrue(EqualsTool.areEqual(history.getStates(), historyFromXml.getStates(), new CollectionEqualizer<CalculatorHistoryState>(null)));
}
private static class TestCalculatorDisplay implements ICalculatorDisplay {
@NotNull
private final TestEditor testEditor = new TestEditor();
private boolean valid;
private String errorMessage;
private JsclOperation operation;
private Generic genericResult;
@Override
public boolean isValid() {
return this.valid;
}
@Override
public void setValid(boolean valid) {
this.valid = valid;
}
@Override
public String getErrorMessage() {
return this.errorMessage;
}
@Override
public void setErrorMessage(@Nullable String errorMessage) {
this.errorMessage = errorMessage;
}
@Override
public void setJsclOperation(@NotNull JsclOperation jsclOperation) {
this.operation = jsclOperation;
}
@NotNull
@Override
public JsclOperation getJsclOperation() {
return this.operation;
}
@Override
public void setGenericResult(@Nullable Generic genericResult) {
this.genericResult = genericResult;
}
@Override
public Generic getGenericResult() {
return this.genericResult;
}
@Override
public CharSequence getText() {
return this.testEditor.getText();
}
@Override
public void setText(@Nullable CharSequence text) {
this.testEditor.setText(text);
}
@Override
public int getSelection() {
return this.testEditor.getSelection();
}
@Override
public void setSelection(int selection) {
this.testEditor.setSelection(selection);
}
}
private static class TestEditor implements Editor {
@Nullable
private CharSequence text;
private int selection;
@Nullable
@Override
public CharSequence getText() {
return this.text;
}
@Override
public void setText(@Nullable CharSequence text) {
this.text = text;
}
@Override
public int getSelection() {
return this.selection;
}
@Override
public void setSelection(int selection) {
this.selection = selection;
}
}
}

View File

@@ -1,445 +1,443 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.calculator.model;
import jscl.AngleUnit;
import jscl.JsclMathEngine;
import jscl.NumeralBase;
import jscl.math.Expression;
import jscl.math.Generic;
import jscl.math.function.Constant;
import jscl.math.function.CustomFunction;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.solovyev.android.calculator.CalculatorEvalException;
import org.solovyev.android.calculator.CalculatorParseException;
import org.solovyev.android.calculator.jscl.JsclOperation;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
import static junit.framework.Assert.fail;
/**
* User: serso
* Date: 9/17/11
* Time: 9:47 PM
*/
public class CalculatorEngineTest {
@BeforeClass
public static void setUp() throws Exception {
CalculatorEngine.instance.init(null, null);
CalculatorEngine.instance.setPrecision(3);
CalculatorEngine.instance.setThreadKiller(new CalculatorEngine.ThreadKillerImpl());
}
@Test
public void testDegrees() throws Exception {
final CalculatorEngine cm = CalculatorEngine.instance;
final AngleUnit defaultAngleUnit = cm.getEngine().getAngleUnits();
try {
cm.getEngine().setAngleUnits(AngleUnit.rad);
cm.setPrecision(3);
try {
Assert.assertEquals("0.017", cm.evaluate(JsclOperation.numeric, "°"));
fail();
} catch (CalculatorParseException e) {
}
Assert.assertEquals("0.017", cm.evaluate(JsclOperation.numeric, "").getStringResult());
Assert.assertEquals("0.349", cm.evaluate(JsclOperation.numeric, "20.0°").getStringResult());
Assert.assertEquals("0.5", cm.evaluate(JsclOperation.numeric, "sin(30°)").getStringResult());
Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "asin(sin(30°))").getStringResult());
Assert.assertEquals("∂(cos(t), t, t, 1°)", cm.evaluate(JsclOperation.numeric, "∂(cos(t),t,t,1°)").getStringResult());
Assert.assertEquals("∂(cos(t), t, t, 1°)", cm.evaluate(JsclOperation.simplify, "∂(cos(t),t,t,1°)").getStringResult());
} finally {
cm.getEngine().setAngleUnits(defaultAngleUnit);
}
}
@Test
public void testLongExecution() throws Exception {
final CalculatorEngine cm = CalculatorEngine.instance;
try {
cm.evaluate(JsclOperation.numeric, "3^10^10^10");
Assert.fail();
} catch (CalculatorParseException e) {
if (e.getMessageCode().equals(Messages.msg_3)) {
} else {
System.out.print(e.getCause().getMessage());
Assert.fail();
}
}
try {
cm.evaluate(JsclOperation.numeric, "9999999!");
Assert.fail();
} catch (CalculatorParseException e) {
if (e.getMessageCode().equals(Messages.msg_3)) {
} else {
System.out.print(e.getCause().getMessage());
Assert.fail();
}
}
/*final long start = System.currentTimeMillis();
try {
cm.evaluate(JsclOperation.numeric, "3^10^10^10");
Assert.fail();
} catch (ParseException e) {
if (e.getMessage().startsWith("Too long calculation")) {
final long end = System.currentTimeMillis();
Assert.assertTrue(end - start < 1000);
} else {
Assert.fail();
}
}*/
}
@Test
public void testEvaluate() throws Exception {
final CalculatorEngine cm = CalculatorEngine.instance;
Assert.assertEquals("cos(t)+10%", cm.evaluate(JsclOperation.simplify, "cos(t)+10%").getStringResult());
final Generic expression = cm.getEngine().simplifyGeneric("cos(t)+10%");
expression.substitute(new Constant("t"), Expression.valueOf(100d));
Assert.assertEquals("it", cm.evaluate(JsclOperation.simplify, "it").getStringResult());
Assert.assertEquals("10%", cm.evaluate(JsclOperation.simplify, "10%").getStringResult());
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(0, 1)").getStringResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq(1, 1)").getStringResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq( 1, 1)").getStringResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.simplify, "eq( 1, 1)").getStringResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "lg(10)").getStringResult());
Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "2+2").getStringResult());
final AngleUnit defaultAngleUnit = cm.getEngine().getAngleUnits();
try {
cm.getEngine().setAngleUnits(AngleUnit.rad);
Assert.assertEquals("-0.757", cm.evaluate(JsclOperation.numeric, "sin(4)").getStringResult());
Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "asin(0.5)").getStringResult());
Assert.assertEquals("-0.396", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)").getStringResult());
Assert.assertEquals("-0.56", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)√(2)").getStringResult());
Assert.assertEquals("-0.56", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)√(2)").getStringResult());
} finally {
cm.getEngine().setAngleUnits(defaultAngleUnit);
}
Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "e^2").getStringResult());
Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "exp(1)^2").getStringResult());
Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "exp(2)").getStringResult());
Assert.assertEquals("2+i", cm.evaluate(JsclOperation.numeric, "2*1+√(-1)").getStringResult());
try {
cm.getEngine().setAngleUnits(AngleUnit.rad);
Assert.assertEquals("0.921+Πi", cm.evaluate(JsclOperation.numeric, "ln(5cosh(38π√(2cos(2))))").getStringResult());
Assert.assertEquals("-3.41+3.41i", cm.evaluate(JsclOperation.numeric, "(5tan(2i)+2i)/(1-i)").getStringResult());
} finally {
cm.getEngine().setAngleUnits(defaultAngleUnit);
}
Assert.assertEquals("7.389i", cm.evaluate(JsclOperation.numeric, "iexp(2)").getStringResult());
Assert.assertEquals("2+7.389i", cm.evaluate(JsclOperation.numeric, "2+iexp(2)").getStringResult());
Assert.assertEquals("2+7.389i", cm.evaluate(JsclOperation.numeric, "2+√(-1)exp(2)").getStringResult());
Assert.assertEquals("2-2.5i", cm.evaluate(JsclOperation.numeric, "2-2.5i").getStringResult());
Assert.assertEquals("-2-2.5i", cm.evaluate(JsclOperation.numeric, "-2-2.5i").getStringResult());
Assert.assertEquals("-2+2.5i", cm.evaluate(JsclOperation.numeric, "-2+2.5i").getStringResult());
Assert.assertEquals("-2+2.1i", cm.evaluate(JsclOperation.numeric, "-2+2.1i").getStringResult());
Assert.assertEquals("-0.1-0.2i", cm.evaluate(JsclOperation.numeric, "(1-i)/(2+6i)").getStringResult());
junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "4!").getStringResult());
junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "(2+2)!").getStringResult());
junit.framework.Assert.assertEquals("120", cm.evaluate(JsclOperation.numeric, "(2+2+1)!").getStringResult());
junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "(2.0+2.0)!").getStringResult());
junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "4.0!").getStringResult());
junit.framework.Assert.assertEquals("720", cm.evaluate(JsclOperation.numeric, "(3!)!").getStringResult());
junit.framework.Assert.assertEquals("36", Expression.valueOf("3!^2").numeric().toString());
junit.framework.Assert.assertEquals("3", Expression.valueOf("cubic(27)").numeric().toString());
try {
junit.framework.Assert.assertEquals("√(-1)!", cm.evaluate(JsclOperation.numeric, "i!").getStringResult());
fail();
} catch (CalculatorParseException e) {
}
junit.framework.Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "(π/π)!").getStringResult());
try {
junit.framework.Assert.assertEquals("i", cm.evaluate(JsclOperation.numeric, "(-1)i!").getStringResult());
fail();
} catch (CalculatorParseException e) {
}
junit.framework.Assert.assertEquals("24i", cm.evaluate(JsclOperation.numeric, "4!i").getStringResult());
CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("si", 5d));
try {
cm.getEngine().setAngleUnits(AngleUnit.rad);
Assert.assertEquals("0.451", cm.evaluate(JsclOperation.numeric, "acos(0.8999999999999811)").getStringResult());
Assert.assertEquals("-0.959", cm.evaluate(JsclOperation.numeric, "sin(5)").getStringResult());
Assert.assertEquals("-4.795", cm.evaluate(JsclOperation.numeric, "sin(5)si").getStringResult());
Assert.assertEquals("-23.973", cm.evaluate(JsclOperation.numeric, "sisin(5)si").getStringResult());
Assert.assertEquals("-23.973", cm.evaluate(JsclOperation.numeric, "si*sin(5)si").getStringResult());
Assert.assertEquals("-3.309", cm.evaluate(JsclOperation.numeric, "sisin(5si)si").getStringResult());
} finally {
cm.getEngine().setAngleUnits(defaultAngleUnit);
}
CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("s", 1d));
Assert.assertEquals("5", cm.evaluate(JsclOperation.numeric, "si").getStringResult());
CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("k", 3.5d));
CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("k1", 4d));
Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "k11").getStringResult());
CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t", (String) null));
Assert.assertEquals("11t", cm.evaluate(JsclOperation.numeric, "t11").getStringResult());
Assert.assertEquals("11et", cm.evaluate(JsclOperation.numeric, "t11e").getStringResult());
Assert.assertEquals("", cm.evaluate(JsclOperation.numeric, "").getStringResult());
Assert.assertEquals("", cm.evaluate(JsclOperation.numeric, "Infinity").getStringResult());
Assert.assertEquals("11∞t", cm.evaluate(JsclOperation.numeric, "t11∞").getStringResult());
Assert.assertEquals("-t+t^3", cm.evaluate(JsclOperation.numeric, "t(t-1)(t+1)").getStringResult());
Assert.assertEquals("100", cm.evaluate(JsclOperation.numeric, "0.1E3").getStringResult());
Assert.assertEquals("3.957", cm.evaluate(JsclOperation.numeric, "ln(8)lg(8)+ln(8)").getStringResult());
Assert.assertEquals("0.933", cm.evaluate(JsclOperation.numeric, "0x:E/0x:F").getStringResult());
try {
cm.getEngine().setNumeralBase(NumeralBase.hex);
Assert.assertEquals("E/F", cm.evaluate(JsclOperation.numeric, "0x:E/0x:F").getStringResult());
Assert.assertEquals("E/F", cm.evaluate(JsclOperation.simplify, "0x:E/0x:F").getStringResult());
Assert.assertEquals("E/F", cm.evaluate(JsclOperation.numeric, "E/F").getStringResult());
Assert.assertEquals("E/F", cm.evaluate(JsclOperation.simplify, "E/F").getStringResult());
} finally {
cm.getEngine().setNumeralBase(NumeralBase.dec);
}
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "((((((0))))))").getStringResult());
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((0))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))").getStringResult());
/* Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "30°").getResult());
Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "(10+20)°").getResult());
Assert.assertEquals("1.047", cm.evaluate(JsclOperation.numeric, "(10+20)°*2").getResult());
try {
Assert.assertEquals("0.278", cm.evaluate(JsclOperation.numeric, "30°^2").getResult());
junit.framework.Assert.fail();
} catch (ParseException e) {
if ( !e.getMessage().equals("Power operation after postfix function is currently unsupported!") ) {
junit.framework.Assert.fail();
}
}*/
/* try {
cm.setTimeout(5000);
Assert.assertEquals("2", cm.evaluate(JsclOperation.numeric, "2!").getResult());
} finally {
cm.setTimeout(3000);
}*/
CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t", (String) null));
Assert.assertEquals("2t", cm.evaluate(JsclOperation.simplify, "∂(t^2,t)").getStringResult());
Assert.assertEquals("2t", cm.evaluate(JsclOperation.numeric, "∂(t^2,t)").getStringResult());
CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t", "2"));
Assert.assertEquals("2t", cm.evaluate(JsclOperation.simplify, "∂(t^2,t)").getStringResult());
Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "∂(t^2,t)").getStringResult());
Assert.assertEquals("-x+x*ln(x)", cm.getEngine().simplify("∫(ln(x), x)"));
Assert.assertEquals("-(x-x*ln(x))/(ln(2)+ln(5))", cm.getEngine().simplify("∫(log(10, x), x)"));
Assert.assertEquals("∫((ln(2)+ln(5))/ln(x), x)", cm.getEngine().simplify("∫(ln(10)/ln(x), x)"));
Assert.assertEquals("∫(ln(10)/ln(x), x)", Expression.valueOf("∫(log(x, 10), x)").expand().toString());
Assert.assertEquals("∫((ln(2)+ln(5))/ln(x), x)", cm.getEngine().simplify("∫(log(x, 10), x)"));
}
@Test
public void testFormatting() throws Exception {
final CalculatorEngine ce = CalculatorEngine.instance;
Assert.assertEquals("12 345", ce.evaluate(JsclOperation.simplify, "12345").getStringResult());
}
@Test
public void testI() throws CalculatorParseException, CalculatorEvalException {
final CalculatorEngine cm = CalculatorEngine.instance;
Assert.assertEquals("-i", cm.evaluate(JsclOperation.numeric, "i^3").getStringResult());
for (int i = 0; i < 1000; i++) {
double real = (Math.random()-0.5) * 1000;
double imag = (Math.random()-0.5) * 1000;
int exp = (int)(Math.random() * 10);
final StringBuilder sb = new StringBuilder();
sb.append(real);
if ( imag > 0 ) {
sb.append("+");
}
sb.append(imag);
sb.append("^").append(exp);
try {
cm.evaluate(JsclOperation.numeric, sb.toString()).getStringResult();
} catch (Throwable e) {
fail(sb.toString());
}
}
}
@Test
public void testEmptyFunction() throws Exception {
final CalculatorEngine cm = CalculatorEngine.instance;
try {
cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(acos(acos(acos(acos(acos(acos(acos(acos(cos(cos(cos(cos(cosh(acos(cos(cos(cos(cos(cos(acos(acos(acos(acos(acos(acos(acos(acos(cos(cos(cos(cos(cosh(acos(cos())))))))))))))))))))))))))))))))))))))");
Assert.fail();
} catch (CalculatorParseException e) {
}
Assert.assertEquals("0.34+1.382i", cm.evaluate(JsclOperation.numeric, "ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(100)))))))))))))))").getStringResult());
try {
cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos())))))))))))))))))))))))))))))))))))");
Assert.fail();
} catch (CalculatorParseException e) {
}
final AngleUnit defaultAngleUnit = cm.getEngine().getAngleUnits();
try {
cm.getEngine().setAngleUnits(AngleUnit.rad);
Assert.assertEquals("0.739", cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(1))))))))))))))))))))))))))))))))))))").getStringResult());
} finally {
cm.getEngine().setAngleUnits(defaultAngleUnit);
}
CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("si", 5d));
Assert.assertEquals("5", cm.evaluate(JsclOperation.numeric, "si").getStringResult());
try {
cm.evaluate(JsclOperation.numeric, "sin");
Assert.fail();
} catch (CalculatorParseException e) {
}
}
@Test
public void testRounding() throws Exception {
final CalculatorEngine cm = CalculatorEngine.instance;
try {
DecimalFormatSymbols decimalGroupSymbols = new DecimalFormatSymbols(Locale.getDefault());
decimalGroupSymbols.setDecimalSeparator('.');
decimalGroupSymbols.setGroupingSeparator('\'');
cm.setDecimalGroupSymbols(decimalGroupSymbols);
cm.setPrecision(2);
Assert.assertEquals("12'345'678.9", cm.evaluate(JsclOperation.numeric, "1.23456789E7").getStringResult());
cm.setPrecision(10);
Assert.assertEquals("12'345'678.9", cm.evaluate(JsclOperation.numeric, "1.23456789E7").getStringResult());
Assert.assertEquals("123'456'789", cm.evaluate(JsclOperation.numeric, "1.234567890E8").getStringResult());
Assert.assertEquals("1'234'567'890.1", cm.evaluate(JsclOperation.numeric, "1.2345678901E9").getStringResult());
} finally {
cm.setPrecision(3);
DecimalFormatSymbols decimalGroupSymbols = new DecimalFormatSymbols(Locale.getDefault());
decimalGroupSymbols.setDecimalSeparator('.');
decimalGroupSymbols.setGroupingSeparator(JsclMathEngine.GROUPING_SEPARATOR_DEFAULT.charAt(0));
cm.setDecimalGroupSymbols(decimalGroupSymbols);
}
}
@Test
public void testComparisonFunction() throws Exception {
final CalculatorEngine cm = CalculatorEngine.instance;
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(0, 1)").getStringResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq(1, 1)").getStringResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq(1, 1.0)").getStringResult());
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(1, 1.000000000000001)").getStringResult());
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(1, 0)").getStringResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "lt(0, 1)").getStringResult());
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "lt(1, 1)").getStringResult());
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "lt(1, 0)").getStringResult());
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "gt(0, 1)").getStringResult());
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "gt(1, 1)").getStringResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "gt(1, 0)").getStringResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ne(0, 1)").getStringResult());
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ne(1, 1)").getStringResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ne(1, 0)").getStringResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "le(0, 1)").getStringResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "le(1, 1)").getStringResult());
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "le(1, 0)").getStringResult());
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ge(0, 1)").getStringResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ge(1, 1)").getStringResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ge(1, 0)").getStringResult());
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ap(0, 1)").getStringResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ap(1, 1)").getStringResult());
//Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ap(1, 1.000000000000001)").getResult());
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ap(1, 0)").getStringResult());
}
@Test
public void testNumeralSystems() throws Exception {
final CalculatorEngine cm = CalculatorEngine.instance;
Assert.assertEquals("11 259 375", cm.evaluate(JsclOperation.numeric, "0x:ABCDEF").getStringResult());
Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "0x:ABCDEF*e").getStringResult());
Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "e*0x:ABCDEF").getStringResult());
Assert.assertEquals("e", cm.evaluate(JsclOperation.numeric, "e*0x:ABCDEF/0x:ABCDEF").getStringResult());
Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "0x:ABCDEF*e*0x:ABCDEF/0x:ABCDEF").getStringResult());
Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "c+0x:ABCDEF*e*0x:ABCDEF/0x:ABCDEF-c+0x:C-0x:C").getStringResult());
Assert.assertEquals("1 446 257 064 651.832", cm.evaluate(JsclOperation.numeric, "28*28 * sin(28) - 0b:1101 + √(28) + exp ( 28) ").getStringResult());
Assert.assertEquals("13", cm.evaluate(JsclOperation.numeric, "0b:1101").getStringResult());
try {
cm.evaluate(JsclOperation.numeric, "0b:π").getStringResult();
Assert.fail();
} catch (CalculatorParseException e) {
// ok
}
final NumeralBase defaultNumeralBase = cm.getEngine().getNumeralBase();
try{
cm.getEngine().setNumeralBase(NumeralBase.bin);
Assert.assertEquals("101", cm.evaluate(JsclOperation.numeric, "10+11").getStringResult());
Assert.assertEquals("10/11", cm.evaluate(JsclOperation.numeric, "10/11").getStringResult());
cm.getEngine().setNumeralBase(NumeralBase.hex);
Assert.assertEquals("63 7B", cm.evaluate(JsclOperation.numeric, "56CE+CAD").getStringResult());
Assert.assertEquals("E", cm.evaluate(JsclOperation.numeric, "E").getStringResult());
} finally {
cm.setNumeralBase(defaultNumeralBase);
}
}
@Test
public void testLog() throws Exception {
final CalculatorEngine cm = CalculatorEngine.instance;
Assert.assertEquals("", Expression.valueOf("1/0").numeric().toString());
Assert.assertEquals("", Expression.valueOf("ln(10)/ln(1)").numeric().toString());
// logarithm
Assert.assertEquals("ln(x)/ln(base)", ((CustomFunction) cm.getFunctionsRegistry().get("log")).getContent());
Assert.assertEquals("", cm.evaluate(JsclOperation.numeric, "log(1, 10)").getStringResult());
Assert.assertEquals("3.322", cm.evaluate(JsclOperation.numeric, "log(2, 10)").getStringResult());
Assert.assertEquals("1.431", cm.evaluate(JsclOperation.numeric, "log(5, 10)").getStringResult());
Assert.assertEquals("0.96", cm.evaluate(JsclOperation.numeric, "log(11, 10)").getStringResult());
Assert.assertEquals("1/(bln(a))", cm.evaluate(JsclOperation.simplify, "∂(log(a, b), b)").getStringResult());
Assert.assertEquals("-ln(b)/(aln(a)^2)", cm.evaluate(JsclOperation.simplify, "∂(log(a, b), a)").getStringResult());
}
}
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.calculator.model;
import jscl.AngleUnit;
import jscl.JsclMathEngine;
import jscl.NumeralBase;
import jscl.math.Expression;
import jscl.math.Generic;
import jscl.math.function.Constant;
import jscl.math.function.CustomFunction;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.solovyev.android.calculator.jscl.JsclOperation;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
import static junit.framework.Assert.fail;
/**
* User: serso
* Date: 9/17/11
* Time: 9:47 PM
*/
public class CalculatorEngineTest {
@BeforeClass
public static void setUp() throws Exception {
CalculatorEngine.instance.init(null, null);
CalculatorEngine.instance.setPrecision(3);
CalculatorEngine.instance.setThreadKiller(new CalculatorEngine.ThreadKillerImpl());
}
@Test
public void testDegrees() throws Exception {
final CalculatorEngine cm = CalculatorEngine.instance;
final AngleUnit defaultAngleUnit = cm.getEngine().getAngleUnits();
try {
cm.getEngine().setAngleUnits(AngleUnit.rad);
cm.setPrecision(3);
try {
Assert.assertEquals("0.017", cm.evaluate(JsclOperation.numeric, "°"));
fail();
} catch (CalculatorParseException e) {
}
Assert.assertEquals("0.017", cm.evaluate(JsclOperation.numeric, "").getResult());
Assert.assertEquals("0.349", cm.evaluate(JsclOperation.numeric, "20.0°").getResult());
Assert.assertEquals("0.5", cm.evaluate(JsclOperation.numeric, "sin(30°)").getResult());
Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "asin(sin(30°))").getResult());
Assert.assertEquals("∂(cos(t), t, t, 1°)", cm.evaluate(JsclOperation.numeric, "∂(cos(t),t,t,1°)").getResult());
Assert.assertEquals("∂(cos(t), t, t, 1°)", cm.evaluate(JsclOperation.simplify, "∂(cos(t),t,t,1°)").getResult());
} finally {
cm.getEngine().setAngleUnits(defaultAngleUnit);
}
}
@Test
public void testLongExecution() throws Exception {
final CalculatorEngine cm = CalculatorEngine.instance;
try {
cm.evaluate(JsclOperation.numeric, "3^10^10^10");
Assert.fail();
} catch (CalculatorParseException e) {
if (e.getMessageCode().equals(Messages.msg_3)) {
} else {
System.out.print(e.getCause().getMessage());
Assert.fail();
}
}
try {
cm.evaluate(JsclOperation.numeric, "9999999!");
Assert.fail();
} catch (CalculatorParseException e) {
if (e.getMessageCode().equals(Messages.msg_3)) {
} else {
System.out.print(e.getCause().getMessage());
Assert.fail();
}
}
/*final long start = System.currentTimeMillis();
try {
cm.evaluate(JsclOperation.numeric, "3^10^10^10");
Assert.fail();
} catch (ParseException e) {
if (e.getMessage().startsWith("Too long calculation")) {
final long end = System.currentTimeMillis();
Assert.assertTrue(end - start < 1000);
} else {
Assert.fail();
}
}*/
}
@Test
public void testEvaluate() throws Exception {
final CalculatorEngine cm = CalculatorEngine.instance;
Assert.assertEquals("cos(t)+10%", cm.evaluate(JsclOperation.simplify, "cos(t)+10%").getResult());
final Generic expression = cm.getEngine().simplifyGeneric("cos(t)+10%");
expression.substitute(new Constant("t"), Expression.valueOf(100d));
Assert.assertEquals("it", cm.evaluate(JsclOperation.simplify, "it").getResult());
Assert.assertEquals("10%", cm.evaluate(JsclOperation.simplify, "10%").getResult());
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(0, 1)").getResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq(1, 1)").getResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq( 1, 1)").getResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.simplify, "eq( 1, 1)").getResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "lg(10)").getResult());
Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "2+2").getResult());
final AngleUnit defaultAngleUnit = cm.getEngine().getAngleUnits();
try {
cm.getEngine().setAngleUnits(AngleUnit.rad);
Assert.assertEquals("-0.757", cm.evaluate(JsclOperation.numeric, "sin(4)").getResult());
Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "asin(0.5)").getResult());
Assert.assertEquals("-0.396", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)").getResult());
Assert.assertEquals("-0.56", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)√(2)").getResult());
Assert.assertEquals("-0.56", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)√(2)").getResult());
} finally {
cm.getEngine().setAngleUnits(defaultAngleUnit);
}
Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "e^2").getResult());
Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "exp(1)^2").getResult());
Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "exp(2)").getResult());
Assert.assertEquals("2+i", cm.evaluate(JsclOperation.numeric, "2*1+√(-1)").getResult());
try {
cm.getEngine().setAngleUnits(AngleUnit.rad);
Assert.assertEquals("0.921+Πi", cm.evaluate(JsclOperation.numeric, "ln(5cosh(38π√(2cos(2))))").getResult());
Assert.assertEquals("-3.41+3.41i", cm.evaluate(JsclOperation.numeric, "(5tan(2i)+2i)/(1-i)").getResult());
} finally {
cm.getEngine().setAngleUnits(defaultAngleUnit);
}
Assert.assertEquals("7.389i", cm.evaluate(JsclOperation.numeric, "iexp(2)").getResult());
Assert.assertEquals("2+7.389i", cm.evaluate(JsclOperation.numeric, "2+iexp(2)").getResult());
Assert.assertEquals("2+7.389i", cm.evaluate(JsclOperation.numeric, "2+√(-1)exp(2)").getResult());
Assert.assertEquals("2-2.5i", cm.evaluate(JsclOperation.numeric, "2-2.5i").getResult());
Assert.assertEquals("-2-2.5i", cm.evaluate(JsclOperation.numeric, "-2-2.5i").getResult());
Assert.assertEquals("-2+2.5i", cm.evaluate(JsclOperation.numeric, "-2+2.5i").getResult());
Assert.assertEquals("-2+2.1i", cm.evaluate(JsclOperation.numeric, "-2+2.1i").getResult());
Assert.assertEquals("-0.1-0.2i", cm.evaluate(JsclOperation.numeric, "(1-i)/(2+6i)").getResult());
junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "4!").getResult());
junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "(2+2)!").getResult());
junit.framework.Assert.assertEquals("120", cm.evaluate(JsclOperation.numeric, "(2+2+1)!").getResult());
junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "(2.0+2.0)!").getResult());
junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "4.0!").getResult());
junit.framework.Assert.assertEquals("720", cm.evaluate(JsclOperation.numeric, "(3!)!").getResult());
junit.framework.Assert.assertEquals("36", Expression.valueOf("3!^2").numeric().toString());
junit.framework.Assert.assertEquals("3", Expression.valueOf("cubic(27)").numeric().toString());
try {
junit.framework.Assert.assertEquals("√(-1)!", cm.evaluate(JsclOperation.numeric, "i!").getResult());
fail();
} catch (CalculatorParseException e) {
}
junit.framework.Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "(π/π)!").getResult());
try {
junit.framework.Assert.assertEquals("i", cm.evaluate(JsclOperation.numeric, "(-1)i!").getResult());
fail();
} catch (CalculatorParseException e) {
}
junit.framework.Assert.assertEquals("24i", cm.evaluate(JsclOperation.numeric, "4!i").getResult());
CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("si", 5d));
try {
cm.getEngine().setAngleUnits(AngleUnit.rad);
Assert.assertEquals("0.451", cm.evaluate(JsclOperation.numeric, "acos(0.8999999999999811)").getResult());
Assert.assertEquals("-0.959", cm.evaluate(JsclOperation.numeric, "sin(5)").getResult());
Assert.assertEquals("-4.795", cm.evaluate(JsclOperation.numeric, "sin(5)si").getResult());
Assert.assertEquals("-23.973", cm.evaluate(JsclOperation.numeric, "sisin(5)si").getResult());
Assert.assertEquals("-23.973", cm.evaluate(JsclOperation.numeric, "si*sin(5)si").getResult());
Assert.assertEquals("-3.309", cm.evaluate(JsclOperation.numeric, "sisin(5si)si").getResult());
} finally {
cm.getEngine().setAngleUnits(defaultAngleUnit);
}
CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("s", 1d));
Assert.assertEquals("5", cm.evaluate(JsclOperation.numeric, "si").getResult());
CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("k", 3.5d));
CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("k1", 4d));
Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "k11").getResult());
CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t", (String) null));
Assert.assertEquals("11t", cm.evaluate(JsclOperation.numeric, "t11").getResult());
Assert.assertEquals("11et", cm.evaluate(JsclOperation.numeric, "t11e").getResult());
Assert.assertEquals("", cm.evaluate(JsclOperation.numeric, "").getResult());
Assert.assertEquals("", cm.evaluate(JsclOperation.numeric, "Infinity").getResult());
Assert.assertEquals("11∞t", cm.evaluate(JsclOperation.numeric, "t11").getResult());
Assert.assertEquals("-t+t^3", cm.evaluate(JsclOperation.numeric, "t(t-1)(t+1)").getResult());
Assert.assertEquals("100", cm.evaluate(JsclOperation.numeric, "0.1E3").getResult());
Assert.assertEquals("3.957", cm.evaluate(JsclOperation.numeric, "ln(8)lg(8)+ln(8)").getResult());
Assert.assertEquals("0.933", cm.evaluate(JsclOperation.numeric, "0x:E/0x:F").getResult());
try {
cm.getEngine().setNumeralBase(NumeralBase.hex);
Assert.assertEquals("E/F", cm.evaluate(JsclOperation.numeric, "0x:E/0x:F").getResult());
Assert.assertEquals("E/F", cm.evaluate(JsclOperation.simplify, "0x:E/0x:F").getResult());
Assert.assertEquals("E/F", cm.evaluate(JsclOperation.numeric, "E/F").getResult());
Assert.assertEquals("E/F", cm.evaluate(JsclOperation.simplify, "E/F").getResult());
} finally {
cm.getEngine().setNumeralBase(NumeralBase.dec);
}
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "((((((0))))))").getResult());
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((0))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))").getResult());
/* Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "30°").getResult());
Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "(10+20)°").getResult());
Assert.assertEquals("1.047", cm.evaluate(JsclOperation.numeric, "(10+20)°*2").getResult());
try {
Assert.assertEquals("0.278", cm.evaluate(JsclOperation.numeric, "30°^2").getResult());
junit.framework.Assert.fail();
} catch (ParseException e) {
if ( !e.getMessage().equals("Power operation after postfix function is currently unsupported!") ) {
junit.framework.Assert.fail();
}
}*/
/* try {
cm.setTimeout(5000);
Assert.assertEquals("2", cm.evaluate(JsclOperation.numeric, "2!").getResult());
} finally {
cm.setTimeout(3000);
}*/
CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t", (String) null));
Assert.assertEquals("2t", cm.evaluate(JsclOperation.simplify, "∂(t^2,t)").getResult());
Assert.assertEquals("2t", cm.evaluate(JsclOperation.numeric, "∂(t^2,t)").getResult());
CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t", "2"));
Assert.assertEquals("2t", cm.evaluate(JsclOperation.simplify, "∂(t^2,t)").getResult());
Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "∂(t^2,t)").getResult());
Assert.assertEquals("-x+x*ln(x)", cm.getEngine().simplify("∫(ln(x), x)"));
Assert.assertEquals("-(x-x*ln(x))/(ln(2)+ln(5))", cm.getEngine().simplify("∫(log(10, x), x)"));
Assert.assertEquals("∫((ln(2)+ln(5))/ln(x), x)", cm.getEngine().simplify("∫(ln(10)/ln(x), x)"));
Assert.assertEquals("∫(ln(10)/ln(x), x)", Expression.valueOf("∫(log(x, 10), x)").expand().toString());
Assert.assertEquals("∫((ln(2)+ln(5))/ln(x), x)", cm.getEngine().simplify("∫(log(x, 10), x)"));
}
@Test
public void testFormatting() throws Exception {
final CalculatorEngine ce = CalculatorEngine.instance;
Assert.assertEquals("12 345", ce.evaluate(JsclOperation.simplify, "12345").getResult());
}
@Test
public void testI() throws CalculatorParseException, CalculatorEvalException {
final CalculatorEngine cm = CalculatorEngine.instance;
Assert.assertEquals("-i", cm.evaluate(JsclOperation.numeric, "i^3").getResult());
for (int i = 0; i < 1000; i++) {
double real = (Math.random()-0.5) * 1000;
double imag = (Math.random()-0.5) * 1000;
int exp = (int)(Math.random() * 10);
final StringBuilder sb = new StringBuilder();
sb.append(real);
if ( imag > 0 ) {
sb.append("+");
}
sb.append(imag);
sb.append("^").append(exp);
try {
cm.evaluate(JsclOperation.numeric, sb.toString()).getResult();
} catch (Throwable e) {
fail(sb.toString());
}
}
}
@Test
public void testEmptyFunction() throws Exception {
final CalculatorEngine cm = CalculatorEngine.instance;
try {
cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(acos(acos(acos(acos(acos(acos(acos(acos(cos(cos(cos(cos(cosh(acos(cos(cos(cos(cos(cos(acos(acos(acos(acos(acos(acos(acos(acos(cos(cos(cos(cos(cosh(acos(cos())))))))))))))))))))))))))))))))))))))");
Assert.fail();
} catch (CalculatorParseException e) {
}
Assert.assertEquals("0.34+1.382i", cm.evaluate(JsclOperation.numeric, "ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(100)))))))))))))))").getResult());
try {
cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos())))))))))))))))))))))))))))))))))))");
Assert.fail();
} catch (CalculatorParseException e) {
}
final AngleUnit defaultAngleUnit = cm.getEngine().getAngleUnits();
try {
cm.getEngine().setAngleUnits(AngleUnit.rad);
Assert.assertEquals("0.739", cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(1))))))))))))))))))))))))))))))))))))").getResult());
} finally {
cm.getEngine().setAngleUnits(defaultAngleUnit);
}
CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("si", 5d));
Assert.assertEquals("5", cm.evaluate(JsclOperation.numeric, "si").getResult());
try {
cm.evaluate(JsclOperation.numeric, "sin");
Assert.fail();
} catch (CalculatorParseException e) {
}
}
@Test
public void testRounding() throws Exception {
final CalculatorEngine cm = CalculatorEngine.instance;
try {
DecimalFormatSymbols decimalGroupSymbols = new DecimalFormatSymbols(Locale.getDefault());
decimalGroupSymbols.setDecimalSeparator('.');
decimalGroupSymbols.setGroupingSeparator('\'');
cm.setDecimalGroupSymbols(decimalGroupSymbols);
cm.setPrecision(2);
Assert.assertEquals("12'345'678.9", cm.evaluate(JsclOperation.numeric, "1.23456789E7").getResult());
cm.setPrecision(10);
Assert.assertEquals("12'345'678.9", cm.evaluate(JsclOperation.numeric, "1.23456789E7").getResult());
Assert.assertEquals("123'456'789", cm.evaluate(JsclOperation.numeric, "1.234567890E8").getResult());
Assert.assertEquals("1'234'567'890.1", cm.evaluate(JsclOperation.numeric, "1.2345678901E9").getResult());
} finally {
cm.setPrecision(3);
DecimalFormatSymbols decimalGroupSymbols = new DecimalFormatSymbols(Locale.getDefault());
decimalGroupSymbols.setDecimalSeparator('.');
decimalGroupSymbols.setGroupingSeparator(JsclMathEngine.GROUPING_SEPARATOR_DEFAULT.charAt(0));
cm.setDecimalGroupSymbols(decimalGroupSymbols);
}
}
@Test
public void testComparisonFunction() throws Exception {
final CalculatorEngine cm = CalculatorEngine.instance;
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(0, 1)").getResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq(1, 1)").getResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq(1, 1.0)").getResult());
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(1, 1.000000000000001)").getResult());
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(1, 0)").getResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "lt(0, 1)").getResult());
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "lt(1, 1)").getResult());
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "lt(1, 0)").getResult());
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "gt(0, 1)").getResult());
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "gt(1, 1)").getResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "gt(1, 0)").getResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ne(0, 1)").getResult());
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ne(1, 1)").getResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ne(1, 0)").getResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "le(0, 1)").getResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "le(1, 1)").getResult());
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "le(1, 0)").getResult());
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ge(0, 1)").getResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ge(1, 1)").getResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ge(1, 0)").getResult());
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ap(0, 1)").getResult());
Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ap(1, 1)").getResult());
//Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ap(1, 1.000000000000001)").getResult());
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ap(1, 0)").getResult());
}
@Test
public void testNumeralSystems() throws Exception {
final CalculatorEngine cm = CalculatorEngine.instance;
Assert.assertEquals("11 259 375", cm.evaluate(JsclOperation.numeric, "0x:ABCDEF").getResult());
Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "0x:ABCDEF*e").getResult());
Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "e*0x:ABCDEF").getResult());
Assert.assertEquals("e", cm.evaluate(JsclOperation.numeric, "e*0x:ABCDEF/0x:ABCDEF").getResult());
Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "0x:ABCDEF*e*0x:ABCDEF/0x:ABCDEF").getResult());
Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "c+0x:ABCDEF*e*0x:ABCDEF/0x:ABCDEF-c+0x:C-0x:C").getResult());
Assert.assertEquals("1 446 257 064 651.832", cm.evaluate(JsclOperation.numeric, "28*28 * sin(28) - 0b:1101 + √(28) + exp ( 28) ").getResult());
Assert.assertEquals("13", cm.evaluate(JsclOperation.numeric, "0b:1101").getResult());
try {
cm.evaluate(JsclOperation.numeric, "0b:π").getResult();
Assert.fail();
} catch (CalculatorParseException e) {
// ok
}
final NumeralBase defaultNumeralBase = cm.getEngine().getNumeralBase();
try{
cm.getEngine().setNumeralBase(NumeralBase.bin);
Assert.assertEquals("101", cm.evaluate(JsclOperation.numeric, "10+11").getResult());
Assert.assertEquals("10/11", cm.evaluate(JsclOperation.numeric, "10/11").getResult());
cm.getEngine().setNumeralBase(NumeralBase.hex);
Assert.assertEquals("63 7B", cm.evaluate(JsclOperation.numeric, "56CE+CAD").getResult());
Assert.assertEquals("E", cm.evaluate(JsclOperation.numeric, "E").getResult());
} finally {
cm.setNumeralBase(defaultNumeralBase);
}
}
@Test
public void testLog() throws Exception {
final CalculatorEngine cm = CalculatorEngine.instance;
Assert.assertEquals("", Expression.valueOf("1/0").numeric().toString());
Assert.assertEquals("", Expression.valueOf("ln(10)/ln(1)").numeric().toString());
// logarithm
Assert.assertEquals("ln(x)/ln(base)", ((CustomFunction) cm.getFunctionsRegistry().get("log")).getContent());
Assert.assertEquals("", cm.evaluate(JsclOperation.numeric, "log(1, 10)").getResult());
Assert.assertEquals("3.322", cm.evaluate(JsclOperation.numeric, "log(2, 10)").getResult());
Assert.assertEquals("1.431", cm.evaluate(JsclOperation.numeric, "log(5, 10)").getResult());
Assert.assertEquals("0.96", cm.evaluate(JsclOperation.numeric, "log(11, 10)").getResult());
Assert.assertEquals("1/(bln(a))", cm.evaluate(JsclOperation.simplify, "∂(log(a, b), b)").getResult());
Assert.assertEquals("-ln(b)/(aln(a)^2)", cm.evaluate(JsclOperation.simplify, "∂(log(a, b), a)").getResult());
}
}

View File

@@ -3,7 +3,6 @@ package org.solovyev.android.calculator.model;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.solovyev.android.calculator.text.FromJsclSimplifyTextProcessor;
import java.text.DecimalFormatSymbols;

View File

@@ -1,148 +1,146 @@
package org.solovyev.android.calculator.model;
import au.com.bytecode.opencsv.CSVReader;
import jscl.JsclMathEngine;
import jscl.MathEngine;
import jscl.math.Expression;
import jscl.text.ParseException;
import jscl.util.ExpressionGeneratorWithInput;
import org.jetbrains.annotations.NotNull;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.solovyev.android.calculator.CalculatorEvalException;
import org.solovyev.android.calculator.CalculatorParseException;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.common.Converter;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
/**
* User: serso
* Date: 12/14/11
* Time: 4:16 PM
*/
public class NumeralBaseTest {
@BeforeClass
public static void setUp() throws Exception {
CalculatorEngine.instance.init(null, null);
CalculatorEngine.instance.setPrecision(3);
CalculatorEngine.instance.setThreadKiller(new CalculatorEngine.ThreadKillerImpl());
}
@Test
public void testConversion() throws Exception {
CSVReader reader = null;
try {
final MathEngine me = JsclMathEngine.instance;
reader = new CSVReader(new InputStreamReader(NumeralBaseTest.class.getResourceAsStream("/org/solovyev/android/calculator/model/nb_table.csv")), '\t');
// skip first line
reader.readNext();
String[] line = reader.readNext();
for (; line != null; line = reader.readNext()) {
testExpression(line, new DummyExpression());
testExpression(line, new Expression1());
testExpression(line, new Expression2());
testExpression(line, new Expression3());
final String dec = line[0].toUpperCase();
final String hex = "0x:" + line[1].toUpperCase();
final String bin = "0b:" + line[2].toUpperCase();
final List<String> input = new ArrayList<String>();
input.add(dec);
input.add(hex);
input.add(bin);
//System.out.println("Dec: " + dec);
//System.out.println("Hex: " + hex);
//System.out.println("Bin: " + bin);
final ExpressionGeneratorWithInput eg = new ExpressionGeneratorWithInput(input, 20);
final List<String> expressions = eg.generate();
final String decExpression = expressions.get(0);
final String hexExpression = expressions.get(1);
final String binExpression = expressions.get(2);
//System.out.println("Dec expression: " + decExpression);
//System.out.println("Hex expression: " + hexExpression);
//System.out.println("Bin expression: " + binExpression);
final String decResult = Expression.valueOf(decExpression).numeric().toString();
//System.out.println("Dec result: " + decResult);
final String hexResult = Expression.valueOf(hexExpression).numeric().toString();
//System.out.println("Hex result: " + hexResult);
final String binResult = Expression.valueOf(binExpression).numeric().toString();
//System.out.println("Bin result: " + binResult);
Assert.assertEquals("dec-hex: " + decExpression + " : " + hexExpression, decResult, hexResult);
Assert.assertEquals("dec-bin: " + decExpression + " : " + binExpression, decResult, binResult);
}
} finally {
if (reader != null) {
reader.close();
}
}
}
public static void testExpression(@NotNull String[] line, @NotNull Converter<String, String> converter) throws ParseException, CalculatorEvalException, CalculatorParseException {
final String dec = line[0].toUpperCase();
final String hex = "0x:" + line[1].toUpperCase();
final String bin = "0b:" + line[2].toUpperCase();
final String decExpression = converter.convert(dec);
final String decResult = CalculatorEngine.instance.evaluate(JsclOperation.numeric, decExpression).getStringResult();
final String hexExpression = converter.convert(hex);
final String hexResult = CalculatorEngine.instance.evaluate(JsclOperation.numeric, hexExpression).getStringResult();
final String binExpression = converter.convert(bin);
final String binResult = CalculatorEngine.instance.evaluate(JsclOperation.numeric, binExpression).getStringResult();
Assert.assertEquals("dec-hex: " + decExpression + " : " + hexExpression, decResult, hexResult);
Assert.assertEquals("dec-bin: " + decExpression + " : " + binExpression, decResult, binResult);
}
private static class DummyExpression implements Converter<String, String> {
@NotNull
@Override
public String convert(@NotNull String s) {
return s;
}
}
private static class Expression1 implements Converter<String, String> {
@NotNull
@Override
public String convert(@NotNull String s) {
return s + "*" + s;
}
}
private static class Expression2 implements Converter<String, String> {
@NotNull
@Override
public String convert(@NotNull String s) {
return s + "*" + s + " * sin(" + s + ") - 0b:1101";
}
}
private static class Expression3 implements Converter<String, String> {
@NotNull
@Override
public String convert(@NotNull String s) {
return s + "*" + s + " * sin(" + s + ") - 0b:1101 + √(" + s + ") + exp ( " + s + ")";
}
}
}
package org.solovyev.android.calculator.model;
import au.com.bytecode.opencsv.CSVReader;
import jscl.JsclMathEngine;
import jscl.MathEngine;
import jscl.math.Expression;
import jscl.text.ParseException;
import jscl.util.ExpressionGeneratorWithInput;
import org.jetbrains.annotations.NotNull;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.common.Converter;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
/**
* User: serso
* Date: 12/14/11
* Time: 4:16 PM
*/
public class NumeralBaseTest {
@BeforeClass
public static void setUp() throws Exception {
CalculatorEngine.instance.init(null, null);
CalculatorEngine.instance.setPrecision(3);
CalculatorEngine.instance.setThreadKiller(new CalculatorEngine.ThreadKillerImpl());
}
@Test
public void testConversion() throws Exception {
CSVReader reader = null;
try {
final MathEngine me = JsclMathEngine.instance;
reader = new CSVReader(new InputStreamReader(NumeralBaseTest.class.getResourceAsStream("/org/solovyev/android/calculator/model/nb_table.csv")), '\t');
// skip first line
reader.readNext();
String[] line = reader.readNext();
for (; line != null; line = reader.readNext()) {
testExpression(line, new DummyExpression());
testExpression(line, new Expression1());
testExpression(line, new Expression2());
testExpression(line, new Expression3());
final String dec = line[0].toUpperCase();
final String hex = "0x:" + line[1].toUpperCase();
final String bin = "0b:" + line[2].toUpperCase();
final List<String> input = new ArrayList<String>();
input.add(dec);
input.add(hex);
input.add(bin);
//System.out.println("Dec: " + dec);
//System.out.println("Hex: " + hex);
//System.out.println("Bin: " + bin);
final ExpressionGeneratorWithInput eg = new ExpressionGeneratorWithInput(input, 20);
final List<String> expressions = eg.generate();
final String decExpression = expressions.get(0);
final String hexExpression = expressions.get(1);
final String binExpression = expressions.get(2);
//System.out.println("Dec expression: " + decExpression);
//System.out.println("Hex expression: " + hexExpression);
//System.out.println("Bin expression: " + binExpression);
final String decResult = Expression.valueOf(decExpression).numeric().toString();
//System.out.println("Dec result: " + decResult);
final String hexResult = Expression.valueOf(hexExpression).numeric().toString();
//System.out.println("Hex result: " + hexResult);
final String binResult = Expression.valueOf(binExpression).numeric().toString();
//System.out.println("Bin result: " + binResult);
Assert.assertEquals("dec-hex: " + decExpression + " : " + hexExpression, decResult, hexResult);
Assert.assertEquals("dec-bin: " + decExpression + " : " + binExpression, decResult, binResult);
}
} finally {
if (reader != null) {
reader.close();
}
}
}
public static void testExpression(@NotNull String[] line, @NotNull Converter<String, String> converter) throws ParseException, CalculatorEvalException, CalculatorParseException {
final String dec = line[0].toUpperCase();
final String hex = "0x:" + line[1].toUpperCase();
final String bin = "0b:" + line[2].toUpperCase();
final String decExpression = converter.convert(dec);
final String decResult = CalculatorEngine.instance.evaluate(JsclOperation.numeric, decExpression).getResult();
final String hexExpression = converter.convert(hex);
final String hexResult = CalculatorEngine.instance.evaluate(JsclOperation.numeric, hexExpression).getResult();
final String binExpression = converter.convert(bin);
final String binResult = CalculatorEngine.instance.evaluate(JsclOperation.numeric, binExpression).getResult();
Assert.assertEquals("dec-hex: " + decExpression + " : " + hexExpression, decResult, hexResult);
Assert.assertEquals("dec-bin: " + decExpression + " : " + binExpression, decResult, binResult);
}
private static class DummyExpression implements Converter<String, String> {
@NotNull
@Override
public String convert(@NotNull String s) {
return s;
}
}
private static class Expression1 implements Converter<String, String> {
@NotNull
@Override
public String convert(@NotNull String s) {
return s + "*" + s;
}
}
private static class Expression2 implements Converter<String, String> {
@NotNull
@Override
public String convert(@NotNull String s) {
return s + "*" + s + " * sin(" + s + ") - 0b:1101";
}
}
private static class Expression3 implements Converter<String, String> {
@NotNull
@Override
public String convert(@NotNull String s) {
return s + "*" + s + " * sin(" + s + ") - 0b:1101 + √(" + s + ") + exp ( " + s + ")";
}
}
}

View File

@@ -11,10 +11,6 @@ import jscl.NumeralBase;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.solovyev.android.calculator.CalculatorParseException;
import org.solovyev.android.calculator.PreparedExpression;
import org.solovyev.android.calculator.ToJsclTextProcessor;
import org.solovyev.android.calculator.text.TextProcessor;
/**
* User: serso