Calculator display changes
This commit is contained in:
@@ -1,347 +1,316 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
|
||||
* For more information, please, contact se.solovyev@gmail.com
|
||||
*/
|
||||
|
||||
package org.solovyev.android.calculator;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.text.Html;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import jscl.NumeralBase;
|
||||
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.ToJsclTextProcessor;
|
||||
import org.solovyev.android.calculator.view.NumeralBaseConverterDialog;
|
||||
import org.solovyev.android.calculator.view.TextHighlighter;
|
||||
import org.solovyev.android.calculator.view.UnitConverterViewBuilder;
|
||||
import org.solovyev.android.menu.AMenuItem;
|
||||
import org.solovyev.android.menu.LabeledMenuItem;
|
||||
import org.solovyev.android.view.AutoResizeTextView;
|
||||
import org.solovyev.common.collections.CollectionsUtils;
|
||||
import org.solovyev.common.text.StringUtils;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 9/17/11
|
||||
* Time: 10:58 PM
|
||||
*/
|
||||
public class CalculatorDisplay extends AutoResizeTextView implements JCalculatorDisplay {
|
||||
|
||||
private static enum ConversionMenuItem implements AMenuItem<CalculatorDisplay> {
|
||||
convert_to_bin(NumeralBase.bin),
|
||||
convert_to_dec(NumeralBase.dec),
|
||||
convert_to_hex(NumeralBase.hex);
|
||||
|
||||
@NotNull
|
||||
private final NumeralBase toNumeralBase;
|
||||
|
||||
private ConversionMenuItem(@NotNull NumeralBase toNumeralBase) {
|
||||
this.toNumeralBase = toNumeralBase;
|
||||
}
|
||||
|
||||
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
|
||||
boolean result = false;
|
||||
|
||||
if (operation == JsclOperation.numeric) {
|
||||
if (generic.getConstants().isEmpty()) {
|
||||
try {
|
||||
convert(generic);
|
||||
|
||||
// conversion possible => return true
|
||||
result = true;
|
||||
|
||||
} catch (UnitConverterViewBuilder.ConversionException e) {
|
||||
// conversion is not possible => return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(@NotNull CalculatorDisplay data, @NotNull Context context) {
|
||||
final NumeralBase fromNumeralBase = CalculatorEngine.instance.getEngine().getNumeralBase();
|
||||
|
||||
String to;
|
||||
try {
|
||||
to = convert(data.getGenericResult());
|
||||
|
||||
// add prefix
|
||||
if (fromNumeralBase != toNumeralBase) {
|
||||
to = toNumeralBase.getJsclPrefix() + to;
|
||||
}
|
||||
} catch (UnitConverterViewBuilder.ConversionException e) {
|
||||
to = context.getString(R.string.c_error);
|
||||
}
|
||||
|
||||
data.setText(to);
|
||||
data.redraw();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String convert(@NotNull Generic generic) throws UnitConverterViewBuilder.ConversionException {
|
||||
final NumeralBase fromNumeralBase = CalculatorEngine.instance.getEngine().getNumeralBase();
|
||||
|
||||
if (fromNumeralBase != toNumeralBase) {
|
||||
String from = generic.toString();
|
||||
if (!StringUtils.isEmpty(from)) {
|
||||
try {
|
||||
from = ToJsclTextProcessor.getInstance().process(from).getExpression();
|
||||
} catch (CalculatorParseException e) {
|
||||
// ok, problems while processing occurred
|
||||
}
|
||||
}
|
||||
|
||||
return UnitConverterViewBuilder.doConversion(AndroidNumeralBase.getConverter(), from, AndroidNumeralBase.valueOf(fromNumeralBase), AndroidNumeralBase.valueOf(toNumeralBase));
|
||||
} else {
|
||||
return generic.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static enum MenuItem implements LabeledMenuItem<CalculatorDisplay> {
|
||||
|
||||
copy(R.string.c_copy) {
|
||||
@Override
|
||||
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 CalculatorDisplay data, @NotNull Context context) {
|
||||
ConversionMenuItem.convert_to_bin.onClick(data, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
|
||||
return ConversionMenuItem.convert_to_bin.isItemVisibleFor(generic, operation);
|
||||
}
|
||||
},
|
||||
|
||||
convert_to_dec(R.string.convert_to_dec) {
|
||||
@Override
|
||||
public void onClick(@NotNull CalculatorDisplay data, @NotNull Context context) {
|
||||
ConversionMenuItem.convert_to_dec.onClick(data, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
|
||||
return ConversionMenuItem.convert_to_dec.isItemVisibleFor(generic, operation);
|
||||
}
|
||||
},
|
||||
|
||||
convert_to_hex(R.string.convert_to_hex) {
|
||||
@Override
|
||||
public void onClick(@NotNull CalculatorDisplay data, @NotNull Context context) {
|
||||
ConversionMenuItem.convert_to_hex.onClick(data, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
|
||||
return ConversionMenuItem.convert_to_hex.isItemVisibleFor(generic, operation);
|
||||
}
|
||||
},
|
||||
|
||||
convert(R.string.c_convert) {
|
||||
@Override
|
||||
public void onClick(@NotNull CalculatorDisplay data, @NotNull Context context) {
|
||||
new NumeralBaseConverterDialog(data.getGenericResult().toString()).show(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
|
||||
return operation == JsclOperation.numeric && generic.getConstants().isEmpty();
|
||||
}
|
||||
},
|
||||
|
||||
plot(R.string.c_plot) {
|
||||
@Override
|
||||
public void onClick(@NotNull CalculatorDisplay data, @NotNull Context context) {
|
||||
final Generic generic = data.getGenericResult();
|
||||
assert generic != null;
|
||||
|
||||
final Constant constant = CollectionsUtils.getFirstCollectionElement(getNotSystemConstants(generic));
|
||||
assert constant != null;
|
||||
CalculatorActivityLauncher.plotGraph(context, generic, constant);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
|
||||
boolean result = false;
|
||||
|
||||
if (operation == JsclOperation.simplify) {
|
||||
if (getNotSystemConstants(generic).size() == 1) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Set<Constant> getNotSystemConstants(@NotNull Generic generic) {
|
||||
final Set<Constant> notSystemConstants = new HashSet<Constant>();
|
||||
|
||||
for (Constant constant : generic.getConstants()) {
|
||||
IConstant var = CalculatorEngine.instance.getVarsRegistry().get(constant.getName());
|
||||
if (var != null && !var.isSystem() && !var.isDefined()) {
|
||||
notSystemConstants.add(constant);
|
||||
}
|
||||
}
|
||||
|
||||
return notSystemConstants;
|
||||
}
|
||||
};
|
||||
|
||||
private final int captionId;
|
||||
|
||||
MenuItem(int captionId) {
|
||||
this.captionId = captionId;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getCaption(@NotNull Context context) {
|
||||
return context.getString(captionId);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean valid = true;
|
||||
|
||||
@Nullable
|
||||
private String errorMessage;
|
||||
|
||||
@NotNull
|
||||
private JsclOperation jsclOperation = JsclOperation.numeric;
|
||||
|
||||
@NotNull
|
||||
private final static TextProcessor<TextHighlighter.Result, String> textHighlighter = new TextHighlighter(Color.WHITE, false, CalculatorEngine.instance.getEngine());
|
||||
|
||||
@Nullable
|
||||
private Generic genericResult;
|
||||
|
||||
public CalculatorDisplay(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public CalculatorDisplay(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public CalculatorDisplay(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return valid;
|
||||
}
|
||||
|
||||
@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));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getErrorMessage() {
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setErrorMessage(@Nullable String errorMessage) {
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setJsclOperation(@NotNull JsclOperation jsclOperation) {
|
||||
this.jsclOperation = jsclOperation;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public JsclOperation getJsclOperation() {
|
||||
return jsclOperation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setText(CharSequence text, BufferType type) {
|
||||
super.setText(text, type);
|
||||
|
||||
setValid(true);
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
|
||||
* For more information, please, contact se.solovyev@gmail.com
|
||||
*/
|
||||
|
||||
package org.solovyev.android.calculator;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.text.Html;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import jscl.NumeralBase;
|
||||
import jscl.math.Generic;
|
||||
import jscl.math.function.Constant;
|
||||
import jscl.math.function.IConstant;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
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.view.NumeralBaseConverterDialog;
|
||||
import org.solovyev.android.calculator.view.TextHighlighter;
|
||||
import org.solovyev.android.calculator.view.UnitConverterViewBuilder;
|
||||
import org.solovyev.android.menu.AMenuItem;
|
||||
import org.solovyev.android.menu.LabeledMenuItem;
|
||||
import org.solovyev.android.view.AutoResizeTextView;
|
||||
import org.solovyev.common.collections.CollectionsUtils;
|
||||
import org.solovyev.common.text.StringUtils;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 9/17/11
|
||||
* Time: 10:58 PM
|
||||
*/
|
||||
public class AndroidCalculatorDisplayView extends AutoResizeTextView implements CalculatorDisplayView {
|
||||
|
||||
private static enum ConversionMenuItem implements AMenuItem<CalculatorDisplayView> {
|
||||
convert_to_bin(NumeralBase.bin),
|
||||
convert_to_dec(NumeralBase.dec),
|
||||
convert_to_hex(NumeralBase.hex);
|
||||
|
||||
@NotNull
|
||||
private final NumeralBase toNumeralBase;
|
||||
|
||||
private ConversionMenuItem(@NotNull NumeralBase toNumeralBase) {
|
||||
this.toNumeralBase = toNumeralBase;
|
||||
}
|
||||
|
||||
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
|
||||
boolean result = false;
|
||||
|
||||
if (operation == JsclOperation.numeric) {
|
||||
if (generic.getConstants().isEmpty()) {
|
||||
try {
|
||||
convert(generic);
|
||||
|
||||
// conversion possible => return true
|
||||
result = true;
|
||||
|
||||
} catch (UnitConverterViewBuilder.ConversionException e) {
|
||||
// conversion is not possible => return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) {
|
||||
final NumeralBase fromNumeralBase = CalculatorEngine.instance.getEngine().getNumeralBase();
|
||||
|
||||
final Generic lastResult = CalculatorLocatorImpl.getInstance().getCalculatorDisplay().getViewState().getResult();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
data.setText(to);
|
||||
//data.redraw();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String convert(@NotNull Generic generic) throws UnitConverterViewBuilder.ConversionException {
|
||||
final NumeralBase fromNumeralBase = CalculatorEngine.instance.getEngine().getNumeralBase();
|
||||
|
||||
if (fromNumeralBase != toNumeralBase) {
|
||||
String from = generic.toString();
|
||||
if (!StringUtils.isEmpty(from)) {
|
||||
try {
|
||||
from = ToJsclTextProcessor.getInstance().process(from).getExpression();
|
||||
} catch (CalculatorParseException e) {
|
||||
// ok, problems while processing occurred
|
||||
}
|
||||
}
|
||||
|
||||
return UnitConverterViewBuilder.doConversion(AndroidNumeralBase.getConverter(), from, AndroidNumeralBase.valueOf(fromNumeralBase), AndroidNumeralBase.valueOf(toNumeralBase));
|
||||
} else {
|
||||
return generic.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static enum MenuItem implements LabeledMenuItem<CalculatorDisplayView> {
|
||||
|
||||
copy(R.string.c_copy) {
|
||||
@Override
|
||||
public void onClick(@NotNull CalculatorDisplayView 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) {
|
||||
ConversionMenuItem.convert_to_bin.onClick(data, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
|
||||
return ConversionMenuItem.convert_to_bin.isItemVisibleFor(generic, operation);
|
||||
}
|
||||
},
|
||||
|
||||
convert_to_dec(R.string.convert_to_dec) {
|
||||
@Override
|
||||
public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) {
|
||||
ConversionMenuItem.convert_to_dec.onClick(data, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
|
||||
return ConversionMenuItem.convert_to_dec.isItemVisibleFor(generic, operation);
|
||||
}
|
||||
},
|
||||
|
||||
convert_to_hex(R.string.convert_to_hex) {
|
||||
@Override
|
||||
public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) {
|
||||
ConversionMenuItem.convert_to_hex.onClick(data, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
|
||||
return ConversionMenuItem.convert_to_hex.isItemVisibleFor(generic, operation);
|
||||
}
|
||||
},
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
|
||||
return operation == JsclOperation.numeric && generic.getConstants().isEmpty();
|
||||
}
|
||||
},
|
||||
|
||||
plot(R.string.c_plot) {
|
||||
@Override
|
||||
public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) {
|
||||
final Generic generic = data.getState().getResult();
|
||||
assert generic != null;
|
||||
|
||||
final Constant constant = CollectionsUtils.getFirstCollectionElement(getNotSystemConstants(generic));
|
||||
assert constant != null;
|
||||
CalculatorActivityLauncher.plotGraph(context, generic, constant);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
|
||||
boolean result = false;
|
||||
|
||||
if (operation == JsclOperation.simplify) {
|
||||
if (getNotSystemConstants(generic).size() == 1) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Set<Constant> getNotSystemConstants(@NotNull Generic generic) {
|
||||
final Set<Constant> notSystemConstants = new HashSet<Constant>();
|
||||
|
||||
for (Constant constant : generic.getConstants()) {
|
||||
IConstant var = CalculatorEngine.instance.getVarsRegistry().get(constant.getName());
|
||||
if (var != null && !var.isSystem() && !var.isDefined()) {
|
||||
notSystemConstants.add(constant);
|
||||
}
|
||||
}
|
||||
|
||||
return notSystemConstants;
|
||||
}
|
||||
};
|
||||
|
||||
private final int 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());
|
||||
}
|
||||
|
||||
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getCaption(@NotNull Context context) {
|
||||
return context.getString(captionId);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private CalculatorDisplayViewState state = CalculatorDisplayViewStateImpl.newDefaultInstance();
|
||||
|
||||
@NotNull
|
||||
private final static TextProcessor<TextHighlighter.Result, String> textHighlighter = new TextHighlighter(Color.WHITE, false, CalculatorEngine.instance.getEngine());
|
||||
|
||||
public AndroidCalculatorDisplayView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public AndroidCalculatorDisplayView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public AndroidCalculatorDisplayView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return this.state.isValid();
|
||||
}
|
||||
|
||||
|
||||
@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());
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CalculatorDisplayViewState getState() {
|
||||
return this.state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setText(CharSequence text, BufferType type) {
|
||||
super.setText(text, type);
|
||||
}
|
||||
|
||||
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 int getSelection() {
|
||||
return this.getSelectionStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelection(int selection) {
|
||||
// not supported by TextView
|
||||
}
|
||||
}
|
@@ -1,409 +1,412 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
|
||||
* For more information, please, contact se.solovyev@gmail.com
|
||||
*/
|
||||
|
||||
package org.solovyev.android.calculator;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Handler;
|
||||
import android.text.ClipboardManager;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
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.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.history.HistoryControl;
|
||||
import org.solovyev.android.menu.AMenuBuilder;
|
||||
import org.solovyev.android.menu.MenuImpl;
|
||||
import org.solovyev.common.MutableObject;
|
||||
import org.solovyev.common.history.HistoryAction;
|
||||
import org.solovyev.common.msg.Message;
|
||||
import org.solovyev.common.text.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 9/12/11
|
||||
* Time: 11:15 PM
|
||||
*/
|
||||
public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorHistoryState>, CalculatorEngineControl {
|
||||
|
||||
instance;
|
||||
|
||||
// millis to wait before evaluation after user edit action
|
||||
public static final int EVAL_DELAY_MILLIS = 0;
|
||||
|
||||
@NotNull
|
||||
private CalculatorEditor editor;
|
||||
|
||||
@NotNull
|
||||
private CalculatorDisplay display;
|
||||
|
||||
@NotNull
|
||||
private CalculatorEngine calculatorEngine;
|
||||
|
||||
public CalculatorModel init(@NotNull final Activity activity, @NotNull SharedPreferences preferences, @NotNull CalculatorEngine calculator) {
|
||||
Log.d(this.getClass().getName(), "CalculatorModel initialization with activity: " + activity);
|
||||
this.calculatorEngine = calculator;
|
||||
|
||||
this.editor = (CalculatorEditor) activity.findViewById(R.id.calculatorEditor);
|
||||
this.editor.init(preferences);
|
||||
preferences.registerOnSharedPreferenceChangeListener(editor);
|
||||
|
||||
this.display = (CalculatorDisplay) activity.findViewById(R.id.calculatorDisplay);
|
||||
this.display.setOnClickListener(new CalculatorDisplayOnClickListener(activity));
|
||||
|
||||
final CalculatorHistoryState lastState = AndroidCalculatorHistoryImpl.instance.getLastHistoryState();
|
||||
if (lastState == null) {
|
||||
saveHistoryState();
|
||||
} else {
|
||||
setCurrentHistoryState(lastState);
|
||||
}
|
||||
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private static void showEvaluationError(@NotNull Activity activity, @NotNull final String errorMessage) {
|
||||
final LayoutInflater layoutInflater = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
|
||||
|
||||
final View errorMessageView = layoutInflater.inflate(R.layout.display_error_message, null);
|
||||
((TextView) errorMessageView.findViewById(R.id.error_message_text_view)).setText(errorMessage);
|
||||
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(activity)
|
||||
.setPositiveButton(R.string.c_cancel, null)
|
||||
.setView(errorMessageView);
|
||||
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
public void copyResult(@NotNull Context context) {
|
||||
copyResult(context, display);
|
||||
}
|
||||
|
||||
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);
|
||||
clipboard.setText(text.toString());
|
||||
Toast.makeText(context, context.getText(R.string.c_result_copied), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveHistoryState() {
|
||||
AndroidCalculatorHistoryImpl.instance.addState(getCurrentHistoryState());
|
||||
}
|
||||
|
||||
public void setCursorOnStart() {
|
||||
editor.setSelection(0);
|
||||
}
|
||||
|
||||
public void setCursorOnEnd() {
|
||||
editor.setSelection(editor.getText().length());
|
||||
}
|
||||
|
||||
public void moveCursorLeft() {
|
||||
if (editor.getSelectionStart() > 0) {
|
||||
editor.setSelection(editor.getSelectionStart() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void moveCursorRight() {
|
||||
if (editor.getSelectionStart() < editor.getText().length()) {
|
||||
editor.setSelection(editor.getSelectionStart() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void doTextOperation(@NotNull TextOperation operation) {
|
||||
doTextOperation(operation, true);
|
||||
}
|
||||
|
||||
public void doTextOperation(@NotNull TextOperation operation, boolean delayEvaluate) {
|
||||
doTextOperation(operation, delayEvaluate, JsclOperation.numeric, false);
|
||||
}
|
||||
|
||||
public void doTextOperation(@NotNull TextOperation operation, boolean delayEvaluate, @NotNull JsclOperation jsclOperation, boolean forceEval) {
|
||||
final String editorStateBefore = this.editor.getText().toString();
|
||||
|
||||
Log.d(CalculatorModel.class.getName(), "Editor state changed before '" + editorStateBefore + "'");
|
||||
operation.doOperation(this.editor);
|
||||
//Log.d(CalculatorModel.class.getName(), "Doing text operation" + StringUtils.fromStackTrace(Thread.currentThread().getStackTrace()));
|
||||
|
||||
final String editorStateAfter = this.editor.getText().toString();
|
||||
if (forceEval ||!editorStateBefore.equals(editorStateAfter)) {
|
||||
|
||||
editor.redraw();
|
||||
|
||||
evaluate(delayEvaluate, editorStateAfter, jsclOperation, null);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private final static MutableObject<Runnable> pendingOperation = new MutableObject<Runnable>();
|
||||
|
||||
private void evaluate(boolean delayEvaluate,
|
||||
@NotNull final String expression,
|
||||
@NotNull final JsclOperation operation,
|
||||
@Nullable CalculatorHistoryState historyState) {
|
||||
|
||||
final CalculatorHistoryState localHistoryState;
|
||||
if (historyState == null) {
|
||||
//this.display.setText("");
|
||||
localHistoryState = getCurrentHistoryState();
|
||||
} else {
|
||||
this.display.setText(historyState.getDisplayState().getEditorState().getText());
|
||||
localHistoryState = historyState;
|
||||
}
|
||||
|
||||
pendingOperation.setObject(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// allow only one runner at one time
|
||||
synchronized (pendingOperation) {
|
||||
//lock all operations with history
|
||||
if (pendingOperation.getObject() == this) {
|
||||
// actually nothing shall be logged while text operations are done
|
||||
evaluate(expression, operation, this);
|
||||
|
||||
if (pendingOperation.getObject() == this) {
|
||||
// todo serso: of course there is small probability that someone will set pendingOperation after if statement but before .setObject(null)
|
||||
pendingOperation.setObject(null);
|
||||
localHistoryState.setDisplayState(getCurrentHistoryState().getDisplayState());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (delayEvaluate) {
|
||||
if (historyState == null) {
|
||||
AndroidCalculatorHistoryImpl.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evaluate() {
|
||||
evaluate(false, this.editor.getText().toString(), JsclOperation.numeric, null);
|
||||
}
|
||||
|
||||
public void evaluate(@NotNull JsclOperation operation) {
|
||||
evaluate(false, this.editor.getText().toString(), operation, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void simplify() {
|
||||
evaluate(false, this.editor.getText().toString(), JsclOperation.simplify, null);
|
||||
}
|
||||
|
||||
private void evaluate(@Nullable final String expression,
|
||||
@NotNull JsclOperation operation,
|
||||
@NotNull Runnable currentRunner) {
|
||||
|
||||
if (!StringUtils.isEmpty(expression)) {
|
||||
try {
|
||||
Log.d(CalculatorModel.class.getName(), "Trying to evaluate '" + operation + "': " + expression /*+ StringUtils.fromStackTrace(Thread.currentThread().getStackTrace())*/);
|
||||
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.getResult());
|
||||
} else {
|
||||
display.setText("");
|
||||
}
|
||||
display.setJsclOperation(result.getUserOperation());
|
||||
display.setGenericResult(result.getGenericResult());
|
||||
} catch (CalculatorParseException e) {
|
||||
handleEvaluationException(expression, display, operation, e);
|
||||
} catch (CalculatorEvalException e) {
|
||||
handleEvaluationException(expression, display, operation, e);
|
||||
}
|
||||
} else {
|
||||
this.display.setText("");
|
||||
this.display.setJsclOperation(operation);
|
||||
this.display.setGenericResult(null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
this.display.redraw();
|
||||
}
|
||||
|
||||
private void handleEvaluationException(@NotNull String expression,
|
||||
@NotNull CalculatorDisplay localDisplay,
|
||||
@NotNull JsclOperation operation,
|
||||
@NotNull Message e) {
|
||||
Log.d(CalculatorModel.class.getName(), "Evaluation failed for : " + expression + ". Error message: " + e);
|
||||
if ( StringUtils.isEmpty(localDisplay.getText()) ) {
|
||||
// if previous display state was empty -> show error
|
||||
localDisplay.setText(R.string.c_syntax_error);
|
||||
} else {
|
||||
// show previous result instead of error caption (actually previous result will be greyed)
|
||||
}
|
||||
localDisplay.setJsclOperation(operation);
|
||||
localDisplay.setGenericResult(null);
|
||||
localDisplay.setValid(false);
|
||||
localDisplay.setErrorMessage(e.getLocalizedMessage());
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
if (!StringUtils.isEmpty(editor.getText()) || !StringUtils.isEmpty(display.getText())) {
|
||||
editor.getText().clear();
|
||||
display.setText("");
|
||||
saveHistoryState();
|
||||
}
|
||||
}
|
||||
|
||||
public void processDigitButtonAction(@Nullable final String text) {
|
||||
processDigitButtonAction(text, true);
|
||||
}
|
||||
|
||||
public void processDigitButtonAction(@Nullable final String text, boolean delayEvaluate) {
|
||||
|
||||
if (!StringUtils.isEmpty(text)) {
|
||||
doTextOperation(new CalculatorModel.TextOperation() {
|
||||
|
||||
@Override
|
||||
public void doOperation(@NotNull EditText editor) {
|
||||
int cursorPositionOffset = 0;
|
||||
final StringBuilder textToBeInserted = new StringBuilder(text);
|
||||
|
||||
final MathType.Result mathType = MathType.getType(text, 0, false);
|
||||
switch (mathType.getMathType()) {
|
||||
case function:
|
||||
textToBeInserted.append("()");
|
||||
cursorPositionOffset = -1;
|
||||
break;
|
||||
case operator:
|
||||
textToBeInserted.append("()");
|
||||
cursorPositionOffset = -1;
|
||||
break;
|
||||
case comma:
|
||||
textToBeInserted.append(" ");
|
||||
break;
|
||||
}
|
||||
|
||||
if (cursorPositionOffset == 0) {
|
||||
if (MathType.openGroupSymbols.contains(text)) {
|
||||
cursorPositionOffset = -1;
|
||||
}
|
||||
}
|
||||
|
||||
editor.getText().insert(editor.getSelectionStart(), textToBeInserted.toString());
|
||||
editor.setSelection(editor.getSelectionStart() + cursorPositionOffset, editor.getSelectionEnd() + cursorPositionOffset);
|
||||
}
|
||||
}, delayEvaluate);
|
||||
}
|
||||
}
|
||||
|
||||
public static interface TextOperation {
|
||||
|
||||
void doOperation(@NotNull EditText editor);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doHistoryAction(@NotNull HistoryAction historyAction) {
|
||||
synchronized (AndroidCalculatorHistoryImpl.instance) {
|
||||
if (AndroidCalculatorHistoryImpl.instance.isActionAvailable(historyAction)) {
|
||||
final CalculatorHistoryState newState = AndroidCalculatorHistoryImpl.instance.doAction(historyAction, getCurrentHistoryState());
|
||||
if (newState != null) {
|
||||
setCurrentHistoryState(newState);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrentHistoryState(@NotNull CalculatorHistoryState editorHistoryState) {
|
||||
synchronized (AndroidCalculatorHistoryImpl.instance) {
|
||||
Log.d(this.getClass().getName(), "Saved history found: " + editorHistoryState);
|
||||
|
||||
editorHistoryState.setValuesFromHistory(new TextViewEditorAdapter(this.editor), this.display);
|
||||
|
||||
final String expression = this.editor.getText().toString();
|
||||
if ( !StringUtils.isEmpty(expression) ) {
|
||||
if ( StringUtils.isEmpty(this.display.getText().toString()) ) {
|
||||
evaluate(false, expression, this.display.getJsclOperation(), editorHistoryState);
|
||||
}
|
||||
}
|
||||
|
||||
editor.redraw();
|
||||
display.redraw();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public CalculatorHistoryState getCurrentHistoryState() {
|
||||
synchronized (AndroidCalculatorHistoryImpl.instance) {
|
||||
return CalculatorHistoryState.newInstance(new TextViewEditorAdapter(this.editor), this.display);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public CalculatorDisplay getDisplay() {
|
||||
return display;
|
||||
}
|
||||
|
||||
private static class CalculatorDisplayOnClickListener implements View.OnClickListener {
|
||||
|
||||
@NotNull
|
||||
private final Activity activity;
|
||||
|
||||
public CalculatorDisplayOnClickListener(@NotNull Activity activity) {
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (v instanceof CalculatorDisplay) {
|
||||
final CalculatorDisplay cd = (CalculatorDisplay) v;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if (!filteredMenuItems.isEmpty()) {
|
||||
AMenuBuilder.newInstance(activity, MenuImpl.newInstance(filteredMenuItems)).create(cd).show();
|
||||
}
|
||||
|
||||
} else {
|
||||
final String errorMessage = cd.getErrorMessage();
|
||||
if (errorMessage != null) {
|
||||
showEvaluationError(activity, errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
|
||||
* For more information, please, contact se.solovyev@gmail.com
|
||||
*/
|
||||
|
||||
package org.solovyev.android.calculator;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Handler;
|
||||
import android.text.ClipboardManager;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
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.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.history.HistoryControl;
|
||||
import org.solovyev.android.menu.AMenuBuilder;
|
||||
import org.solovyev.android.menu.MenuImpl;
|
||||
import org.solovyev.common.MutableObject;
|
||||
import org.solovyev.common.history.HistoryAction;
|
||||
import org.solovyev.common.msg.Message;
|
||||
import org.solovyev.common.text.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 9/12/11
|
||||
* Time: 11:15 PM
|
||||
*/
|
||||
public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorHistoryState>, CalculatorEngineControl {
|
||||
|
||||
instance;
|
||||
|
||||
// millis to wait before evaluation after user edit action
|
||||
public static final int EVAL_DELAY_MILLIS = 0;
|
||||
|
||||
@NotNull
|
||||
private CalculatorEditor editor;
|
||||
|
||||
@NotNull
|
||||
private AndroidCalculatorDisplayView display;
|
||||
|
||||
@NotNull
|
||||
private CalculatorEngine calculatorEngine;
|
||||
|
||||
public CalculatorModel init(@NotNull final Activity activity, @NotNull SharedPreferences preferences, @NotNull CalculatorEngine calculator) {
|
||||
Log.d(this.getClass().getName(), "CalculatorModel initialization with activity: " + activity);
|
||||
this.calculatorEngine = calculator;
|
||||
|
||||
this.editor = (CalculatorEditor) activity.findViewById(R.id.calculatorEditor);
|
||||
this.editor.init(preferences);
|
||||
preferences.registerOnSharedPreferenceChangeListener(editor);
|
||||
|
||||
this.display = (AndroidCalculatorDisplayView) activity.findViewById(R.id.calculatorDisplay);
|
||||
this.display.setOnClickListener(new CalculatorDisplayOnClickListener(activity));
|
||||
|
||||
final CalculatorHistoryState lastState = AndroidCalculatorHistoryImpl.instance.getLastHistoryState();
|
||||
if (lastState == null) {
|
||||
saveHistoryState();
|
||||
} else {
|
||||
setCurrentHistoryState(lastState);
|
||||
}
|
||||
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private static void showEvaluationError(@NotNull Activity activity, @NotNull final String errorMessage) {
|
||||
final LayoutInflater layoutInflater = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
|
||||
|
||||
final View errorMessageView = layoutInflater.inflate(R.layout.display_error_message, null);
|
||||
((TextView) errorMessageView.findViewById(R.id.error_message_text_view)).setText(errorMessage);
|
||||
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(activity)
|
||||
.setPositiveButton(R.string.c_cancel, null)
|
||||
.setView(errorMessageView);
|
||||
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
public void copyResult(@NotNull Context context) {
|
||||
copyResult(context, display);
|
||||
}
|
||||
|
||||
public static void copyResult(@NotNull Context context, @NotNull final CalculatorDisplayView display) {
|
||||
final CalculatorDisplayViewState displayViewState = display.getState();
|
||||
if (displayViewState.isValid()) {
|
||||
final CharSequence text = display.getText();
|
||||
if (!StringUtils.isEmpty(text)) {
|
||||
final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE);
|
||||
clipboard.setText(text.toString());
|
||||
Toast.makeText(context, context.getText(R.string.c_result_copied), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveHistoryState() {
|
||||
AndroidCalculatorHistoryImpl.instance.addState(getCurrentHistoryState());
|
||||
}
|
||||
|
||||
public void setCursorOnStart() {
|
||||
editor.setSelection(0);
|
||||
}
|
||||
|
||||
public void setCursorOnEnd() {
|
||||
editor.setSelection(editor.getText().length());
|
||||
}
|
||||
|
||||
public void moveCursorLeft() {
|
||||
if (editor.getSelectionStart() > 0) {
|
||||
editor.setSelection(editor.getSelectionStart() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void moveCursorRight() {
|
||||
if (editor.getSelectionStart() < editor.getText().length()) {
|
||||
editor.setSelection(editor.getSelectionStart() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void doTextOperation(@NotNull TextOperation operation) {
|
||||
doTextOperation(operation, true);
|
||||
}
|
||||
|
||||
public void doTextOperation(@NotNull TextOperation operation, boolean delayEvaluate) {
|
||||
doTextOperation(operation, delayEvaluate, JsclOperation.numeric, false);
|
||||
}
|
||||
|
||||
public void doTextOperation(@NotNull TextOperation operation, boolean delayEvaluate, @NotNull JsclOperation jsclOperation, boolean forceEval) {
|
||||
final String editorStateBefore = this.editor.getText().toString();
|
||||
|
||||
Log.d(CalculatorModel.class.getName(), "Editor state changed before '" + editorStateBefore + "'");
|
||||
operation.doOperation(this.editor);
|
||||
//Log.d(CalculatorModel.class.getName(), "Doing text operation" + StringUtils.fromStackTrace(Thread.currentThread().getStackTrace()));
|
||||
|
||||
final String editorStateAfter = this.editor.getText().toString();
|
||||
if (forceEval ||!editorStateBefore.equals(editorStateAfter)) {
|
||||
|
||||
editor.redraw();
|
||||
|
||||
evaluate(delayEvaluate, editorStateAfter, jsclOperation, null);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private final static MutableObject<Runnable> pendingOperation = new MutableObject<Runnable>();
|
||||
|
||||
private void evaluate(boolean delayEvaluate,
|
||||
@NotNull final String expression,
|
||||
@NotNull final JsclOperation operation,
|
||||
@Nullable CalculatorHistoryState historyState) {
|
||||
|
||||
final CalculatorHistoryState localHistoryState;
|
||||
if (historyState == null) {
|
||||
//this.display.setText("");
|
||||
localHistoryState = getCurrentHistoryState();
|
||||
} else {
|
||||
this.display.setText(historyState.getDisplayState().getEditorState().getText());
|
||||
localHistoryState = historyState;
|
||||
}
|
||||
|
||||
pendingOperation.setObject(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// allow only one runner at one time
|
||||
synchronized (pendingOperation) {
|
||||
//lock all operations with history
|
||||
if (pendingOperation.getObject() == this) {
|
||||
// actually nothing shall be logged while text operations are done
|
||||
evaluate(expression, operation, this);
|
||||
|
||||
if (pendingOperation.getObject() == this) {
|
||||
// todo serso: of course there is small probability that someone will set pendingOperation after if statement but before .setObject(null)
|
||||
pendingOperation.setObject(null);
|
||||
localHistoryState.setDisplayState(getCurrentHistoryState().getDisplayState());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (delayEvaluate) {
|
||||
if (historyState == null) {
|
||||
AndroidCalculatorHistoryImpl.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evaluate() {
|
||||
evaluate(false, this.editor.getText().toString(), JsclOperation.numeric, null);
|
||||
}
|
||||
|
||||
public void evaluate(@NotNull JsclOperation operation) {
|
||||
evaluate(false, this.editor.getText().toString(), operation, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void simplify() {
|
||||
evaluate(false, this.editor.getText().toString(), JsclOperation.simplify, null);
|
||||
}
|
||||
|
||||
private void evaluate(@Nullable final String expression,
|
||||
@NotNull JsclOperation operation,
|
||||
@NotNull Runnable currentRunner) {
|
||||
|
||||
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);
|
||||
|
||||
// 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());
|
||||
} else {
|
||||
display.setText("");
|
||||
}
|
||||
display.setJsclOperation(result.getOperation());
|
||||
display.setGenericResult(result.getResult());
|
||||
} catch (CalculatorParseException e) {
|
||||
handleEvaluationException(expression, display, operation, e);
|
||||
} catch (CalculatorEvalException e) {
|
||||
handleEvaluationException(expression, display, operation, e);
|
||||
}
|
||||
} else {
|
||||
this.display.setText("");
|
||||
this.display.setJsclOperation(operation);
|
||||
this.display.setGenericResult(null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
this.display.redraw();
|
||||
}
|
||||
|
||||
private void handleEvaluationException(@NotNull String expression,
|
||||
@NotNull AndroidCalculatorDisplayView localDisplay,
|
||||
@NotNull JsclOperation operation,
|
||||
@NotNull Message e) {
|
||||
Log.d(CalculatorModel.class.getName(), "Evaluation failed for : " + expression + ". Error message: " + e);
|
||||
if ( StringUtils.isEmpty(localDisplay.getText()) ) {
|
||||
// if previous display state was empty -> show error
|
||||
localDisplay.setText(R.string.c_syntax_error);
|
||||
} else {
|
||||
// show previous result instead of error caption (actually previous result will be greyed)
|
||||
}
|
||||
localDisplay.setJsclOperation(operation);
|
||||
localDisplay.setGenericResult(null);
|
||||
localDisplay.setValid(false);
|
||||
localDisplay.setErrorMessage(e.getLocalizedMessage());
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
if (!StringUtils.isEmpty(editor.getText()) || !StringUtils.isEmpty(display.getText())) {
|
||||
editor.getText().clear();
|
||||
display.setText("");
|
||||
saveHistoryState();
|
||||
}
|
||||
}
|
||||
|
||||
public void processDigitButtonAction(@Nullable final String text) {
|
||||
processDigitButtonAction(text, true);
|
||||
}
|
||||
|
||||
public void processDigitButtonAction(@Nullable final String text, boolean delayEvaluate) {
|
||||
|
||||
if (!StringUtils.isEmpty(text)) {
|
||||
doTextOperation(new CalculatorModel.TextOperation() {
|
||||
|
||||
@Override
|
||||
public void doOperation(@NotNull EditText editor) {
|
||||
int cursorPositionOffset = 0;
|
||||
final StringBuilder textToBeInserted = new StringBuilder(text);
|
||||
|
||||
final MathType.Result mathType = MathType.getType(text, 0, false);
|
||||
switch (mathType.getMathType()) {
|
||||
case function:
|
||||
textToBeInserted.append("()");
|
||||
cursorPositionOffset = -1;
|
||||
break;
|
||||
case operator:
|
||||
textToBeInserted.append("()");
|
||||
cursorPositionOffset = -1;
|
||||
break;
|
||||
case comma:
|
||||
textToBeInserted.append(" ");
|
||||
break;
|
||||
}
|
||||
|
||||
if (cursorPositionOffset == 0) {
|
||||
if (MathType.openGroupSymbols.contains(text)) {
|
||||
cursorPositionOffset = -1;
|
||||
}
|
||||
}
|
||||
|
||||
editor.getText().insert(editor.getSelectionStart(), textToBeInserted.toString());
|
||||
editor.setSelection(editor.getSelectionStart() + cursorPositionOffset, editor.getSelectionEnd() + cursorPositionOffset);
|
||||
}
|
||||
}, delayEvaluate);
|
||||
}
|
||||
}
|
||||
|
||||
public static interface TextOperation {
|
||||
|
||||
void doOperation(@NotNull EditText editor);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doHistoryAction(@NotNull HistoryAction historyAction) {
|
||||
synchronized (AndroidCalculatorHistoryImpl.instance) {
|
||||
if (AndroidCalculatorHistoryImpl.instance.isActionAvailable(historyAction)) {
|
||||
final CalculatorHistoryState newState = AndroidCalculatorHistoryImpl.instance.doAction(historyAction, getCurrentHistoryState());
|
||||
if (newState != null) {
|
||||
setCurrentHistoryState(newState);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrentHistoryState(@NotNull CalculatorHistoryState editorHistoryState) {
|
||||
synchronized (AndroidCalculatorHistoryImpl.instance) {
|
||||
Log.d(this.getClass().getName(), "Saved history found: " + editorHistoryState);
|
||||
|
||||
editorHistoryState.setValuesFromHistory(new TextViewEditorAdapter(this.editor), this.display);
|
||||
|
||||
final String expression = this.editor.getText().toString();
|
||||
if ( !StringUtils.isEmpty(expression) ) {
|
||||
if ( StringUtils.isEmpty(this.display.getText().toString()) ) {
|
||||
evaluate(false, expression, this.display.getJsclOperation(), editorHistoryState);
|
||||
}
|
||||
}
|
||||
|
||||
editor.redraw();
|
||||
display.redraw();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public CalculatorHistoryState getCurrentHistoryState() {
|
||||
synchronized (AndroidCalculatorHistoryImpl.instance) {
|
||||
return CalculatorHistoryState.newInstance(new TextViewEditorAdapter(this.editor), this.display);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public AndroidCalculatorDisplayView getDisplay() {
|
||||
return display;
|
||||
}
|
||||
|
||||
private static class CalculatorDisplayOnClickListener implements View.OnClickListener {
|
||||
|
||||
@NotNull
|
||||
private final Activity activity;
|
||||
|
||||
public CalculatorDisplayOnClickListener(@NotNull Activity activity) {
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (v instanceof CalculatorDisplayView) {
|
||||
final CalculatorDisplay cd = CalculatorLocatorImpl.getInstance().getCalculatorDisplay();
|
||||
|
||||
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)) {
|
||||
filteredMenuItems.add(menuItem);
|
||||
}
|
||||
}
|
||||
|
||||
if (!filteredMenuItems.isEmpty()) {
|
||||
AMenuBuilder.newInstance(activity, MenuImpl.newInstance(filteredMenuItems)).create(cd).show();
|
||||
}
|
||||
|
||||
} else {
|
||||
final String errorMessage = displayViewState.getErrorMessage();
|
||||
if (errorMessage != null) {
|
||||
showEvaluationError(activity, errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,437 +1,404 @@
|
||||
/*
|
||||
* 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 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());
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
|
||||
@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.*;
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,321 +1,321 @@
|
||||
/*
|
||||
* 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.Editor;
|
||||
import org.solovyev.android.calculator.JCalculatorDisplay;
|
||||
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>();
|
||||
|
||||
JCalculatorDisplay 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 JCalculatorDisplay {
|
||||
|
||||
@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.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,445 +1,445 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
|
||||
* For more information, please, contact se.solovyev@gmail.com
|
||||
*/
|
||||
|
||||
package org.solovyev.android.calculator.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, "1°").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());
|
||||
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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, "1°").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());
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -1,148 +1,148 @@
|
||||
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).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 + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
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 + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user