Calculator display changes

This commit is contained in:
Sergey Solovyev 2012-09-20 22:49:39 +04:00
parent 1d2aaa9d47
commit 79e85ea255
36 changed files with 3595 additions and 2859 deletions

View File

@ -1,9 +1,25 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
/** import org.jetbrains.annotations.NotNull;
* User: Solovyev_S import org.jetbrains.annotations.Nullable;
* Date: 20.09.12 import org.solovyev.android.calculator.jscl.JsclOperation;
* Time: 16:38 import org.solovyev.common.msg.MessageRegistry;
*/
public interface Calculator extends CalculatorEventContainer { /**
} * User: Solovyev_S
* Date: 20.09.12
* Time: 16:38
*/
public interface Calculator extends CalculatorEventContainer {
@NotNull
CalculatorEventDataId createFirstEventDataId();
void evaluate(@NotNull JsclOperation operation,
@NotNull String expression);
@NotNull
CalculatorEventDataId evaluate(@NotNull JsclOperation operation,
@NotNull String expression,
@Nullable MessageRegistry mr);
}

View File

@ -0,0 +1,28 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: serso
* Date: 12/17/11
* Time: 9:45 PM
*/
public interface CalculatorDisplay extends CalculatorEventListener {
void setView(@Nullable CalculatorDisplayView view);
@NotNull
CalculatorDisplayViewState getViewState();
void setViewState(@NotNull CalculatorDisplayViewState viewState);
@NotNull
CalculatorEventData getLastEventData();
}

View File

@ -0,0 +1,147 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static org.solovyev.android.calculator.CalculatorEventType.*;
/**
* User: serso
* Date: 9/20/12
* Time: 8:24 PM
*/
public class CalculatorDisplayImpl implements CalculatorDisplay {
@NotNull
private CalculatorEventData lastCalculatorEventData = CalculatorEventDataImpl.newInstance(CalculatorLocatorImpl.getInstance().getCalculator().createFirstEventDataId());
@Nullable
private CalculatorDisplayView view;
@NotNull
private final Object viewLock = new Object();
@NotNull
private CalculatorDisplayViewState lastViewState = CalculatorDisplayViewStateImpl.newDefaultInstance();
@Override
public void setView(@Nullable CalculatorDisplayView view) {
synchronized (viewLock) {
this.view = view;
if (view != null) {
this.view.setState(lastViewState);
}
}
}
@NotNull
@Override
public CalculatorDisplayViewState getViewState() {
return this.lastViewState;
}
@Override
public void setViewState(@NotNull CalculatorDisplayViewState viewState) {
synchronized (viewLock) {
this.lastViewState = viewState;
if (this.view != null) {
this.view.setState(viewState);
}
}
}
/* @Override
@Nullable
public CharSequence getText() {
synchronized (viewLock) {
return view != null ? view.getText() : null;
}
}
@Override
public void setText(@Nullable CharSequence text) {
synchronized (viewLock) {
if (view != null) {
view.setText(text);
}
}
}
@Override
public int getSelection() {
synchronized (viewLock) {
return view != null ? view.getSelection() : 0;
}
}
@Override
public void setSelection(int selection) {
synchronized (viewLock) {
if (view != null) {
view.setSelection(selection);
}
}
}*/
@Override
@NotNull
public CalculatorEventData getLastEventData() {
return lastCalculatorEventData;
}
@Override
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData,
@NotNull CalculatorEventType calculatorEventType,
@Nullable Object data) {
if (calculatorEventType.isOfType(calculation_result, calculation_failed, calculation_cancelled)) {
if (calculatorEventData.isAfter(lastCalculatorEventData)) {
lastCalculatorEventData = calculatorEventData;
}
switch (calculatorEventType) {
case calculation_result:
processCalculationResult((CalculatorEvaluationEventData)calculatorEventData, (CalculatorOutput) data);
break;
case calculation_cancelled:
processCalculationCancelled((CalculatorEvaluationEventData)calculatorEventData);
break;
case calculation_failed:
processCalculationFailed((CalculatorEvaluationEventData)calculatorEventData, (CalculatorFailure) data);
break;
}
}
}
private void processCalculationFailed(@NotNull CalculatorEvaluationEventData calculatorEventData, @NotNull CalculatorFailure data) {
final CalculatorEvalException calculatorEvalException = data.getCalculationEvalException();
final String errorMessage;
if (calculatorEvalException != null) {
errorMessage = CalculatorMessages.getBundle().getString(CalculatorMessages.syntax_error);
} else {
final CalculatorParseException calculationParseException = data.getCalculationParseException();
if (calculationParseException != null) {
errorMessage = calculationParseException.getLocalizedMessage();
} else {
errorMessage = CalculatorMessages.getBundle().getString(CalculatorMessages.syntax_error);
}
}
this.setViewState(CalculatorDisplayViewStateImpl.newErrorState(calculatorEventData.getOperation(), errorMessage));
}
private void processCalculationCancelled(@NotNull CalculatorEvaluationEventData calculatorEventData) {
final String errorMessage = CalculatorMessages.getBundle().getString(CalculatorMessages.syntax_error);
this.setViewState(CalculatorDisplayViewStateImpl.newErrorState(calculatorEventData.getOperation(), errorMessage));
}
private void processCalculationResult(@NotNull CalculatorEvaluationEventData calculatorEventData, @NotNull CalculatorOutput data) {
final String stringResult = data.getStringResult();
this.setViewState(CalculatorDisplayViewStateImpl.newValidState(calculatorEventData.getOperation(), data.getResult(), stringResult, 0));
}
}

View File

@ -0,0 +1,16 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
/**
* User: serso
* Date: 9/20/12
* Time: 8:25 PM
*/
public interface CalculatorDisplayView {
void setState(@NotNull CalculatorDisplayViewState state);
@NotNull
CalculatorDisplayViewState getState();
}

View File

@ -0,0 +1,33 @@
package org.solovyev.android.calculator;
import jscl.math.Generic;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.jscl.JsclOperation;
/**
* User: serso
* Date: 9/20/12
* Time: 9:50 PM
*/
public interface CalculatorDisplayViewState {
@NotNull
String getText();
int getSelection();
@Nullable
Generic getResult();
boolean isValid();
@Nullable
String getErrorMessage();
@NotNull
JsclOperation getOperation();
@Nullable
String getStringResult();
}

View File

@ -0,0 +1,104 @@
package org.solovyev.android.calculator;
import jscl.math.Generic;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.common.text.StringUtils;
/**
* User: serso
* Date: 9/20/12
* Time: 9:50 PM
*/
public class CalculatorDisplayViewStateImpl implements CalculatorDisplayViewState {
@NotNull
private JsclOperation operation = JsclOperation.numeric;
@Nullable
private Generic result;
@Nullable
private String stringResult = "";
private boolean valid = true;
@Nullable
private String errorMessage;
private int selection = 0;
private CalculatorDisplayViewStateImpl() {
}
@NotNull
public static CalculatorDisplayViewState newDefaultInstance() {
return new CalculatorDisplayViewStateImpl();
}
@NotNull
public static CalculatorDisplayViewState newErrorState(@NotNull JsclOperation operation,
@NotNull String errorMessage) {
final CalculatorDisplayViewStateImpl calculatorDisplayState = new CalculatorDisplayViewStateImpl();
calculatorDisplayState.valid = false;
calculatorDisplayState.errorMessage = errorMessage;
calculatorDisplayState.operation = operation;
return calculatorDisplayState;
}
@NotNull
public static CalculatorDisplayViewState newValidState(@NotNull JsclOperation operation,
@Nullable Generic result,
@NotNull String stringResult,
int selection) {
final CalculatorDisplayViewStateImpl calculatorDisplayState = new CalculatorDisplayViewStateImpl();
calculatorDisplayState.valid = true;
calculatorDisplayState.result = result;
calculatorDisplayState.stringResult = stringResult;
calculatorDisplayState.operation = operation;
calculatorDisplayState.selection = selection;
return calculatorDisplayState;
}
@NotNull
@Override
public String getText() {
return StringUtils.getNotEmpty(isValid() ? stringResult : errorMessage, "");
}
@Override
public int getSelection() {
return selection;
}
@Nullable
@Override
public Generic getResult() {
return this.result;
}
@Override
public boolean isValid() {
return this.valid;
}
@Nullable
@Override
public String getErrorMessage() {
return this.errorMessage;
}
@Override
@Nullable
public String getStringResult() {
return stringResult;
}
@NotNull
@Override
public JsclOperation getOperation() {
return this.operation;
}
}

View File

@ -0,0 +1,18 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.jscl.JsclOperation;
/**
* User: serso
* Date: 9/20/12
* Time: 10:00 PM
*/
public interface CalculatorEvaluationEventData extends CalculatorEventData{
@NotNull
JsclOperation getOperation();
@NotNull
String getExpression();
}

View File

