This commit is contained in:
serso
2011-11-18 00:47:33 +04:00
parent 94282706fe
commit c1a1e23e3c
13 changed files with 204 additions and 87 deletions

View File

@@ -96,6 +96,18 @@ public class CalculatorActivity extends Activity implements FontSizeAdjuster, Sh
final OnDragListener historyOnDragListener = new OnDragListenerVibrator(newOnDragListener(new HistoryDragProcessor<CalculatorHistoryState>(this.calculatorModel), dragPreferences), vibrator, preferences);
((DragButton) findViewById(R.id.historyButton)).setOnDragListener(historyOnDragListener);
((DragButton) findViewById(R.id.subtractionButton)).setOnDragListener(new OnDragListenerVibrator(newOnDragListener(new SimpleOnDragListener.DragProcessor() {
@Override
public boolean processDragEvent(@NotNull DragDirection dragDirection, @NotNull DragButton dragButton, @NotNull Point2d startPoint2d, @NotNull MotionEvent motionEvent) {
if (dragDirection == DragDirection.down) {
operatorsButtonClickHandler(dragButton);
return true;
}
return false;
}
}, dragPreferences), vibrator, preferences));
final OnDragListener toPositionOnDragListener = new OnDragListenerVibrator(new SimpleOnDragListener(new CursorDragProcessor(calculatorModel), dragPreferences), vibrator, preferences);
((DragButton) findViewById(R.id.rightButton)).setOnDragListener(toPositionOnDragListener);
((DragButton) findViewById(R.id.leftButton)).setOnDragListener(toPositionOnDragListener);

View File