@ -0,0 +1,58 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.jscl.JsclOperation;
/**
* User: serso
* Date: 9/20/12
* Time: 10:01 PM
*/
public class CalculatorEvaluationEventDataImpl implements CalculatorEvaluationEventData {
@NotNull
private final CalculatorEventData calculatorEventData;
@NotNull
private final JsclOperation operation;
@NotNull
private final String expression;
public CalculatorEvaluationEventDataImpl(@NotNull CalculatorEventData calculatorEventData,
@NotNull JsclOperation operation,
@NotNull String expression) {
this.calculatorEventData = calculatorEventData;
this.operation = operation;
this.expression = expression;
}
@NotNull
@Override
public JsclOperation getOperation() {
return this.operation;
}
@NotNull
@Override
public String getExpression() {
return this.expression;
}
@Override
public long getEventId() {
return calculatorEventData.getEventId();
}
@Override
@Nullable
public Long getCalculationId() {
return calculatorEventData.getCalculationId();
}
@Override
public boolean isAfter(@NotNull CalculatorEventDataId calculatorEventDataId) {
return calculatorEventData.isAfter(calculatorEventDataId);
}
}

View File

@ -1,11 +1,10 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import org.jetbrains.annotations.Nullable; /**
* User: Solovyev_S
/** * Date: 20.09.12
* User: Solovyev_S * Time: 16:51
* Date: 20.09.12 */
* Time: 16:51 public interface CalculatorEventData extends CalculatorEventDataId {
*/
public interface CalculatorEventData extends CalculatorEventDataId { }
}

View File

@ -1,17 +1,20 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: Solovyev_S /**
* Date: 20.09.12 * User: Solovyev_S
* Time: 18:18 * Date: 20.09.12
*/ * Time: 18:18
public interface CalculatorEventDataId { */
public interface CalculatorEventDataId {
// the higher id => the later event
long getEventId(); // the higher id => the later event
long getEventId();
@Nullable
Long getCalculationId(); @Nullable
} Long getCalculationId();
boolean isAfter(@NotNull CalculatorEventDataId calculatorEventDataId);
}

View File

@ -1,61 +1,66 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
/** /**
* User: Solovyev_S * User: Solovyev_S
* Date: 20.09.12 * Date: 20.09.12
* Time: 18:18 * Time: 18:18
*/ */
public class CalculatorEventDataIdImpl implements CalculatorEventDataId { class CalculatorEventDataIdImpl implements CalculatorEventDataId {
private final long eventId; private final long eventId;
@Nullable @Nullable
private final Long calculationId; private final Long calculationId;
private CalculatorEventDataIdImpl(long id, private CalculatorEventDataIdImpl(long id,
@Nullable Long calculationId) { @Nullable Long calculationId) {
this.eventId = id; this.eventId = id;
this.calculationId = calculationId; this.calculationId = calculationId;
} }
@NotNull @NotNull
public static CalculatorEventDataId newInstance(long id, static CalculatorEventDataId newInstance(long id,
@Nullable Long calculationId) { @Nullable Long calculationId) {
return new CalculatorEventDataIdImpl(id, calculationId); return new CalculatorEventDataIdImpl(id, calculationId);
} }
@Override @Override
public long getEventId() { public long getEventId() {
return this.eventId; return this.eventId;
} }
@Nullable @Nullable
@Override @Override
public Long getCalculationId() { public Long getCalculationId() {
return this.calculationId; return this.calculationId;
} }
@Override @Override
public boolean equals(Object o) { public boolean isAfter(@NotNull CalculatorEventDataId calculatorEventDataId) {
if (this == o) return true; return this.eventId > calculatorEventDataId.getEventId();
if (!(o instanceof CalculatorEventDataIdImpl)) return false; }
CalculatorEventDataIdImpl that = (CalculatorEventDataIdImpl) o; @Override
public boolean equals(Object o) {
if (eventId != that.eventId) return false; if (this == o) return true;
if (calculationId != null ? !calculationId.equals(that.calculationId) : that.calculationId != null) if (!(o instanceof CalculatorEventDataIdImpl)) return false;
return false;
CalculatorEventDataIdImpl that = (CalculatorEventDataIdImpl) o;
return true;
} if (eventId != that.eventId) return false;
if (calculationId != null ? !calculationId.equals(that.calculationId) : that.calculationId != null)
@Override return false;
public int hashCode() {
int result = (int) (eventId ^ (eventId >>> 32)); return true;
result = 31 * result + (calculationId != null ? calculationId.hashCode() : 0); }
return result;
} @Override
} public int hashCode() {
int result = (int) (eventId ^ (eventId >>> 32));
result = 31 * result + (calculationId != null ? calculationId.hashCode() : 0);
return result;
}
}

View File

@ -1,47 +1,57 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
/** /**
* User: Solovyev_S * User: Solovyev_S
* Date: 20.09.12 * Date: 20.09.12
* Time: 16:54 * Time: 16:54
*/ */
class CalculatorEventDataImpl implements CalculatorEventData { class CalculatorEventDataImpl implements CalculatorEventData {
@NotNull @NotNull
private CalculatorEventDataId calculatorEventDataId; private CalculatorEventDataId calculatorEventDataId;
CalculatorEventDataImpl(@NotNull CalculatorEventDataId calculatorEventDataId) { private CalculatorEventDataImpl(@NotNull CalculatorEventDataId calculatorEventDataId) {
this.calculatorEventDataId = calculatorEventDataId; this.calculatorEventDataId = calculatorEventDataId;
} }
@Override @NotNull
public long getEventId() { public static CalculatorEventData newInstance(@NotNull CalculatorEventDataId calculatorEventDataId) {
return calculatorEventDataId.getEventId(); return new CalculatorEventDataImpl(calculatorEventDataId);
} }
@Override @Override
@Nullable public long getEventId() {
public Long getCalculationId() { return calculatorEventDataId.getEventId();
return calculatorEventDataId.getCalculationId(); }
}
@Override
@Override @Nullable
public boolean equals(Object o) { public Long getCalculationId() {
if (this == o) return true; return calculatorEventDataId.getCalculationId();
if (!(o instanceof CalculatorEventDataImpl)) return false; }
CalculatorEventDataImpl that = (CalculatorEventDataImpl) o; @Override
public boolean isAfter(@NotNull CalculatorEventDataId calculatorEventDataId) {
if (!calculatorEventDataId.equals(that.calculatorEventDataId)) return false; return this.calculatorEventDataId.isAfter(calculatorEventDataId);
}
return true;
} @Override
public boolean equals(Object o) {
@Override if (this == o) return true;
public int hashCode() { if (!(o instanceof CalculatorEventDataImpl)) return false;
return calculatorEventDataId.hashCode();
} CalculatorEventDataImpl that = (CalculatorEventDataImpl) o;
}
if (!calculatorEventDataId.equals(that.calculatorEventDataId)) return false;
return true;
}
@Override
public int hashCode() {
return calculatorEventDataId.hashCode();
}
}

View File