@@ -269,6 +269,9 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
textToBeInserted.append("()");
cursorPositionOffset = -1;
break;
case comma:
textToBeInserted.append(" ");
break;
}
if (cursorPositionOffset == 0) {

View File

@@ -118,6 +118,8 @@ public enum MathType {
}
},
comma(1150, false, false, ","),
text(1200, false, false) {
@Override
public int processToJscl(@NotNull StringBuilder result, int i, @NotNull String match) {

View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.model;
import org.jetbrains.annotations.NotNull;
import org.solovyev.common.math.MathEntity;
import org.solovyev.common.math.MathRegistry;
import java.util.HashMap;
import java.util.Map;
/**
* User: serso
* Date: 11/17/11
* Time: 11:28 PM
*/
public class AndroidFunctionsMathRegistry<T extends MathEntity> extends AndroidMathRegistryImpl<T> {
@NotNull
private static final Map<String, String> substitutes = new HashMap<String, String>();
static {
substitutes.put("", "sqrt");
}
@NotNull
private static final String FUNCTION_DESCRIPTION_PREFIX = "c_fun_description_";
public AndroidFunctionsMathRegistry(@NotNull MathRegistry<T> functionsRegistry) {
super(functionsRegistry, FUNCTION_DESCRIPTION_PREFIX);
}
@NotNull
@Override
protected Map<String, String> getSubstitutes() {
return substitutes;
}
}

View File

@@ -7,7 +7,6 @@
package org.solovyev.android.calculator.model;
import android.content.Context;
import jscl.math.function.Function;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.R;
@@ -24,32 +23,40 @@ import java.util.Map;
* Date: 10/30/11
* Time: 1:03 AM
*/
public class AndroidMathRegistryImpl<T extends MathEntity> implements AndroidMathRegistry<T> {
@NotNull
private static final String FUNCTION_DESCRIPTION_PREFIX = "c_fun_description_";
public abstract class AndroidMathRegistryImpl<T extends MathEntity> implements AndroidMathRegistry<T> {
@NotNull
private final MathRegistry<T> functionsRegistry;
public AndroidMathRegistryImpl(@NotNull MathRegistry<T> functionsRegistry) {
@NotNull
private final String prefix;
protected AndroidMathRegistryImpl(@NotNull MathRegistry<T> functionsRegistry, @NotNull String prefix) {
this.functionsRegistry = functionsRegistry;
this.prefix = prefix;
}
@NotNull
protected abstract Map<String, String> getSubstitutes();
@Nullable
@Override
public String getDescription(@NotNull Context context, @NotNull String functionName) {
public String getDescription(@NotNull Context context, @NotNull String name) {
final String result;
final Map<String, Integer> stringsCache = RClassUtils.getCache(R.string.class);
final Integer stringId;
if (!functionName.equals("")) {
stringId = stringsCache.get(FUNCTION_DESCRIPTION_PREFIX + functionName);
final Map<String, String> substitutes = getSubstitutes();
final String substitute = substitutes.get(name);
if (substitute == null) {
stringId = stringsCache.get(prefix + name);
} else {
// todo serso: think
stringId = stringsCache.get(FUNCTION_DESCRIPTION_PREFIX + "sqrt");
stringId = stringsCache.get(prefix + substitute);
}
if (stringId != null) {
result = context.getString(stringId);
} else {

View File

@@ -0,0 +1,46 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.model;
import org.jetbrains.annotations.NotNull;
import org.solovyev.common.math.MathEntity;
import org.solovyev.common.math.MathRegistry;
import java.util.HashMap;
import java.util.Map;
/**
* User: serso
* Date: 11/17/11
* Time: 11:29 PM
*/
public class AndroidOperatorsMathRegistry<T extends MathEntity> extends AndroidMathRegistryImpl<T> {
@NotNull
private static final Map<String, String> substitutes = new HashMap<String, String>();
static {
substitutes.put("Σ", "sum");
substitutes.put("", "product");
substitutes.put("", "derivative");
substitutes.put("∫ab", "integral_ab");
substitutes.put("", "integral");
substitutes.put("Σ", "sum");
}
@NotNull
private static final String OPERATOR_DESCRIPTION_PREFIX = "c_op_description_";
protected AndroidOperatorsMathRegistry(@NotNull MathRegistry<T> functionsRegistry) {
super(functionsRegistry, OPERATOR_DESCRIPTION_PREFIX);
}
@NotNull
@Override
protected Map<String, String> getSubstitutes() {
return substitutes;
}
}

View File

@@ -13,24 +13,18 @@ import jscl.math.operator.Operator;
import jscl.text.ParseInterruptedException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.msg.AndroidMessage;
import org.solovyev.common.NumberMapper;
import org.solovyev.common.math.MathRegistry;
import org.solovyev.common.msg.MessageRegistry;
import org.solovyev.common.msg.MessageType;
import org.solovyev.common.utils.CollectionsUtils;
import org.solovyev.common.utils.Formatter;
import org.solovyev.common.utils.MutableObject;
import org.solovyev.common.utils.StringUtils;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -70,18 +64,16 @@ public enum CalculatorEngine {
private final AndroidVarsRegistry varsRegister = new AndroidVarsRegistryImpl(engine.getConstantsRegistry());
@NotNull
private final AndroidMathRegistry functionsRegistry = new AndroidMathRegistryImpl(engine.getFunctionsRegistry());
private final AndroidMathRegistry functionsRegistry = new AndroidFunctionsMathRegistry(engine.getFunctionsRegistry());
@NotNull
private final AndroidMathRegistry operatorsRegistry = new AndroidMathRegistryImpl(engine.getOperatorsRegistry());
private final AndroidMathRegistry operatorsRegistry = new AndroidOperatorsMathRegistry(engine.getOperatorsRegistry());
private final MathRegistry<Operator> postfixFunctionsRegistry = engine.getPostfixFunctionsRegistry();
@NotNull
private final static Set<String> tooLongExecutionCache = new HashSet<String>();
@NotNull
private DecimalFormatSymbols decimalGroupSymbols = new DecimalFormatSymbols(Locale.getDefault());
{
decimalGroupSymbols.setDecimalSeparator('.');
decimalGroupSymbols.setGroupingSeparator(GROUPING_SEPARATOR_DEFAULT.charAt(0));
@@ -106,7 +98,7 @@ public enum CalculatorEngine {
final DecimalFormat df = new DecimalFormat();
df.setDecimalFormatSymbols(decimalGroupSymbols);
df.setGroupingUsed(useGroupingSeparator);
if (round ) {
if (round) {
if (isRoundResult()) {
df.setMaximumFractionDigits(instance.getPrecision());
return df.format(new BigDecimal(value).setScale(instance.getPrecision(), BigDecimal.ROUND_HALF_UP).doubleValue());
@@ -178,75 +170,70 @@ public enum CalculatorEngine {
final JsclOperation finalOperation = operation;
final String result;
if (!tooLongExecutionCache.contains(jsclExpression)) {
final MutableObject<String> calculationResult = new MutableObject<String>(null);
final MutableObject<ParseException> exception = new MutableObject<ParseException>(null);
final MutableObject<Thread> calculationThread = new MutableObject<Thread>(null);
final MutableObject<String> calculationResult = new MutableObject<String>(null);
final MutableObject<ParseException> exception = new MutableObject<ParseException>(null);
final MutableObject<Thread> calculationThread = new MutableObject<Thread>(null);
final CountDownLatch latch = new CountDownLatch(1);
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);
calculationResult.setObject(finalOperation.evaluate(jsclExpression));
} catch (ArithmeticException e) {
//System.out.println(e.getMessage());
exception.setObject(new ParseException(e.getMessage(), e));
} catch (jscl.text.ParseException e) {
//System.out.println(e.getMessage());
exception.setObject(new ParseException(e.getMessage(), 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();
}
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);
calculationResult.setObject(finalOperation.evaluate(jsclExpression));
} catch (ArithmeticException e) {
//System.out.println(e.getMessage());
exception.setObject(new ParseException(e.getMessage(), e));
} catch (jscl.text.ParseException e) {
//System.out.println(e.getMessage());
exception.setObject(new ParseException(e.getMessage(), 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();
}
}).start();
try {
//Log.d(CalculatorEngine.class.getName(), "Main thread is waiting: " + Thread.currentThread().getName());
latch.await(timeout, TimeUnit.MILLISECONDS);
//Log.d(CalculatorEngine.class.getName(), "Main thread got up: " + Thread.currentThread().getName());
try {
//Log.d(CalculatorEngine.class.getName(), "Main thread is waiting: " + Thread.currentThread().getName());
latch.await(timeout, TimeUnit.MILLISECONDS);
//Log.d(CalculatorEngine.class.getName(), "Main thread got up: " + Thread.currentThread().getName());
final ParseException evalErrorLocal = exception.getObject();
final Object calculationResultLocal = calculationResult.getObject();
final Thread calculationThreadLocal = calculationThread.getObject();
final ParseException evalErrorLocal = exception.getObject();
final Object calculationResultLocal = calculationResult.getObject();
final Thread calculationThreadLocal = calculationThread.getObject();
if (calculationThreadLocal != null) {
// todo serso: interrupt doesn't stop the thread but it MUST be killed
threadKiller.killThread(calculationThreadLocal);
//calculationThreadLocal.stop();
}
if ( evalErrorLocal != null ) {
if ( finalOperation == JsclOperation.numeric && preparedExpression.isExistsUndefinedVar() ) {
return evaluate(JsclOperation.simplify, expression, mr);
}
throw evalErrorLocal;
}
if ( calculationResultLocal == null ) {
tooLongExecutionCache.add(jsclExpression);
throw new ParseException("Too long calculation for: " + jsclExpression);
}
} catch (InterruptedException e) {
throw new ParseException(e);
if (calculationThreadLocal != null) {
// todo serso: interrupt doesn't stop the thread but it MUST be killed
threadKiller.killThread(calculationThreadLocal);
//calculationThreadLocal.stop();
}
result = String.valueOf(calculationResult.getObject()).trim();
} else {
throw new ParseException("Too long calculation for: " + jsclExpression);
if (evalErrorLocal != null) {
if (finalOperation == JsclOperation.numeric && preparedExpression.isExistsUndefinedVar()) {
return evaluate(JsclOperation.simplify, expression, mr);
}
throw evalErrorLocal;
}
if (calculationResultLocal == null) {
throw new ParseException("Too long calculation for: " + jsclExpression);
}
} catch (InterruptedException e) {
throw new ParseException(e);
}
result = String.valueOf(calculationResult.getObject()).trim();
return new Result(operation.getFromProcessor().process(result), operation);
}
}
@@ -330,6 +317,7 @@ public enum CalculatorEngine {
void setTimeout(int timeout) {
this.timeout = timeout;
}
// for tests only
void setThreadKiller(@NotNull ThreadKiller threadKiller) {
this.threadKiller = threadKiller;