@ -1,9 +1,43 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
/** import org.jetbrains.annotations.NotNull;
* User: Solovyev_S
* Date: 20.09.12 /**
* Time: 16:40 * User: Solovyev_S
*/ * Date: 20.09.12
public enum CalculatorEventType { * Time: 16:40
} */
public enum CalculatorEventType {
/*
**********************************************************************
*
* org.solovyev.android.calculator.CalculatorEvaluationEventData
*
**********************************************************************
*/
// @NotNull org.solovyev.android.calculator.CalculatorInput
calculation_started,
// @NotNull org.solovyev.android.calculator.CalculatorOutput
calculation_result,
calculation_cancelled,
calculation_finished,
// @NotNull org.solovyev.android.calculator.CalculatorFailure
calculation_failed;
public boolean isOfType(@NotNull CalculatorEventType... types) {
for (CalculatorEventType type : types) {
if ( this == type ) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,21 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: serso
* Date: 9/20/12
* Time: 7:33 PM
*/
public interface CalculatorFailure {
@NotNull
Exception getException();
@Nullable
CalculatorParseException getCalculationParseException();
@Nullable
CalculatorEvalException getCalculationEvalException();
}

View File

@ -0,0 +1,34 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
/**
* User: serso
* Date: 9/20/12
* Time: 7:34 PM
*/
public class CalculatorFailureImpl implements CalculatorFailure {
@NotNull
private Exception exception;
public CalculatorFailureImpl(@NotNull Exception exception) {
this.exception = exception;
}
@NotNull
@Override
public Exception getException() {
return this.exception;
}
@Override
public CalculatorParseException getCalculationParseException() {
return exception instanceof CalculatorParseException ? (CalculatorParseException)exception : null;
}
@Override
public CalculatorEvalException getCalculationEvalException() {
return exception instanceof CalculatorEvalException ? (CalculatorEvalException)exception : null;
}
}

View File

@ -1,168 +1,209 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import jscl.AbstractJsclArithmeticException; import jscl.AbstractJsclArithmeticException;
import jscl.NumeralBaseException; import jscl.NumeralBaseException;
import jscl.math.Generic; import jscl.math.Generic;
import jscl.text.ParseInterruptedException; import jscl.text.ParseInterruptedException;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.jscl.JsclOperation; import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.calculator.text.TextProcessor; import org.solovyev.android.calculator.text.TextProcessor;
import org.solovyev.common.msg.MessageRegistry; import org.solovyev.common.msg.MessageRegistry;
import org.solovyev.common.msg.MessageType;
import java.util.List;
import java.util.concurrent.*; import java.util.List;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/** import java.util.concurrent.atomic.AtomicLong;
* User: Solovyev_S
* Date: 20.09.12 /**
* Time: 16:42 * User: Solovyev_S
*/ * Date: 20.09.12
public class CalculatorImpl implements Calculator { * Time: 16:42
*/
@NotNull public class CalculatorImpl implements Calculator {
private final CalculatorEventContainer calculatorEventContainer = new ListCalculatorEventContainer();
private static final long FIRST_ID = 0;
@NotNull
private static final Calculator instance = new CalculatorImpl(); @NotNull
private final CalculatorEventContainer calculatorEventContainer = new ListCalculatorEventContainer();
@NotNull
private final AtomicLong counter = new AtomicLong(0); @NotNull
private final AtomicLong counter = new AtomicLong(FIRST_ID);
@NotNull
private final Object lock = new Object(); @NotNull
private final Object lock = new Object();
@NotNull
private final TextProcessor<PreparedExpression, String> preprocessor = ToJsclTextProcessor.getInstance(); @NotNull
private final TextProcessor<PreparedExpression, String> preprocessor = ToJsclTextProcessor.getInstance();
@NotNull
private final Executor threadPoolExecutor = Executors.newFixedThreadPool(10); @NotNull
private final Executor threadPoolExecutor = Executors.newFixedThreadPool(10);
private CalculatorImpl() {
} public CalculatorImpl() {
}
@NotNull
public static Calculator getInstance() { @NotNull
return instance; private CalculatorEventDataId nextCalculatorEventDataId() {
} long eventId = counter.incrementAndGet();
return CalculatorEventDataIdImpl.newInstance(eventId, eventId);
@NotNull }
private CalculatorEventDataId nextCalculatorEventDataId() {
long eventId = counter.incrementAndGet(); @NotNull
return CalculatorEventDataIdImpl.newInstance(eventId, eventId); private CalculatorEventDataId nextEventDataId(@NotNull Long calculationId) {
} long eventId = counter.incrementAndGet();
return CalculatorEventDataIdImpl.newInstance(eventId, calculationId);
/* }
**********************************************************************
* /*
* CALCULATION **********************************************************************
* *
********************************************************************** * CALCULATION
*/ *
**********************************************************************
public void evaluate(@NotNull JsclOperation operation, */
@NotNull String expression) {
evaluate(operation, expression, null); @NotNull
} @Override
public CalculatorEventDataId createFirstEventDataId() {
public void evaluate(@NotNull final JsclOperation operation, return CalculatorEventDataIdImpl.newInstance(FIRST_ID, FIRST_ID);
@NotNull final String expression, }
@Nullable final MessageRegistry mr) {
@Override
final CalculatorEventDataId eventDataId = nextCalculatorEventDataId(); public void evaluate(@NotNull JsclOperation operation,
@NotNull String expression) {
threadPoolExecutor.execute(new Runnable() { evaluate(operation, expression, null);
@Override }
public void run() {
CalculatorImpl.this.evaluate(eventDataId, operation, expression, mr); @Override
} @NotNull
}); public CalculatorEventDataId evaluate(@NotNull final JsclOperation operation,
} @NotNull final String expression,
@Nullable final MessageRegistry mr) {
private void evaluate(@NotNull CalculatorEventDataId eventDataId,
@NotNull JsclOperation operation, final CalculatorEventDataId eventDataId = nextCalculatorEventDataId();
@NotNull String expression,
@Nullable MessageRegistry mr) { threadPoolExecutor.execute(new Runnable() {
synchronized (lock) { @Override
PreparedExpression preparedExpression = null; public void run() {
CalculatorImpl.this.evaluate(eventDataId.getCalculationId(), operation, expression, mr);
try { }
preparedExpression = preprocessor.process(expression); });
final String jsclExpression = preparedExpression.toString(); return eventDataId;
try { }
final Generic genericResult = operation.evaluateGeneric(jsclExpression); private void evaluate(@NotNull Long calculationId,
@NotNull JsclOperation operation,
// NOTE: toString() method must be called here as ArithmeticOperationException may occur in it (just to avoid later check!) @NotNull String expression,
genericResult.toString(); @Nullable MessageRegistry mr) {
synchronized (lock) {
//return new Result(operation.getFromProcessor().process(genericResult), operation, genericResult);
} catch (AbstractJsclArithmeticException e) { PreparedExpression preparedExpression = null;
handleException(eventDataId, operation, expression, mr, preparedExpression, null, new CalculatorEvalException(e, e, jsclExpression));
} fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_started, new CalculatorInputImpl(expression, operation));
} catch (ArithmeticException e) { try {
//final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_1, MessageType.error, CalculatorApplication.getInstance(), e.getMessage()); preparedExpression = preprocessor.process(expression);
handleException(operation, expression, mr, preparedExpression, new CalculatorParseException(jsclExpression, androidMessage));
} catch (StackOverflowError e) { final String jsclExpression = preparedExpression.toString();
//final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_2, MessageType.error, CalculatorApplication.getInstance());
handleException(eventDataId, operation, expression, mr, preparedExpression, new CalculatorParseException(e), null); try {
} catch (jscl.text.ParseException e) {
//System.out.println(e.getMessage()); final Generic result = operation.evaluateGeneric(jsclExpression);
handleException(eventDataId, operation, expression, mr, preparedExpression, new CalculatorParseException(e), null);
} catch (ParseInterruptedException e) { // NOTE: toString() method must be called here as ArithmeticOperationException may occur in it (just to avoid later check!)
// do nothing - we ourselves interrupt the calculations result.toString();
} catch (CalculatorParseException e) {
handleException(eventDataId, operation, expression, mr, preparedExpression, e, null); final CalculatorOutputImpl data = new CalculatorOutputImpl(operation.getFromProcessor().process(result), operation, result);
} fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_result, data);
}
} } catch (AbstractJsclArithmeticException e) {
handleException(calculationId, operation, expression, mr, new CalculatorEvalException(e, e, jsclExpression));
private void handleException(@NotNull CalculatorEventDataId eventDataId, }
@NotNull JsclOperation operation,
@NotNull String expression, } catch (ArithmeticException e) {
@Nullable MessageRegistry mr, handleException(calculationId, operation, expression, mr, preparedExpression, new CalculatorParseException(expression, new CalculatorMessage(CalculatorMessages.msg_001, MessageType.error, e.getMessage())));
@Nullable PreparedExpression preparedExpression, } catch (StackOverflowError e) {
@Nullable CalculatorParseException parseException, handleException(calculationId, operation, expression, mr, preparedExpression, new CalculatorParseException(expression, new CalculatorMessage(CalculatorMessages.msg_002, MessageType.error)));
@Nullable CalculatorEvalException evalException) { } catch (jscl.text.ParseException e) {
if (operation == JsclOperation.numeric && (preparedExpression != null && preparedExpression.isExistsUndefinedVar() || (evalException != null && evalException.getCause() instanceof NumeralBaseException))) { handleException(calculationId, operation, expression, mr, preparedExpression, new CalculatorParseException(e));
evaluate(eventDataId, JsclOperation.simplify, expression, mr); } catch (ParseInterruptedException e) {
}
// do nothing - we ourselves interrupt the calculations
if (parseException != null) { fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_cancelled, null);
throw parseException;
} else { } catch (CalculatorParseException e) {
throw evalException; handleException(calculationId, operation, expression, mr, preparedExpression, e);
} } finally {
} fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_finished, null);
}
/* }
********************************************************************** }
*
* EVENTS @NotNull
* private CalculatorEventData newCalculationEventData(@NotNull JsclOperation operation,
********************************************************************** @NotNull String expression,
*/ @NotNull Long calculationId) {
return new CalculatorEvaluationEventDataImpl(CalculatorEventDataImpl.newInstance(nextEventDataId(calculationId)), operation, expression);
@Override }
public void addCalculatorEventListener(@NotNull CalculatorEventListener calculatorEventListener) {
calculatorEventContainer.addCalculatorEventListener(calculatorEventListener); private void handleException(@NotNull Long calculationId,
} @NotNull JsclOperation operation,
@NotNull String expression,
@Override @Nullable MessageRegistry mr,
public void removeCalculatorEventListener(@NotNull CalculatorEventListener calculatorEventListener) { @Nullable PreparedExpression preparedExpression,
calculatorEventContainer.removeCalculatorEventListener(calculatorEventListener); @NotNull CalculatorParseException parseException) {
}
if (operation == JsclOperation.numeric
@Override && preparedExpression != null
public void fireCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) { && preparedExpression.isExistsUndefinedVar()) {
calculatorEventContainer.fireCalculatorEvent(calculatorEventData, calculatorEventType, data);
} evaluate(calculationId, JsclOperation.simplify, expression, mr);
@Override }
public void fireCalculatorEvents(@NotNull List<CalculatorEvent> calculatorEvents) {
calculatorEventContainer.fireCalculatorEvents(calculatorEvents); fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_failed, new CalculatorFailureImpl(parseException));
} }
}
private void handleException(@NotNull Long calculationId,
@NotNull JsclOperation operation,
@NotNull String expression,
@Nullable MessageRegistry mr,
@NotNull CalculatorEvalException evalException) {
if (operation == JsclOperation.numeric && evalException.getCause() instanceof NumeralBaseException) {
evaluate(calculationId, JsclOperation.simplify, expression, mr);
}
fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_failed, new CalculatorFailureImpl(evalException));
}
/*
**********************************************************************
*
* EVENTS
*
**********************************************************************
*/
@Override
public void addCalculatorEventListener(@NotNull CalculatorEventListener calculatorEventListener) {
calculatorEventContainer.addCalculatorEventListener(calculatorEventListener);
}
@Override
public void removeCalculatorEventListener(@NotNull CalculatorEventListener calculatorEventListener) {
calculatorEventContainer.removeCalculatorEventListener(calculatorEventListener);
}
@Override
public void fireCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) {
calculatorEventContainer.fireCalculatorEvent(calculatorEventData, calculatorEventType, data);
}
@Override
public void fireCalculatorEvents(@NotNull List<CalculatorEvent> calculatorEvents) {
calculatorEventContainer.fireCalculatorEvents(calculatorEvents);
}
}

View File

@ -0,0 +1,18 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.jscl.JsclOperation;
/**
* User: serso
* Date: 9/20/12
* Time: 7:25 PM
*/
public interface CalculatorInput {
@NotNull
String getExpression();
@NotNull
JsclOperation getOperation();
}

View File

@ -0,0 +1,35 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.jscl.JsclOperation;
/**
* User: serso
* Date: 9/20/12
* Time: 7:26 PM
*/
public class CalculatorInputImpl implements CalculatorInput {
@NotNull
private String expression;
@NotNull
private JsclOperation operation;
public CalculatorInputImpl(@NotNull String expression, @NotNull JsclOperation operation) {
this.expression = expression;
this.operation = operation;
}
@Override
@NotNull
public String getExpression() {
return expression;
}
@Override
@NotNull
public JsclOperation getOperation() {
return operation;
}
}

View File

@ -1,16 +1,22 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
/** /**
* User: Solovyev_S * User: Solovyev_S
* Date: 20.09.12 * Date: 20.09.12
* Time: 12:45 * Time: 12:45
*/ */
public interface CalculatorLocator { public interface CalculatorLocator {
@NotNull @NotNull
JCalculatorEngine getCalculatorEngine(); JCalculatorEngine getCalculatorEngine();
void setCalculatorEngine(@NotNull JCalculatorEngine calculatorEngine); @NotNull
} Calculator getCalculator();
@NotNull
CalculatorDisplay getCalculatorDisplay();
void setCalculatorEngine(@NotNull JCalculatorEngine calculatorEngine);
}

View File

@ -1,36 +1,54 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
/** /**
* User: Solovyev_S * User: Solovyev_S
* Date: 20.09.12 * Date: 20.09.12
* Time: 12:45 * Time: 12:45
*/ */
public class CalculatorLocatorImpl implements CalculatorLocator { public class CalculatorLocatorImpl implements CalculatorLocator {
@NotNull @NotNull
private JCalculatorEngine calculatorEngine; private JCalculatorEngine calculatorEngine;
@NotNull @NotNull
private static final CalculatorLocator instance = new CalculatorLocatorImpl(); private CalculatorDisplay calculatorDisplay = new CalculatorDisplayImpl();
private CalculatorLocatorImpl() { @NotNull
} private Calculator calculator = new CalculatorImpl();
@NotNull @NotNull
public static CalculatorLocator getInstance() { private static final CalculatorLocator instance = new CalculatorLocatorImpl();
return instance;
} private CalculatorLocatorImpl() {
}
@NotNull
@Override @NotNull
public JCalculatorEngine getCalculatorEngine() { public static CalculatorLocator getInstance() {
return calculatorEngine; return instance;
} }
@Override @NotNull
public void setCalculatorEngine(@NotNull JCalculatorEngine calculatorEngine) { @Override
this.calculatorEngine = calculatorEngine; public JCalculatorEngine getCalculatorEngine() {
} return calculatorEngine;
} }
@NotNull
@Override
public Calculator getCalculator() {
return this.calculator;
}
@Override
public void setCalculatorEngine(@NotNull JCalculatorEngine calculatorEngine) {
this.calculatorEngine = calculatorEngine;
}
@Override
@NotNull
public CalculatorDisplay getCalculatorDisplay() {
return calculatorDisplay;
}
}

View File

@ -0,0 +1,31 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import org.solovyev.common.msg.AbstractMessage;
import org.solovyev.common.msg.MessageType;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;
/**
* User: serso
* Date: 9/20/12
* Time: 8:06 PM
*/
public class CalculatorMessage extends AbstractMessage {
protected CalculatorMessage(@NotNull String messageCode, @NotNull MessageType messageType, @org.jetbrains.annotations.Nullable Object... parameters) {
super(messageCode, messageType, parameters);
}
protected CalculatorMessage(@NotNull String messageCode, @NotNull MessageType messageType, @NotNull List<?> parameters) {
super(messageCode, messageType, parameters);
}
@Override
protected String getMessagePattern(@NotNull Locale locale) {
final ResourceBundle rb = CalculatorMessages.getBundle(locale);
return rb.getString(getMessageCode());
}
}

View File

@ -0,0 +1,69 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
/**
* User: serso
* Date: 9/20/12
* Time: 8:10 PM
*/
public final class CalculatorMessages {
@NotNull
private static final Map<Locale, ResourceBundle> bundlesByLocale = new HashMap<Locale, ResourceBundle>();
private CalculatorMessages() {
throw new AssertionError();
}
@NotNull
public static ResourceBundle getBundle() {
return getBundle(Locale.getDefault());
}
@NotNull
public static ResourceBundle getBundle(@NotNull Locale locale) {
synchronized (bundlesByLocale) {
ResourceBundle result = bundlesByLocale.get(locale);
if (result == null) {
result = ResourceBundle.getBundle("org/solovyev/android/calculator/messages", locale);
bundlesByLocale.put(locale, result);
}
return result;
}
}
/* Arithmetic error occurred: {0} */
@NotNull
public static final String msg_001 = "msg_1";
/* Too complex expression*/
@NotNull
public static final String msg_002 = "msg_2";
/* Too long execution time - check the expression*/
@NotNull
public static final String msg_003 = "msg_3";
/* Evaluation was cancelled*/
@NotNull
public static final String msg_004 = "msg_4";
/* No parameters are specified for function: {0}*/
@NotNull
public static final String msg_005 = "msg_5";
/* Infinite loop is detected in expression*/
@NotNull
public static final String msg_006 = "msg_6";
/* Error */
@NotNull
public static final String syntax_error = "syntax_error";
}

View File

@ -0,0 +1,22 @@
package org.solovyev.android.calculator;
import jscl.math.Generic;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.jscl.JsclOperation;
/**
* User: serso
* Date: 9/20/12
* Time: 7:29 PM
*/
public interface CalculatorOutput {
@NotNull
String getStringResult();
@NotNull
JsclOperation getOperation();
@NotNull
Generic getResult();
}

View File

@ -0,0 +1,46 @@
package org.solovyev.android.calculator;
import jscl.math.Generic;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.jscl.JsclOperation;
/**
* User: serso
* Date: 9/20/12
* Time: 7:28 PM
*/
public class CalculatorOutputImpl implements CalculatorOutput {
@NotNull
private Generic result;
@NotNull
private String stringResult;
@NotNull
private JsclOperation operation;
public CalculatorOutputImpl(@NotNull String stringResult, @NotNull JsclOperation operation, @NotNull Generic result) {
this.stringResult = stringResult;
this.operation = operation;
this.result = result;
}
@Override
@NotNull
public String getStringResult() {
return stringResult;
}
@Override
@NotNull
public JsclOperation getOperation() {
return operation;
}
@Override
@NotNull
public Generic getResult() {
return result;
}
}

View File

@ -1,40 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator;
import jscl.math.Generic;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.Editor;
import org.solovyev.android.calculator.jscl.JsclOperation;
/**
* User: serso
* Date: 12/17/11
* Time: 9:45 PM
*/
public interface JCalculatorDisplay extends Editor{
boolean isValid();
void setValid(boolean valid);
@Nullable
String getErrorMessage();
void setErrorMessage(@Nullable String errorMessage);
void setJsclOperation(@NotNull JsclOperation jsclOperation);
@NotNull
JsclOperation getJsclOperation();
void setGenericResult(@Nullable Generic genericResult);
@Nullable
Generic getGenericResult();
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,8 @@
msg_1=Arithmetic error occurred: {0}
msg_2=Too complex expression
msg_3=Too long execution time - check the expression
msg_4=Evaluation was cancelled
msg_5=No parameters are specified for function: {0}
msg_6=Infinite loop is detected in expression
syntax_error=Error

View File

@ -6,7 +6,7 @@
~ or visit http://se.solovyev.org ~ or visit http://se.solovyev.org
--> -->
<org.solovyev.android.calculator.CalculatorDisplay <org.solovyev.android.calculator.AndroidCalculatorDisplayView
xmlns:a="http://schemas.android.com/apk/res/android" xmlns:a="http://schemas.android.com/apk/res/android"
a:id="@+id/calculatorDisplay" a:id="@+id/calculatorDisplay"
style="@style/display_style" style="@style/display_style"

View File

@ -1,347 +1,316 @@
/* /*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev. * Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com * For more information, please, contact se.solovyev@gmail.com
*/ */
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import android.content.Context; import android.content.Context;
import android.graphics.Color; import android.graphics.Color;
import android.text.Html; import android.text.Html;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log; import android.util.Log;
import jscl.NumeralBase; import jscl.NumeralBase;
import jscl.math.Generic; import jscl.math.Generic;
import jscl.math.function.Constant; import jscl.math.function.Constant;
import jscl.math.function.IConstant; import jscl.math.function.IConstant;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.calculator.jscl.JsclOperation; import org.solovyev.android.calculator.model.CalculatorEngine;
import org.solovyev.android.calculator.model.CalculatorEngine; import org.solovyev.android.calculator.text.TextProcessor;
import org.solovyev.android.calculator.text.TextProcessor; import org.solovyev.android.calculator.view.NumeralBaseConverterDialog;
import org.solovyev.android.calculator.ToJsclTextProcessor; import org.solovyev.android.calculator.view.TextHighlighter;
import org.solovyev.android.calculator.view.NumeralBaseConverterDialog; import org.solovyev.android.calculator.view.UnitConverterViewBuilder;
import org.solovyev.android.calculator.view.TextHighlighter; import org.solovyev.android.menu.AMenuItem;
import org.solovyev.android.calculator.view.UnitConverterViewBuilder; import org.solovyev.android.menu.LabeledMenuItem;
import org.solovyev.android.menu.AMenuItem; import org.solovyev.android.view.AutoResizeTextView;
import org.solovyev.android.menu.LabeledMenuItem; import org.solovyev.common.collections.CollectionsUtils;
import org.solovyev.android.view.AutoResizeTextView; import org.solovyev.common.text.StringUtils;
import org.solovyev.common.collections.CollectionsUtils;
import org.solovyev.common.text.StringUtils; import java.util.HashSet;
import java.util.Set;
import java.util.HashSet;
import java.util.Set; /**
* User: serso
/** * Date: 9/17/11
* User: serso * Time: 10:58 PM
* Date: 9/17/11 */
* Time: 10:58 PM public class AndroidCalculatorDisplayView extends AutoResizeTextView implements CalculatorDisplayView {
*/
public class CalculatorDisplay extends AutoResizeTextView implements JCalculatorDisplay { private static enum ConversionMenuItem implements AMenuItem<CalculatorDisplayView> {
convert_to_bin(NumeralBase.bin),
private static enum ConversionMenuItem implements AMenuItem<CalculatorDisplay> { convert_to_dec(NumeralBase.dec),
convert_to_bin(NumeralBase.bin), convert_to_hex(NumeralBase.hex);
convert_to_dec(NumeralBase.dec),
convert_to_hex(NumeralBase.hex); @NotNull
private final NumeralBase toNumeralBase;
@NotNull
private final NumeralBase toNumeralBase; private ConversionMenuItem(@NotNull NumeralBase toNumeralBase) {
this.toNumeralBase = toNumeralBase;
private ConversionMenuItem(@NotNull NumeralBase toNumeralBase) { }
this.toNumeralBase = toNumeralBase;
} protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
boolean result = false;
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
boolean result = false; if (operation == JsclOperation.numeric) {
if (generic.getConstants().isEmpty()) {
if (operation == JsclOperation.numeric) { try {
if (generic.getConstants().isEmpty()) { convert(generic);
try {
convert(generic); // conversion possible => return true
result = true;
// conversion possible => return true
result = true; } catch (UnitConverterViewBuilder.ConversionException e) {
// conversion is not possible => return false
} catch (UnitConverterViewBuilder.ConversionException e) { }
// conversion is not possible => return false }
} }
}
} return result;
}
return result;
} @Override
public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) {
@Override final NumeralBase fromNumeralBase = CalculatorEngine.instance.getEngine().getNumeralBase();
public void onClick(@NotNull CalculatorDisplay data, @NotNull Context context) {
final NumeralBase fromNumeralBase = CalculatorEngine.instance.getEngine().getNumeralBase(); final Generic lastResult = CalculatorLocatorImpl.getInstance().getCalculatorDisplay().getViewState().getResult();
String to; if (lastResult != null) {
try { String to;
to = convert(data.getGenericResult()); try {
to = convert(lastResult);
// add prefix
if (fromNumeralBase != toNumeralBase) { // add prefix
to = toNumeralBase.getJsclPrefix() + to; if (fromNumeralBase != toNumeralBase) {
} to = toNumeralBase.getJsclPrefix() + to;
} catch (UnitConverterViewBuilder.ConversionException e) { }
to = context.getString(R.string.c_error); } catch (UnitConverterViewBuilder.ConversionException e) {
} to = context.getString(R.string.c_error);
}
data.setText(to);
data.redraw(); data.setText(to);
} //data.redraw();
}
@NotNull }
private String convert(@NotNull Generic generic) throws UnitConverterViewBuilder.ConversionException {
final NumeralBase fromNumeralBase = CalculatorEngine.instance.getEngine().getNumeralBase(); @NotNull
private String convert(@NotNull Generic generic) throws UnitConverterViewBuilder.ConversionException {
if (fromNumeralBase != toNumeralBase) { final NumeralBase fromNumeralBase = CalculatorEngine.instance.getEngine().getNumeralBase();
String from = generic.toString();
if (!StringUtils.isEmpty(from)) { if (fromNumeralBase != toNumeralBase) {
try { String from = generic.toString();
from = ToJsclTextProcessor.getInstance().process(from).getExpression(); if (!StringUtils.isEmpty(from)) {
} catch (CalculatorParseException e) { try {
// ok, problems while processing occurred 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(); 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) { public static enum MenuItem implements LabeledMenuItem<CalculatorDisplayView> {
@Override
public void onClick(@NotNull CalculatorDisplay data, @NotNull Context context) { copy(R.string.c_copy) {
CalculatorModel.copyResult(context, data); @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 CalculatorDisplay data, @NotNull Context context) { convert_to_bin(R.string.convert_to_bin) {
ConversionMenuItem.convert_to_bin.onClick(data, context); @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); @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) { convert_to_dec(R.string.convert_to_dec) {
ConversionMenuItem.convert_to_dec.onClick(data, context); @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); @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) { convert_to_hex(R.string.convert_to_hex) {
ConversionMenuItem.convert_to_hex.onClick(data, context); @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); @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) { convert(R.string.c_convert) {
new NumeralBaseConverterDialog(data.getGenericResult().toString()).show(context); @Override
} public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) {
final Generic result = data.getState().getResult();
@Override if (result != null) {
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { new NumeralBaseConverterDialog(result.toString()).show(context);
return operation == JsclOperation.numeric && generic.getConstants().isEmpty(); }
} }
},
@Override
plot(R.string.c_plot) { protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
@Override return operation == JsclOperation.numeric && generic.getConstants().isEmpty();
public void onClick(@NotNull CalculatorDisplay data, @NotNull Context context) { }
final Generic generic = data.getGenericResult(); },
assert generic != null;
plot(R.string.c_plot) {
final Constant constant = CollectionsUtils.getFirstCollectionElement(getNotSystemConstants(generic)); @Override
assert constant != null; public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) {
CalculatorActivityLauncher.plotGraph(context, generic, constant); final Generic generic = data.getState().getResult();
} assert generic != null;
@Override final Constant constant = CollectionsUtils.getFirstCollectionElement(getNotSystemConstants(generic));
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { assert constant != null;
boolean result = false; CalculatorActivityLauncher.plotGraph(context, generic, constant);
}
if (operation == JsclOperation.simplify) {
if (getNotSystemConstants(generic).size() == 1) { @Override
result = true; protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
} boolean result = false;
}
if (operation == JsclOperation.simplify) {
return result; if (getNotSystemConstants(generic).size() == 1) {
} result = true;
}
@NotNull }
private Set<Constant> getNotSystemConstants(@NotNull Generic generic) {
final Set<Constant> notSystemConstants = new HashSet<Constant>(); return result;
}
for (Constant constant : generic.getConstants()) {
IConstant var = CalculatorEngine.instance.getVarsRegistry().get(constant.getName()); @NotNull
if (var != null && !var.isSystem() && !var.isDefined()) { private Set<Constant> getNotSystemConstants(@NotNull Generic generic) {
notSystemConstants.add(constant); final Set<Constant> notSystemConstants = new HashSet<Constant>();
}
} for (Constant constant : generic.getConstants()) {
IConstant var = CalculatorEngine.instance.getVarsRegistry().get(constant.getName());
return notSystemConstants; if (var != null && !var.isSystem() && !var.isDefined()) {
} notSystemConstants.add(constant);
}; }
}
private final int captionId;
return notSystemConstants;
MenuItem(int captionId) { }
this.captionId = captionId; };
}
private final int captionId;
public final boolean isItemVisible(@NotNull CalculatorDisplay display) {
//noinspection ConstantConditions MenuItem(int captionId) {
return display.isValid() && display.getGenericResult() != null && isItemVisibleFor(display.getGenericResult(), display.getJsclOperation()); this.captionId = captionId;
} }
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { public final boolean isItemVisible(@NotNull CalculatorDisplayViewState displayViewState) {
return true; //noinspection ConstantConditions
} return displayViewState.isValid() && displayViewState.getResult() != null && isItemVisibleFor(displayViewState.getResult(), displayViewState.getOperation());
}
@NotNull
@Override protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
public String getCaption(@NotNull Context context) { return true;
return context.getString(captionId); }
}
} @NotNull
@Override
private boolean valid = true; public String getCaption(@NotNull Context context) {
return context.getString(captionId);
@Nullable }
private String errorMessage; }
@NotNull @NotNull
private JsclOperation jsclOperation = JsclOperation.numeric; private CalculatorDisplayViewState state = CalculatorDisplayViewStateImpl.newDefaultInstance();
@NotNull @NotNull
private final static TextProcessor<TextHighlighter.Result, String> textHighlighter = new TextHighlighter(Color.WHITE, false, CalculatorEngine.instance.getEngine()); private final static TextProcessor<TextHighlighter.Result, String> textHighlighter = new TextHighlighter(Color.WHITE, false, CalculatorEngine.instance.getEngine());
@Nullable public AndroidCalculatorDisplayView(Context context) {
private Generic genericResult; super(context);
}
public CalculatorDisplay(Context context) {
super(context); public AndroidCalculatorDisplayView(Context context, AttributeSet attrs) {
} super(context, attrs);
}
public CalculatorDisplay(Context context, AttributeSet attrs) {
super(context, attrs); public AndroidCalculatorDisplayView(Context context, AttributeSet attrs, int defStyle) {
} super(context, attrs, defStyle);
}
public CalculatorDisplay(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle); public boolean isValid() {
} return this.state.isValid();
}
@Override
public boolean isValid() {
return valid; @Override
} public void setState(@NotNull CalculatorDisplayViewState state) {
this.state = state;
@Override if ( state.isValid() ) {
public void setValid(boolean valid) { setTextColor(getResources().getColor(R.color.default_text_color));
this.valid = valid; setText(state.getStringResult());
if (valid) { } else {
errorMessage = null; setTextColor(getResources().getColor(R.color.display_error_text_color));
setTextColor(getResources().getColor(R.color.default_text_color)); setText(state.getErrorMessage());
} else { }
setTextColor(getResources().getColor(R.color.display_error_text_color)); }
}
} @NotNull
@Override
@Override public CalculatorDisplayViewState getState() {
@Nullable return this.state;
public String getErrorMessage() { }
return errorMessage;
} @Override
public void setText(CharSequence text, BufferType type) {
@Override super.setText(text, type);
public void setErrorMessage(@Nullable String errorMessage) { }
this.errorMessage = errorMessage;
} public synchronized void redraw() {
if (isValid()) {
@Override String text = getText().toString();
public void setJsclOperation(@NotNull JsclOperation jsclOperation) {
this.jsclOperation = jsclOperation; Log.d(this.getClass().getName(), text);
}
try {
@Override TextHighlighter.Result result = textHighlighter.process(text);
@NotNull text = result.toString();
public JsclOperation getJsclOperation() { } catch (CalculatorParseException e) {
return jsclOperation; Log.e(this.getClass().getName(), e.getMessage(), e);
} }
@Override Log.d(this.getClass().getName(), text);
public void setText(CharSequence text, BufferType type) { super.setText(Html.fromHtml(text), BufferType.EDITABLE);
super.setText(text, type); }
setValid(true); // todo serso: think where to move it (keep in mind org.solovyev.android.view.AutoResizeTextView.resetTextSize())
} setAddEllipsis(false);
setMinTextSize(10);
public synchronized void redraw() { resizeText();
if (isValid()) { }
String text = getText().toString();
@Override
Log.d(this.getClass().getName(), text); public int getSelection() {
return this.getSelectionStart();
try { }
TextHighlighter.Result result = textHighlighter.process(text);
text = result.toString(); @Override
} catch (CalculatorParseException e) { public void setSelection(int selection) {
Log.e(this.getClass().getName(), e.getMessage(), e); // not supported by TextView
} }
}
Log.d(this.getClass().getName(), text);
super.setText(Html.fromHtml(text), BufferType.EDITABLE);
}
// todo serso: think where to move it (keep in mind org.solovyev.android.view.AutoResizeTextView.resetTextSize())
setAddEllipsis(false);
setMinTextSize(10);
resizeText();
}
@Override
public void setGenericResult(@Nullable Generic genericResult) {
this.genericResult = genericResult;
}
@Override
@Nullable
public Generic getGenericResult() {
return genericResult;
}
@Override
public int getSelection() {
return this.getSelectionStart();
}
@Override
public void setSelection(int selection) {
// not supported by TextView
}
}

View File

@ -1,409 +1,412 @@
/* /*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev. * Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com * For more information, please, contact se.solovyev@gmail.com
*/ */
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Handler; import android.os.Handler;
import android.text.ClipboardManager; import android.text.ClipboardManager;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.widget.EditText; import android.widget.EditText;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.solovyev.android.CursorControl; import org.solovyev.android.CursorControl;
import org.solovyev.android.calculator.history.AndroidCalculatorHistoryImpl; import org.solovyev.android.calculator.history.AndroidCalculatorHistoryImpl;
import org.solovyev.android.calculator.history.CalculatorHistoryState; import org.solovyev.android.calculator.history.CalculatorHistoryState;
import org.solovyev.android.calculator.history.TextViewEditorAdapter; import org.solovyev.android.calculator.history.TextViewEditorAdapter;
import org.solovyev.android.calculator.jscl.JsclOperation; import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.calculator.math.MathType; import org.solovyev.android.calculator.math.MathType;
import org.solovyev.android.calculator.model.CalculatorEngine; import org.solovyev.android.calculator.model.CalculatorEngine;
import org.solovyev.android.history.HistoryControl; import org.solovyev.android.history.HistoryControl;
import org.solovyev.android.menu.AMenuBuilder; import org.solovyev.android.menu.AMenuBuilder;
import org.solovyev.android.menu.MenuImpl; import org.solovyev.android.menu.MenuImpl;
import org.solovyev.common.MutableObject; import org.solovyev.common.MutableObject;
import org.solovyev.common.history.HistoryAction; import org.solovyev.common.history.HistoryAction;
import org.solovyev.common.msg.Message; import org.solovyev.common.msg.Message;
import org.solovyev.common.text.StringUtils; import org.solovyev.common.text.StringUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* User: serso * User: serso
* Date: 9/12/11 * Date: 9/12/11
* Time: 11:15 PM * Time: 11:15 PM
*/ */
public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorHistoryState>, CalculatorEngineControl { public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorHistoryState>, CalculatorEngineControl {
instance; instance;
// millis to wait before evaluation after user edit action // millis to wait before evaluation after user edit action
public static final int EVAL_DELAY_MILLIS = 0; public static final int EVAL_DELAY_MILLIS = 0;
@NotNull @NotNull
private CalculatorEditor editor; private CalculatorEditor editor;
@NotNull @NotNull
private CalculatorDisplay display; private AndroidCalculatorDisplayView display;
@NotNull @NotNull
private CalculatorEngine calculatorEngine; private CalculatorEngine calculatorEngine;
public CalculatorModel init(@NotNull final Activity activity, @NotNull SharedPreferences preferences, @NotNull CalculatorEngine calculator) { public CalculatorModel init(@NotNull final Activity activity, @NotNull SharedPreferences preferences, @NotNull CalculatorEngine calculator) {
Log.d(this.getClass().getName(), "CalculatorModel initialization with activity: " + activity); Log.d(this.getClass().getName(), "CalculatorModel initialization with activity: " + activity);
this.calculatorEngine = calculator; this.calculatorEngine = calculator;
this.editor = (CalculatorEditor) activity.findViewById(R.id.calculatorEditor); this.editor = (CalculatorEditor) activity.findViewById(R.id.calculatorEditor);
this.editor.init(preferences); this.editor.init(preferences);
preferences.registerOnSharedPreferenceChangeListener(editor); preferences.registerOnSharedPreferenceChangeListener(editor);
this.display = (CalculatorDisplay) activity.findViewById(R.id.calculatorDisplay); this.display = (AndroidCalculatorDisplayView) activity.findViewById(R.id.calculatorDisplay);
this.display.setOnClickListener(new CalculatorDisplayOnClickListener(activity)); this.display.setOnClickListener(new CalculatorDisplayOnClickListener(activity));
final CalculatorHistoryState lastState = AndroidCalculatorHistoryImpl.instance.getLastHistoryState(); final CalculatorHistoryState lastState = AndroidCalculatorHistoryImpl.instance.getLastHistoryState();
if (lastState == null) { if (lastState == null) {
saveHistoryState(); saveHistoryState();
} else { } else {
setCurrentHistoryState(lastState); setCurrentHistoryState(lastState);
} }
return this; return this;
} }
private static void showEvaluationError(@NotNull Activity activity, @NotNull final String errorMessage) { private static void showEvaluationError(@NotNull Activity activity, @NotNull final String errorMessage) {
final LayoutInflater layoutInflater = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE); final LayoutInflater layoutInflater = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
final View errorMessageView = layoutInflater.inflate(R.layout.display_error_message, null); final View errorMessageView = layoutInflater.inflate(R.layout.display_error_message, null);
((TextView) errorMessageView.findViewById(R.id.error_message_text_view)).setText(errorMessage); ((TextView) errorMessageView.findViewById(R.id.error_message_text_view)).setText(errorMessage);
final AlertDialog.Builder builder = new AlertDialog.Builder(activity) final AlertDialog.Builder builder = new AlertDialog.Builder(activity)
.setPositiveButton(R.string.c_cancel, null) .setPositiveButton(R.string.c_cancel, null)
.setView(errorMessageView); .setView(errorMessageView);
builder.create().show(); builder.create().show();
} }
public void copyResult(@NotNull Context context) { public void copyResult(@NotNull Context context) {
copyResult(context, display); copyResult(context, display);
} }
public static void copyResult(@NotNull Context context, @NotNull final CalculatorDisplay display) { public static void copyResult(@NotNull Context context, @NotNull final CalculatorDisplayView display) {
if (display.isValid()) { final CalculatorDisplayViewState displayViewState = display.getState();
final CharSequence text = display.getText(); if (displayViewState.isValid()) {
if (!StringUtils.isEmpty(text)) { final CharSequence text = display.getText();
final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE); if (!StringUtils.isEmpty(text)) {
clipboard.setText(text.toString()); final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE);
Toast.makeText(context, context.getText(R.string.c_result_copied), Toast.LENGTH_SHORT).show(); 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()); private void saveHistoryState() {
} AndroidCalculatorHistoryImpl.instance.addState(getCurrentHistoryState());
}
public void setCursorOnStart() {
editor.setSelection(0); public void setCursorOnStart() {
} editor.setSelection(0);
}
public void setCursorOnEnd() {
editor.setSelection(editor.getText().length()); public void setCursorOnEnd() {
} editor.setSelection(editor.getText().length());
}
public void moveCursorLeft() {
if (editor.getSelectionStart() > 0) { public void moveCursorLeft() {
editor.setSelection(editor.getSelectionStart() - 1); if (editor.getSelectionStart() > 0) {
} editor.setSelection(editor.getSelectionStart() - 1);
} }
}
public void moveCursorRight() {
if (editor.getSelectionStart() < editor.getText().length()) { public void moveCursorRight() {
editor.setSelection(editor.getSelectionStart() + 1); 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) {
} 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) {
} 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(); 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(), "Editor state changed before '" + editorStateBefore + "'");
//Log.d(CalculatorModel.class.getName(), "Doing text operation" + StringUtils.fromStackTrace(Thread.currentThread().getStackTrace())); 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)) { final String editorStateAfter = this.editor.getText().toString();
if (forceEval ||!editorStateBefore.equals(editorStateAfter)) {
editor.redraw();
editor.redraw();
evaluate(delayEvaluate, editorStateAfter, jsclOperation, null);
} evaluate(delayEvaluate, editorStateAfter, jsclOperation, null);
} }
}
@NotNull
private final static MutableObject<Runnable> pendingOperation = new MutableObject<Runnable>(); @NotNull
private final static MutableObject<Runnable> pendingOperation = new MutableObject<Runnable>();
private void evaluate(boolean delayEvaluate,
@NotNull final String expression, private void evaluate(boolean delayEvaluate,
@NotNull final JsclOperation operation, @NotNull final String expression,
@Nullable CalculatorHistoryState historyState) { @NotNull final JsclOperation operation,
@Nullable CalculatorHistoryState historyState) {
final CalculatorHistoryState localHistoryState;
if (historyState == null) { final CalculatorHistoryState localHistoryState;
//this.display.setText(""); if (historyState == null) {
localHistoryState = getCurrentHistoryState(); //this.display.setText("");
} else { localHistoryState = getCurrentHistoryState();
this.display.setText(historyState.getDisplayState().getEditorState().getText()); } else {
localHistoryState = historyState; this.display.setText(historyState.getDisplayState().getEditorState().getText());
} localHistoryState = historyState;
}
pendingOperation.setObject(new Runnable() {
@Override pendingOperation.setObject(new Runnable() {
public void run() { @Override
// allow only one runner at one time public void run() {
synchronized (pendingOperation) { // allow only one runner at one time
//lock all operations with history synchronized (pendingOperation) {
if (pendingOperation.getObject() == this) { //lock all operations with history
// actually nothing shall be logged while text operations are done if (pendingOperation.getObject() == this) {
evaluate(expression, operation, 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) if (pendingOperation.getObject() == this) {
pendingOperation.setObject(null); // todo serso: of course there is small probability that someone will set pendingOperation after if statement but before .setObject(null)
localHistoryState.setDisplayState(getCurrentHistoryState().getDisplayState()); pendingOperation.setObject(null);
} localHistoryState.setDisplayState(getCurrentHistoryState().getDisplayState());
} }
} }
} }
}); }
});
if (delayEvaluate) {
if (historyState == null) { if (delayEvaluate) {
AndroidCalculatorHistoryImpl.instance.addState(localHistoryState); 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); // todo serso: this is not correct - operation is processing still in the same thread
} else { new Handler().postDelayed(pendingOperation.getObject(), EVAL_DELAY_MILLIS);
pendingOperation.getObject().run(); } else {
if (historyState == null) { pendingOperation.getObject().run();
AndroidCalculatorHistoryImpl.instance.addState(localHistoryState); if (historyState == null) {
} AndroidCalculatorHistoryImpl.instance.addState(localHistoryState);
} }
} }
}
@Override
public void evaluate() { @Override
evaluate(false, this.editor.getText().toString(), JsclOperation.numeric, null); 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); public void evaluate(@NotNull JsclOperation operation) {
} evaluate(false, this.editor.getText().toString(), operation, null);
}
@Override
public void simplify() { @Override
evaluate(false, this.editor.getText().toString(), JsclOperation.simplify, null); public void simplify() {
} evaluate(false, this.editor.getText().toString(), JsclOperation.simplify, null);
}
private void evaluate(@Nullable final String expression,
@NotNull JsclOperation operation, private void evaluate(@Nullable final String expression,
@NotNull Runnable currentRunner) { @NotNull JsclOperation operation,
@NotNull Runnable currentRunner) {
if (!StringUtils.isEmpty(expression)) {
try { if (!StringUtils.isEmpty(expression)) {
Log.d(CalculatorModel.class.getName(), "Trying to evaluate '" + operation + "': " + expression /*+ StringUtils.fromStackTrace(Thread.currentThread().getStackTrace())*/); try {
final CalculatorEngine.Result result = calculatorEngine.evaluate(operation, expression); 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) { // todo serso: second condition might replaced with expression.equals(this.editor.getText().toString()) ONLY if expression will be formatted with text highlighter
display.setText(result.getResult()); if (currentRunner == pendingOperation.getObject() && this.editor.getText().length() > 0) {
} else { display.setText(result.getStringResult());
display.setText(""); } else {
} display.setText("");
display.setJsclOperation(result.getUserOperation()); }
display.setGenericResult(result.getGenericResult()); display.setJsclOperation(result.getOperation());
} catch (CalculatorParseException e) { display.setGenericResult(result.getResult());
handleEvaluationException(expression, display, operation, e); } catch (CalculatorParseException e) {
} catch (CalculatorEvalException e) { handleEvaluationException(expression, display, operation, e);
handleEvaluationException(expression, display, operation, e); } catch (CalculatorEvalException e) {
} handleEvaluationException(expression, display, operation, e);
} else { }
this.display.setText(""); } else {
this.display.setJsclOperation(operation); this.display.setText("");
this.display.setGenericResult(null); this.display.setJsclOperation(operation);
} this.display.setGenericResult(null);
}
this.display.redraw();
} this.display.redraw();
}
private void handleEvaluationException(@NotNull String expression,
@NotNull CalculatorDisplay localDisplay, private void handleEvaluationException(@NotNull String expression,
@NotNull JsclOperation operation, @NotNull AndroidCalculatorDisplayView localDisplay,
@NotNull Message e) { @NotNull JsclOperation operation,
Log.d(CalculatorModel.class.getName(), "Evaluation failed for : " + expression + ". Error message: " + e); @NotNull Message e) {
if ( StringUtils.isEmpty(localDisplay.getText()) ) { Log.d(CalculatorModel.class.getName(), "Evaluation failed for : " + expression + ". Error message: " + e);
// if previous display state was empty -> show error if ( StringUtils.isEmpty(localDisplay.getText()) ) {
localDisplay.setText(R.string.c_syntax_error); // if previous display state was empty -> show error
} else { localDisplay.setText(R.string.c_syntax_error);
// show previous result instead of error caption (actually previous result will be greyed) } else {
} // show previous result instead of error caption (actually previous result will be greyed)
localDisplay.setJsclOperation(operation); }
localDisplay.setGenericResult(null); localDisplay.setJsclOperation(operation);
localDisplay.setValid(false); localDisplay.setGenericResult(null);
localDisplay.setErrorMessage(e.getLocalizedMessage()); localDisplay.setValid(false);
} localDisplay.setErrorMessage(e.getLocalizedMessage());
}
public void clear() {
if (!StringUtils.isEmpty(editor.getText()) || !StringUtils.isEmpty(display.getText())) { public void clear() {
editor.getText().clear(); if (!StringUtils.isEmpty(editor.getText()) || !StringUtils.isEmpty(display.getText())) {
display.setText(""); editor.getText().clear();
saveHistoryState(); display.setText("");
} saveHistoryState();
} }
}
public void processDigitButtonAction(@Nullable final String text) {
processDigitButtonAction(text, true); public void processDigitButtonAction(@Nullable final String text) {
} processDigitButtonAction(text, true);
}
public void processDigitButtonAction(@Nullable final String text, boolean delayEvaluate) {
public void processDigitButtonAction(@Nullable final String text, boolean delayEvaluate) {
if (!StringUtils.isEmpty(text)) {
doTextOperation(new CalculatorModel.TextOperation() { if (!StringUtils.isEmpty(text)) {
doTextOperation(new CalculatorModel.TextOperation() {
@Override
public void doOperation(@NotNull EditText editor) { @Override
int cursorPositionOffset = 0; public void doOperation(@NotNull EditText editor) {
final StringBuilder textToBeInserted = new StringBuilder(text); int cursorPositionOffset = 0;
final StringBuilder textToBeInserted = new StringBuilder(text);
final MathType.Result mathType = MathType.getType(text, 0, false);
switch (mathType.getMathType()) { final MathType.Result mathType = MathType.getType(text, 0, false);
case function: switch (mathType.getMathType()) {
textToBeInserted.append("()"); case function:
cursorPositionOffset = -1; textToBeInserted.append("()");
break; cursorPositionOffset = -1;
case operator: break;
textToBeInserted.append("()"); case operator:
cursorPositionOffset = -1; textToBeInserted.append("()");
break; cursorPositionOffset = -1;
case comma: break;
textToBeInserted.append(" "); case comma:
break; textToBeInserted.append(" ");
} break;
}
if (cursorPositionOffset == 0) {
if (MathType.openGroupSymbols.contains(text)) { if (cursorPositionOffset == 0) {
cursorPositionOffset = -1; if (MathType.openGroupSymbols.contains(text)) {
} cursorPositionOffset = -1;
} }
}
editor.getText().insert(editor.getSelectionStart(), textToBeInserted.toString());
editor.setSelection(editor.getSelectionStart() + cursorPositionOffset, editor.getSelectionEnd() + cursorPositionOffset); editor.getText().insert(editor.getSelectionStart(), textToBeInserted.toString());
} editor.setSelection(editor.getSelectionStart() + cursorPositionOffset, editor.getSelectionEnd() + cursorPositionOffset);
}, delayEvaluate); }
} }, delayEvaluate);
} }
}
public static interface TextOperation {
public static interface TextOperation {
void doOperation(@NotNull EditText editor);
void doOperation(@NotNull EditText editor);
}
}
@Override
public void doHistoryAction(@NotNull HistoryAction historyAction) { @Override
synchronized (AndroidCalculatorHistoryImpl.instance) { public void doHistoryAction(@NotNull HistoryAction historyAction) {
if (AndroidCalculatorHistoryImpl.instance.isActionAvailable(historyAction)) { synchronized (AndroidCalculatorHistoryImpl.instance) {
final CalculatorHistoryState newState = AndroidCalculatorHistoryImpl.instance.doAction(historyAction, getCurrentHistoryState()); if (AndroidCalculatorHistoryImpl.instance.isActionAvailable(historyAction)) {
if (newState != null) { final CalculatorHistoryState newState = AndroidCalculatorHistoryImpl.instance.doAction(historyAction, getCurrentHistoryState());
setCurrentHistoryState(newState); if (newState != null) {
} setCurrentHistoryState(newState);
} }
} }
} }
}
@Override
public void setCurrentHistoryState(@NotNull CalculatorHistoryState editorHistoryState) { @Override
synchronized (AndroidCalculatorHistoryImpl.instance) { public void setCurrentHistoryState(@NotNull CalculatorHistoryState editorHistoryState) {
Log.d(this.getClass().getName(), "Saved history found: " + editorHistoryState); synchronized (AndroidCalculatorHistoryImpl.instance) {
Log.d(this.getClass().getName(), "Saved history found: " + editorHistoryState);
editorHistoryState.setValuesFromHistory(new TextViewEditorAdapter(this.editor), this.display);
editorHistoryState.setValuesFromHistory(new TextViewEditorAdapter(this.editor), this.display);
final String expression = this.editor.getText().toString();
if ( !StringUtils.isEmpty(expression) ) { final String expression = this.editor.getText().toString();
if ( StringUtils.isEmpty(this.display.getText().toString()) ) { if ( !StringUtils.isEmpty(expression) ) {
evaluate(false, expression, this.display.getJsclOperation(), editorHistoryState); if ( StringUtils.isEmpty(this.display.getText().toString()) ) {
} evaluate(false, expression, this.display.getJsclOperation(), editorHistoryState);
} }
}
editor.redraw();
display.redraw(); editor.redraw();
} display.redraw();
} }
}
@Override
@NotNull @Override
public CalculatorHistoryState getCurrentHistoryState() { @NotNull
synchronized (AndroidCalculatorHistoryImpl.instance) { public CalculatorHistoryState getCurrentHistoryState() {
return CalculatorHistoryState.newInstance(new TextViewEditorAdapter(this.editor), this.display); synchronized (AndroidCalculatorHistoryImpl.instance) {
} return CalculatorHistoryState.newInstance(new TextViewEditorAdapter(this.editor), this.display);
} }
}
@NotNull
public CalculatorDisplay getDisplay() { @NotNull
return display; public AndroidCalculatorDisplayView getDisplay() {
} return display;
}
private static class CalculatorDisplayOnClickListener implements View.OnClickListener {
private static class CalculatorDisplayOnClickListener implements View.OnClickListener {
@NotNull
private final Activity activity; @NotNull
private final Activity activity;
public CalculatorDisplayOnClickListener(@NotNull Activity activity) {
this.activity = activity; public CalculatorDisplayOnClickListener(@NotNull Activity activity) {
} this.activity = activity;
}
@Override
public void onClick(View v) { @Override
if (v instanceof CalculatorDisplay) { public void onClick(View v) {
final CalculatorDisplay cd = (CalculatorDisplay) v; if (v instanceof CalculatorDisplayView) {
final CalculatorDisplay cd = CalculatorLocatorImpl.getInstance().getCalculatorDisplay();
if (cd.isValid()) {
final List<CalculatorDisplay.MenuItem> filteredMenuItems = new ArrayList<CalculatorDisplay.MenuItem>(CalculatorDisplay.MenuItem.values().length); final CalculatorDisplayViewState displayViewState = cd.getViewState();
for (CalculatorDisplay.MenuItem menuItem : CalculatorDisplay.MenuItem.values()) {
if (menuItem.isItemVisible(cd)) { if (displayViewState.isValid()) {
filteredMenuItems.add(menuItem); 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(); }
}
if (!filteredMenuItems.isEmpty()) {
} else { AMenuBuilder.newInstance(activity, MenuImpl.newInstance(filteredMenuItems)).create(cd).show();
final String errorMessage = cd.getErrorMessage(); }
if (errorMessage != null) {
showEvaluationError(activity, errorMessage); } else {
} final String errorMessage = displayViewState.getErrorMessage();
} if (errorMessage != null) {
} showEvaluationError(activity, errorMessage);
} }
} }
} }
}
}
}

View File

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

View File

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

View File

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

View File

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