diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/Calculator.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/Calculator.java index b310b90c..ee0e756b 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/Calculator.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/Calculator.java @@ -1,9 +1,25 @@ -package org.solovyev.android.calculator; - -/** - * User: Solovyev_S - * Date: 20.09.12 - * Time: 16:38 - */ -public interface Calculator extends CalculatorEventContainer { -} +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.calculator.jscl.JsclOperation; +import org.solovyev.common.msg.MessageRegistry; + +/** + * 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); +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplay.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplay.java new file mode 100644 index 00000000..a68d9747 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplay.java @@ -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(); +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplayImpl.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplayImpl.java new file mode 100644 index 00000000..5e794275 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplayImpl.java @@ -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)); + } +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplayView.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplayView.java new file mode 100644 index 00000000..5ddc45df --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplayView.java @@ -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(); +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplayViewState.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplayViewState.java new file mode 100644 index 00000000..2fdc83b3 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplayViewState.java @@ -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(); +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplayViewStateImpl.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplayViewStateImpl.java new file mode 100644 index 00000000..ac13a9bc --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplayViewStateImpl.java @@ -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; + } +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEvaluationEventData.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEvaluationEventData.java new file mode 100644 index 00000000..45c74178 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEvaluationEventData.java @@ -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(); +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEvaluationEventDataImpl.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEvaluationEventDataImpl.java new file mode 100644 index 00000000..677d6eca --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEvaluationEventDataImpl.java @@ -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); + } +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventData.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventData.java index 5d3b348b..567808a7 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventData.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventData.java @@ -1,11 +1,10 @@ -package org.solovyev.android.calculator; - -import org.jetbrains.annotations.Nullable; - -/** - * User: Solovyev_S - * Date: 20.09.12 - * Time: 16:51 - */ -public interface CalculatorEventData extends CalculatorEventDataId { -} +package org.solovyev.android.calculator; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 16:51 + */ +public interface CalculatorEventData extends CalculatorEventDataId { + +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataId.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataId.java index 58d8dc11..61684be3 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataId.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataId.java @@ -1,17 +1,20 @@ -package org.solovyev.android.calculator; - -import org.jetbrains.annotations.Nullable; - -/** - * User: Solovyev_S - * Date: 20.09.12 - * Time: 18:18 - */ -public interface CalculatorEventDataId { - - // the higher id => the later event - long getEventId(); - - @Nullable - Long getCalculationId(); -} +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 18:18 + */ +public interface CalculatorEventDataId { + + // the higher id => the later event + long getEventId(); + + @Nullable + Long getCalculationId(); + + boolean isAfter(@NotNull CalculatorEventDataId calculatorEventDataId); +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataIdImpl.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataIdImpl.java index 1767719f..6992bef6 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataIdImpl.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataIdImpl.java @@ -1,61 +1,66 @@ -package org.solovyev.android.calculator; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * User: Solovyev_S - * Date: 20.09.12 - * Time: 18:18 - */ -public class CalculatorEventDataIdImpl implements CalculatorEventDataId { - - private final long eventId; - - @Nullable - private final Long calculationId; - - private CalculatorEventDataIdImpl(long id, - @Nullable Long calculationId) { - this.eventId = id; - this.calculationId = calculationId; - } - - @NotNull - public static CalculatorEventDataId newInstance(long id, - @Nullable Long calculationId) { - return new CalculatorEventDataIdImpl(id, calculationId); - } - - @Override - public long getEventId() { - return this.eventId; - } - - @Nullable - @Override - public Long getCalculationId() { - return this.calculationId; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof CalculatorEventDataIdImpl)) return false; - - CalculatorEventDataIdImpl that = (CalculatorEventDataIdImpl) o; - - if (eventId != that.eventId) return false; - if (calculationId != null ? !calculationId.equals(that.calculationId) : that.calculationId != null) - return false; - - return true; - } - - @Override - public int hashCode() { - int result = (int) (eventId ^ (eventId >>> 32)); - result = 31 * result + (calculationId != null ? calculationId.hashCode() : 0); - return result; - } -} +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 18:18 + */ +class CalculatorEventDataIdImpl implements CalculatorEventDataId { + + private final long eventId; + + @Nullable + private final Long calculationId; + + private CalculatorEventDataIdImpl(long id, + @Nullable Long calculationId) { + this.eventId = id; + this.calculationId = calculationId; + } + + @NotNull + static CalculatorEventDataId newInstance(long id, + @Nullable Long calculationId) { + return new CalculatorEventDataIdImpl(id, calculationId); + } + + @Override + public long getEventId() { + return this.eventId; + } + + @Nullable + @Override + public Long getCalculationId() { + return this.calculationId; + } + + @Override + public boolean isAfter(@NotNull CalculatorEventDataId calculatorEventDataId) { + return this.eventId > calculatorEventDataId.getEventId(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof CalculatorEventDataIdImpl)) return false; + + CalculatorEventDataIdImpl that = (CalculatorEventDataIdImpl) o; + + if (eventId != that.eventId) return false; + if (calculationId != null ? !calculationId.equals(that.calculationId) : that.calculationId != null) + return false; + + return true; + } + + @Override + public int hashCode() { + int result = (int) (eventId ^ (eventId >>> 32)); + result = 31 * result + (calculationId != null ? calculationId.hashCode() : 0); + return result; + } +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataImpl.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataImpl.java index 18c9a317..ad9d19b8 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataImpl.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataImpl.java @@ -1,47 +1,57 @@ -package org.solovyev.android.calculator; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * User: Solovyev_S - * Date: 20.09.12 - * Time: 16:54 - */ -class CalculatorEventDataImpl implements CalculatorEventData { - - @NotNull - private CalculatorEventDataId calculatorEventDataId; - - CalculatorEventDataImpl(@NotNull CalculatorEventDataId calculatorEventDataId) { - this.calculatorEventDataId = calculatorEventDataId; - } - - @Override - public long getEventId() { - return calculatorEventDataId.getEventId(); - } - - @Override - @Nullable - public Long getCalculationId() { - return calculatorEventDataId.getCalculationId(); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof CalculatorEventDataImpl)) return false; - - CalculatorEventDataImpl that = (CalculatorEventDataImpl) o; - - if (!calculatorEventDataId.equals(that.calculatorEventDataId)) return false; - - return true; - } - - @Override - public int hashCode() { - return calculatorEventDataId.hashCode(); - } -} +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 16:54 + */ +class CalculatorEventDataImpl implements CalculatorEventData { + + @NotNull + private CalculatorEventDataId calculatorEventDataId; + + private CalculatorEventDataImpl(@NotNull CalculatorEventDataId calculatorEventDataId) { + this.calculatorEventDataId = calculatorEventDataId; + } + + @NotNull + public static CalculatorEventData newInstance(@NotNull CalculatorEventDataId calculatorEventDataId) { + return new CalculatorEventDataImpl(calculatorEventDataId); + } + + @Override + public long getEventId() { + return calculatorEventDataId.getEventId(); + } + + @Override + @Nullable + public Long getCalculationId() { + return calculatorEventDataId.getCalculationId(); + } + + @Override + public boolean isAfter(@NotNull CalculatorEventDataId calculatorEventDataId) { + return this.calculatorEventDataId.isAfter(calculatorEventDataId); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof CalculatorEventDataImpl)) return false; + + CalculatorEventDataImpl that = (CalculatorEventDataImpl) o; + + if (!calculatorEventDataId.equals(that.calculatorEventDataId)) return false; + + return true; + } + + @Override + public int hashCode() { + return calculatorEventDataId.hashCode(); + } +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventType.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventType.java index 7df859e9..c06da282 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventType.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventType.java @@ -1,9 +1,43 @@ -package org.solovyev.android.calculator; - -/** - * User: Solovyev_S - * Date: 20.09.12 - * Time: 16:40 - */ -public enum CalculatorEventType { -} +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * 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; + } + +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorFailure.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorFailure.java new file mode 100644 index 00000000..5d07bb40 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorFailure.java @@ -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(); +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorFailureImpl.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorFailureImpl.java new file mode 100644 index 00000000..866146b3 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorFailureImpl.java @@ -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; + } +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorImpl.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorImpl.java index c98d1712..fab2b16a 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorImpl.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorImpl.java @@ -1,168 +1,209 @@ -package org.solovyev.android.calculator; - -import jscl.AbstractJsclArithmeticException; -import jscl.NumeralBaseException; -import jscl.math.Generic; -import jscl.text.ParseInterruptedException; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.solovyev.android.calculator.jscl.JsclOperation; -import org.solovyev.android.calculator.text.TextProcessor; -import org.solovyev.common.msg.MessageRegistry; - -import java.util.List; -import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicLong; - -/** - * User: Solovyev_S - * Date: 20.09.12 - * Time: 16:42 - */ -public class CalculatorImpl implements Calculator { - - @NotNull - private final CalculatorEventContainer calculatorEventContainer = new ListCalculatorEventContainer(); - - @NotNull - private static final Calculator instance = new CalculatorImpl(); - - @NotNull - private final AtomicLong counter = new AtomicLong(0); - - @NotNull - private final Object lock = new Object(); - - @NotNull - private final TextProcessor preprocessor = ToJsclTextProcessor.getInstance(); - - @NotNull - private final Executor threadPoolExecutor = Executors.newFixedThreadPool(10); - - private CalculatorImpl() { - } - - @NotNull - public static Calculator getInstance() { - return instance; - } - - @NotNull - private CalculatorEventDataId nextCalculatorEventDataId() { - long eventId = counter.incrementAndGet(); - return CalculatorEventDataIdImpl.newInstance(eventId, eventId); - } - - /* - ********************************************************************** - * - * CALCULATION - * - ********************************************************************** - */ - - public void evaluate(@NotNull JsclOperation operation, - @NotNull String expression) { - evaluate(operation, expression, null); - } - - public void evaluate(@NotNull final JsclOperation operation, - @NotNull final String expression, - @Nullable final MessageRegistry mr) { - - final CalculatorEventDataId eventDataId = nextCalculatorEventDataId(); - - threadPoolExecutor.execute(new Runnable() { - @Override - public void run() { - CalculatorImpl.this.evaluate(eventDataId, operation, expression, mr); - } - }); - } - - private void evaluate(@NotNull CalculatorEventDataId eventDataId, - @NotNull JsclOperation operation, - @NotNull String expression, - @Nullable MessageRegistry mr) { - synchronized (lock) { - PreparedExpression preparedExpression = null; - - try { - preparedExpression = preprocessor.process(expression); - - final String jsclExpression = preparedExpression.toString(); - try { - - final Generic genericResult = operation.evaluateGeneric(jsclExpression); - - // NOTE: toString() method must be called here as ArithmeticOperationException may occur in it (just to avoid later check!) - genericResult.toString(); - - //return new Result(operation.getFromProcessor().process(genericResult), operation, genericResult); - } catch (AbstractJsclArithmeticException e) { - handleException(eventDataId, operation, expression, mr, preparedExpression, null, new CalculatorEvalException(e, e, jsclExpression)); - } - - } catch (ArithmeticException e) { - //final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_1, MessageType.error, CalculatorApplication.getInstance(), e.getMessage()); - handleException(operation, expression, mr, preparedExpression, new CalculatorParseException(jsclExpression, androidMessage)); - } catch (StackOverflowError e) { - //final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_2, MessageType.error, CalculatorApplication.getInstance()); - handleException(eventDataId, operation, expression, mr, preparedExpression, new CalculatorParseException(e), null); - } catch (jscl.text.ParseException e) { - //System.out.println(e.getMessage()); - handleException(eventDataId, operation, expression, mr, preparedExpression, new CalculatorParseException(e), null); - } catch (ParseInterruptedException e) { - // do nothing - we ourselves interrupt the calculations - } catch (CalculatorParseException e) { - handleException(eventDataId, operation, expression, mr, preparedExpression, e, null); - } - } - } - - private void handleException(@NotNull CalculatorEventDataId eventDataId, - @NotNull JsclOperation operation, - @NotNull String expression, - @Nullable MessageRegistry mr, - @Nullable PreparedExpression preparedExpression, - @Nullable CalculatorParseException parseException, - @Nullable CalculatorEvalException evalException) { - if (operation == JsclOperation.numeric && (preparedExpression != null && preparedExpression.isExistsUndefinedVar() || (evalException != null && evalException.getCause() instanceof NumeralBaseException))) { - evaluate(eventDataId, JsclOperation.simplify, expression, mr); - } - - if (parseException != null) { - throw parseException; - } else { - throw 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 calculatorEvents) { - calculatorEventContainer.fireCalculatorEvents(calculatorEvents); - } -} +package org.solovyev.android.calculator; + +import jscl.AbstractJsclArithmeticException; +import jscl.NumeralBaseException; +import jscl.math.Generic; +import jscl.text.ParseInterruptedException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.calculator.jscl.JsclOperation; +import org.solovyev.android.calculator.text.TextProcessor; +import org.solovyev.common.msg.MessageRegistry; +import org.solovyev.common.msg.MessageType; + +import java.util.List; +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 + */ +public class CalculatorImpl implements Calculator { + + private static final long FIRST_ID = 0; + + @NotNull + private final CalculatorEventContainer calculatorEventContainer = new ListCalculatorEventContainer(); + + @NotNull + private final AtomicLong counter = new AtomicLong(FIRST_ID); + + @NotNull + private final Object lock = new Object(); + + @NotNull + private final TextProcessor preprocessor = ToJsclTextProcessor.getInstance(); + + @NotNull + private final Executor threadPoolExecutor = Executors.newFixedThreadPool(10); + + public CalculatorImpl() { + } + + @NotNull + private CalculatorEventDataId nextCalculatorEventDataId() { + long eventId = counter.incrementAndGet(); + return CalculatorEventDataIdImpl.newInstance(eventId, eventId); + } + + @NotNull + private CalculatorEventDataId nextEventDataId(@NotNull Long calculationId) { + long eventId = counter.incrementAndGet(); + return CalculatorEventDataIdImpl.newInstance(eventId, calculationId); + } + + /* + ********************************************************************** + * + * CALCULATION + * + ********************************************************************** + */ + + @NotNull + @Override + public CalculatorEventDataId createFirstEventDataId() { + return CalculatorEventDataIdImpl.newInstance(FIRST_ID, FIRST_ID); + } + + @Override + public void evaluate(@NotNull JsclOperation operation, + @NotNull String expression) { + evaluate(operation, expression, null); + } + + @Override + @NotNull + public CalculatorEventDataId evaluate(@NotNull final JsclOperation operation, + @NotNull final String expression, + @Nullable final MessageRegistry mr) { + + final CalculatorEventDataId eventDataId = nextCalculatorEventDataId(); + + threadPoolExecutor.execute(new Runnable() { + @Override + public void run() { + CalculatorImpl.this.evaluate(eventDataId.getCalculationId(), operation, expression, mr); + } + }); + + return eventDataId; + } + + private void evaluate(@NotNull Long calculationId, + @NotNull JsclOperation operation, + @NotNull String expression, + @Nullable MessageRegistry mr) { + synchronized (lock) { + + PreparedExpression preparedExpression = null; + + fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_started, new CalculatorInputImpl(expression, operation)); + + try { + preparedExpression = preprocessor.process(expression); + + final String jsclExpression = preparedExpression.toString(); + + try { + + final Generic result = operation.evaluateGeneric(jsclExpression); + + // NOTE: toString() method must be called here as ArithmeticOperationException may occur in it (just to avoid later check!) + result.toString(); + + 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)); + } + + } catch (ArithmeticException e) { + handleException(calculationId, operation, expression, mr, preparedExpression, new CalculatorParseException(expression, new CalculatorMessage(CalculatorMessages.msg_001, MessageType.error, e.getMessage()))); + } catch (StackOverflowError e) { + handleException(calculationId, operation, expression, mr, preparedExpression, new CalculatorParseException(expression, new CalculatorMessage(CalculatorMessages.msg_002, MessageType.error))); + } catch (jscl.text.ParseException e) { + handleException(calculationId, operation, expression, mr, preparedExpression, new CalculatorParseException(e)); + } catch (ParseInterruptedException e) { + + // do nothing - we ourselves interrupt the calculations + fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_cancelled, null); + + } catch (CalculatorParseException e) { + handleException(calculationId, operation, expression, mr, preparedExpression, e); + } finally { + fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_finished, null); + } + } + } + + @NotNull + private CalculatorEventData newCalculationEventData(@NotNull JsclOperation operation, + @NotNull String expression, + @NotNull Long calculationId) { + return new CalculatorEvaluationEventDataImpl(CalculatorEventDataImpl.newInstance(nextEventDataId(calculationId)), operation, expression); + } + + private void handleException(@NotNull Long calculationId, + @NotNull JsclOperation operation, + @NotNull String expression, + @Nullable MessageRegistry mr, + @Nullable PreparedExpression preparedExpression, + @NotNull CalculatorParseException parseException) { + + if (operation == JsclOperation.numeric + && preparedExpression != null + && preparedExpression.isExistsUndefinedVar()) { + + evaluate(calculationId, JsclOperation.simplify, expression, mr); + + } + + 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 calculatorEvents) { + calculatorEventContainer.fireCalculatorEvents(calculatorEvents); + } +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorInput.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorInput.java new file mode 100644 index 00000000..1f0c98ab --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorInput.java @@ -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(); +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorInputImpl.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorInputImpl.java new file mode 100644 index 00000000..2ad4608c --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorInputImpl.java @@ -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; + } +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorLocator.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorLocator.java index 5b74bde7..3b04e8be 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorLocator.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorLocator.java @@ -1,16 +1,22 @@ -package org.solovyev.android.calculator; - -import org.jetbrains.annotations.NotNull; - -/** - * User: Solovyev_S - * Date: 20.09.12 - * Time: 12:45 - */ -public interface CalculatorLocator { - - @NotNull - JCalculatorEngine getCalculatorEngine(); - - void setCalculatorEngine(@NotNull JCalculatorEngine calculatorEngine); -} +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 12:45 + */ +public interface CalculatorLocator { + + @NotNull + JCalculatorEngine getCalculatorEngine(); + + @NotNull + Calculator getCalculator(); + + @NotNull + CalculatorDisplay getCalculatorDisplay(); + + void setCalculatorEngine(@NotNull JCalculatorEngine calculatorEngine); +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorLocatorImpl.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorLocatorImpl.java index 6cee9b7c..989e1445 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorLocatorImpl.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorLocatorImpl.java @@ -1,36 +1,54 @@ -package org.solovyev.android.calculator; - -import org.jetbrains.annotations.NotNull; - -/** - * User: Solovyev_S - * Date: 20.09.12 - * Time: 12:45 - */ -public class CalculatorLocatorImpl implements CalculatorLocator { - - @NotNull - private JCalculatorEngine calculatorEngine; - - @NotNull - private static final CalculatorLocator instance = new CalculatorLocatorImpl(); - - private CalculatorLocatorImpl() { - } - - @NotNull - public static CalculatorLocator getInstance() { - return instance; - } - - @NotNull - @Override - public JCalculatorEngine getCalculatorEngine() { - return calculatorEngine; - } - - @Override - public void setCalculatorEngine(@NotNull JCalculatorEngine calculatorEngine) { - this.calculatorEngine = calculatorEngine; - } -} +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 12:45 + */ +public class CalculatorLocatorImpl implements CalculatorLocator { + + @NotNull + private JCalculatorEngine calculatorEngine; + + @NotNull + private CalculatorDisplay calculatorDisplay = new CalculatorDisplayImpl(); + + @NotNull + private Calculator calculator = new CalculatorImpl(); + + @NotNull + private static final CalculatorLocator instance = new CalculatorLocatorImpl(); + + private CalculatorLocatorImpl() { + } + + @NotNull + public static CalculatorLocator getInstance() { + return instance; + } + + @NotNull + @Override + 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; + } +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorMessage.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorMessage.java new file mode 100644 index 00000000..ab7e1763 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorMessage.java @@ -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()); + } +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorMessages.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorMessages.java new file mode 100644 index 00000000..aa21657b --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorMessages.java @@ -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 bundlesByLocale = new HashMap(); + + 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"; +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorOutput.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorOutput.java new file mode 100644 index 00000000..bac56104 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorOutput.java @@ -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(); +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorOutputImpl.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorOutputImpl.java new file mode 100644 index 00000000..0ca7e1fe --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorOutputImpl.java @@ -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; + } +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/JCalculatorDisplay.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/JCalculatorDisplay.java deleted file mode 100644 index 1594893a..00000000 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/JCalculatorDisplay.java +++ /dev/null @@ -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(); -} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorDisplayHistoryState.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorDisplayHistoryState.java index 356f953a..c8c37c41 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorDisplayHistoryState.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorDisplayHistoryState.java @@ -1,138 +1,144 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - */ - -package org.solovyev.android.calculator.history; - -import jscl.math.Generic; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; -import org.simpleframework.xml.Transient; -import org.solovyev.android.calculator.JCalculatorDisplay; -import org.solovyev.android.calculator.jscl.JsclOperation; - -/** - * User: serso - * Date: 9/17/11 - * Time: 11:05 PM - */ - -@Root -public class CalculatorDisplayHistoryState implements Cloneable { - - @Transient - private boolean valid = true; - - @Transient - @Nullable - private String errorMessage = null; - - @Element - @NotNull - private EditorHistoryState editorState; - - @Element - @NotNull - private JsclOperation jsclOperation; - - @Transient - @Nullable - private Generic genericResult; - - private CalculatorDisplayHistoryState() { - // for xml - } - - @NotNull - public static CalculatorDisplayHistoryState newInstance(@NotNull JCalculatorDisplay display) { - final CalculatorDisplayHistoryState result = new CalculatorDisplayHistoryState(); - - result.editorState = EditorHistoryState.newInstance(display); - result.valid = display.isValid(); - result.jsclOperation = display.getJsclOperation(); - result.genericResult = display.getGenericResult(); - result.errorMessage = display.getErrorMessage(); - - return result; - } - - public void setValuesFromHistory(@NotNull JCalculatorDisplay display) { - this.getEditorState().setValuesFromHistory(display); - display.setValid(this.isValid()); - display.setErrorMessage(this.getErrorMessage()); - display.setJsclOperation(this.getJsclOperation()); - display.setGenericResult(this.getGenericResult()); - } - - - public boolean isValid() { - return valid; - } - - @NotNull - public EditorHistoryState getEditorState() { - return editorState; - } - - @NotNull - public JsclOperation getJsclOperation() { - return jsclOperation; - } - - @Nullable - public String getErrorMessage() { - return errorMessage; - } - - @Nullable - public Generic getGenericResult() { - return genericResult; - } - - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - CalculatorDisplayHistoryState that = (CalculatorDisplayHistoryState) o; - - if (!editorState.equals(that.editorState)) return false; - if (jsclOperation != that.jsclOperation) return false; - - return true; - } - - @Override - public int hashCode() { - int result = editorState.hashCode(); - result = 31 * result + jsclOperation.hashCode(); - return result; - } - - @Override - public String toString() { - return "CalculatorDisplayHistoryState{" + - "valid=" + valid + - ", errorMessage='" + errorMessage + '\'' + - ", editorHistoryState=" + editorState + - ", jsclOperation=" + jsclOperation + - '}'; - } - - @Override - protected CalculatorDisplayHistoryState clone() { - try { - final CalculatorDisplayHistoryState clone = (CalculatorDisplayHistoryState) super.clone(); - - clone.editorState = this.editorState.clone(); - - return clone; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + */ + +package org.solovyev.android.calculator.history; + +import jscl.math.Generic; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.simpleframework.xml.Element; +import org.simpleframework.xml.Root; +import org.simpleframework.xml.Transient; +import org.solovyev.android.calculator.CalculatorDisplay; +import org.solovyev.android.calculator.CalculatorDisplayViewState; +import org.solovyev.android.calculator.CalculatorDisplayViewStateImpl; +import org.solovyev.android.calculator.jscl.JsclOperation; +import org.solovyev.common.text.StringUtils; + +/** + * User: serso + * Date: 9/17/11 + * Time: 11:05 PM + */ + +@Root +public class CalculatorDisplayHistoryState implements Cloneable { + + @Transient + private boolean valid = true; + + @Transient + @Nullable + private String errorMessage = null; + + @Element + @NotNull + private EditorHistoryState editorState; + + @Element + @NotNull + private JsclOperation jsclOperation; + + @Transient + @Nullable + private Generic genericResult; + + private CalculatorDisplayHistoryState() { + // for xml + } + + @NotNull + public static CalculatorDisplayHistoryState newInstance(@NotNull CalculatorDisplay display) { + final CalculatorDisplayHistoryState result = new CalculatorDisplayHistoryState(); + + result.editorState = EditorHistoryState.newInstance(display.getViewState()); + + final CalculatorDisplayViewState displayViewState = display.getViewState(); + + result.valid = displayViewState.isValid(); + result.jsclOperation = displayViewState.getOperation(); + result.genericResult = displayViewState.getResult(); + result.errorMessage = displayViewState.getErrorMessage(); + + return result; + } + + 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())); + } else { + display.setViewState(CalculatorDisplayViewStateImpl.newErrorState(this.getJsclOperation(), StringUtils.getNotEmpty(this.getErrorMessage(), ""))); + } + } + + + public boolean isValid() { + return valid; + } + + @NotNull + public EditorHistoryState getEditorState() { + return editorState; + } + + @NotNull + public JsclOperation getJsclOperation() { + return jsclOperation; + } + + @Nullable + public String getErrorMessage() { + return errorMessage; + } + + @Nullable + public Generic getGenericResult() { + return genericResult; + } + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + CalculatorDisplayHistoryState that = (CalculatorDisplayHistoryState) o; + + if (!editorState.equals(that.editorState)) return false; + if (jsclOperation != that.jsclOperation) return false; + + return true; + } + + @Override + public int hashCode() { + int result = editorState.hashCode(); + result = 31 * result + jsclOperation.hashCode(); + return result; + } + + @Override + public String toString() { + return "CalculatorDisplayHistoryState{" + + "valid=" + valid + + ", errorMessage='" + errorMessage + '\'' + + ", editorHistoryState=" + editorState + + ", jsclOperation=" + jsclOperation + + '}'; + } + + @Override + protected CalculatorDisplayHistoryState clone() { + try { + final CalculatorDisplayHistoryState clone = (CalculatorDisplayHistoryState) super.clone(); + + clone.editorState = this.editorState.clone(); + + return clone; + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorHistoryState.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorHistoryState.java index a7d37aaa..f866596a 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorHistoryState.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorHistoryState.java @@ -1,111 +1,111 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - */ - -package org.solovyev.android.calculator.history; - -import org.jetbrains.annotations.NotNull; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; -import org.solovyev.android.calculator.Editor; -import org.solovyev.android.calculator.JCalculatorDisplay; - -/** - * User: serso - * Date: 9/11/11 - * Time: 12:16 AM - */ - -@Root -public class CalculatorHistoryState extends AbstractHistoryState { - - @Element - @NotNull - private EditorHistoryState editorState; - - @Element - @NotNull - private CalculatorDisplayHistoryState displayState; - - private CalculatorHistoryState() { - // for xml - } - - private CalculatorHistoryState(@NotNull EditorHistoryState editorState, - @NotNull CalculatorDisplayHistoryState displayState) { - this.editorState = editorState; - this.displayState = displayState; - } - - public static CalculatorHistoryState newInstance(@NotNull Editor editor, @NotNull JCalculatorDisplay display) { - final EditorHistoryState editorHistoryState = EditorHistoryState.newInstance(editor); - final CalculatorDisplayHistoryState displayHistoryState = CalculatorDisplayHistoryState.newInstance(display); - return new CalculatorHistoryState(editorHistoryState, displayHistoryState); - } - - @NotNull - public EditorHistoryState getEditorState() { - return editorState; - } - - public void setEditorState(@NotNull EditorHistoryState editorState) { - this.editorState = editorState; - } - - @NotNull - public CalculatorDisplayHistoryState getDisplayState() { - return displayState; - } - - public void setDisplayState(@NotNull CalculatorDisplayHistoryState displayState) { - this.displayState = displayState; - } - - @Override - public String toString() { - return "CalculatorHistoryState{" + - "editorState=" + editorState + - ", displayState=" + displayState + - '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - CalculatorHistoryState that = (CalculatorHistoryState) o; - - if (this.isSaved() != that.isSaved()) return false; - if (this.getId() != that.getId()) return false; - if (!displayState.equals(that.displayState)) return false; - if (!editorState.equals(that.editorState)) return false; - - return true; - } - - @Override - public int hashCode() { - int result = Boolean.valueOf(isSaved()).hashCode(); - result = 31 * result + getId(); - result = 31 * result + editorState.hashCode(); - result = 31 * result + displayState.hashCode(); - return result; - } - - public void setValuesFromHistory(@NotNull Editor editor, @NotNull JCalculatorDisplay display) { - this.getEditorState().setValuesFromHistory(editor); - this.getDisplayState().setValuesFromHistory(display); - } - - @Override - protected CalculatorHistoryState clone() { - final CalculatorHistoryState clone = (CalculatorHistoryState)super.clone(); - - clone.editorState = this.editorState.clone(); - clone.displayState = this.displayState.clone(); - - return clone; - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + */ + +package org.solovyev.android.calculator.history; + +import org.jetbrains.annotations.NotNull; +import org.simpleframework.xml.Element; +import org.simpleframework.xml.Root; +import org.solovyev.android.calculator.CalculatorDisplay; +import org.solovyev.android.calculator.Editor; + +/** + * User: serso + * Date: 9/11/11 + * Time: 12:16 AM + */ + +@Root +public class CalculatorHistoryState extends AbstractHistoryState { + + @Element + @NotNull + private EditorHistoryState editorState; + + @Element + @NotNull + private CalculatorDisplayHistoryState displayState; + + private CalculatorHistoryState() { + // for xml + } + + private CalculatorHistoryState(@NotNull EditorHistoryState editorState, + @NotNull CalculatorDisplayHistoryState displayState) { + this.editorState = editorState; + this.displayState = displayState; + } + + public static CalculatorHistoryState newInstance(@NotNull Editor editor, @NotNull CalculatorDisplay display) { + final EditorHistoryState editorHistoryState = EditorHistoryState.newInstance(editor); + final CalculatorDisplayHistoryState displayHistoryState = CalculatorDisplayHistoryState.newInstance(display); + return new CalculatorHistoryState(editorHistoryState, displayHistoryState); + } + + @NotNull + public EditorHistoryState getEditorState() { + return editorState; + } + + public void setEditorState(@NotNull EditorHistoryState editorState) { + this.editorState = editorState; + } + + @NotNull + public CalculatorDisplayHistoryState getDisplayState() { + return displayState; + } + + public void setDisplayState(@NotNull CalculatorDisplayHistoryState displayState) { + this.displayState = displayState; + } + + @Override + public String toString() { + return "CalculatorHistoryState{" + + "editorState=" + editorState + + ", displayState=" + displayState + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + CalculatorHistoryState that = (CalculatorHistoryState) o; + + if (this.isSaved() != that.isSaved()) return false; + if (this.getId() != that.getId()) return false; + if (!displayState.equals(that.displayState)) return false; + if (!editorState.equals(that.editorState)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = Boolean.valueOf(isSaved()).hashCode(); + result = 31 * result + getId(); + result = 31 * result + editorState.hashCode(); + result = 31 * result + displayState.hashCode(); + return result; + } + + public void setValuesFromHistory(@NotNull Editor editor, @NotNull CalculatorDisplay display) { + this.getEditorState().setValuesFromHistory(editor); + this.getDisplayState().setValuesFromHistory(display); + } + + @Override + protected CalculatorHistoryState clone() { + final CalculatorHistoryState clone = (CalculatorHistoryState)super.clone(); + + clone.editorState = this.editorState.clone(); + clone.displayState = this.displayState.clone(); + + return clone; + } +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/EditorHistoryState.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/EditorHistoryState.java index ea2de8ab..2ffa241b 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/EditorHistoryState.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/EditorHistoryState.java @@ -1,88 +1,99 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - */ - -package org.solovyev.android.calculator.history; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; -import org.solovyev.android.calculator.Editor; - -@Root -public class EditorHistoryState implements Cloneable{ - - @Element - private int cursorPosition; - - @Element(required = false) - @Nullable - private String text; - - private EditorHistoryState() { - // for xml - } - - @NotNull - public static EditorHistoryState newInstance(@NotNull Editor editor) { - final EditorHistoryState result = new EditorHistoryState(); - - result.text = String.valueOf(editor.getText()); - result.cursorPosition = editor.getSelection(); - - return result; - } - - public void setValuesFromHistory(@NotNull Editor editor) { - editor.setText(this.getText()); - editor.setSelection(this.getCursorPosition()); - } - - @Nullable - public String getText() { - return text; - } - - public int getCursorPosition() { - return cursorPosition; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof EditorHistoryState)) return false; - - EditorHistoryState that = (EditorHistoryState) o; - - if (cursorPosition != that.cursorPosition) return false; - if (text != null ? !text.equals(that.text) : that.text != null) return false; - - return true; - } - - @Override - public int hashCode() { - int result = cursorPosition; - result = 31 * result + (text != null ? text.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "EditorHistoryState{" + - "cursorPosition=" + cursorPosition + - ", text='" + text + '\'' + - '}'; - } - - @Override - protected EditorHistoryState clone() { - try { - return (EditorHistoryState)super.clone(); - } catch (CloneNotSupportedException e) { - throw new UnsupportedOperationException(e); - } - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + */ + +package org.solovyev.android.calculator.history; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.simpleframework.xml.Element; +import org.simpleframework.xml.Root; +import org.solovyev.android.calculator.CalculatorDisplayViewState; +import org.solovyev.android.calculator.Editor; + +@Root +public class EditorHistoryState implements Cloneable{ + + @Element + private int cursorPosition; + + @Element(required = false) + @Nullable + private String text; + + private EditorHistoryState() { + // for xml + } + + @NotNull + public static EditorHistoryState newInstance(@NotNull Editor editor) { + final EditorHistoryState result = new EditorHistoryState(); + + result.text = String.valueOf(editor.getText()); + result.cursorPosition = editor.getSelection(); + + return result; + } + + @NotNull + public static EditorHistoryState newInstance(@NotNull CalculatorDisplayViewState viewState) { + final EditorHistoryState result = new EditorHistoryState(); + + result.text = viewState.getText(); + result.cursorPosition = viewState.getSelection(); + + return result; + } + + public void setValuesFromHistory(@NotNull Editor editor) { + editor.setText(this.getText()); + editor.setSelection(this.getCursorPosition()); + } + + @Nullable + public String getText() { + return text; + } + + public int getCursorPosition() { + return cursorPosition; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof EditorHistoryState)) return false; + + EditorHistoryState that = (EditorHistoryState) o; + + if (cursorPosition != that.cursorPosition) return false; + if (text != null ? !text.equals(that.text) : that.text != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = cursorPosition; + result = 31 * result + (text != null ? text.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "EditorHistoryState{" + + "cursorPosition=" + cursorPosition + + ", text='" + text + '\'' + + '}'; + } + + @Override + protected EditorHistoryState clone() { + try { + return (EditorHistoryState)super.clone(); + } catch (CloneNotSupportedException e) { + throw new UnsupportedOperationException(e); + } + } +} diff --git a/calculatorpp-core/src/main/resources/org/solovyev/android/calculator/messages.properties b/calculatorpp-core/src/main/resources/org/solovyev/android/calculator/messages.properties new file mode 100644 index 00000000..e6534912 --- /dev/null +++ b/calculatorpp-core/src/main/resources/org/solovyev/android/calculator/messages.properties @@ -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 \ No newline at end of file diff --git a/calculatorpp/res/layout/calc_display.xml b/calculatorpp/res/layout/calc_display.xml index c4e6a164..c44067dd 100644 --- a/calculatorpp/res/layout/calc_display.xml +++ b/calculatorpp/res/layout/calc_display.xml @@ -6,7 +6,7 @@ ~ or visit http://se.solovyev.org --> - { - convert_to_bin(NumeralBase.bin), - convert_to_dec(NumeralBase.dec), - convert_to_hex(NumeralBase.hex); - - @NotNull - private final NumeralBase toNumeralBase; - - private ConversionMenuItem(@NotNull NumeralBase toNumeralBase) { - this.toNumeralBase = toNumeralBase; - } - - protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { - boolean result = false; - - if (operation == JsclOperation.numeric) { - if (generic.getConstants().isEmpty()) { - try { - convert(generic); - - // conversion possible => return true - result = true; - - } catch (UnitConverterViewBuilder.ConversionException e) { - // conversion is not possible => return false - } - } - } - - return result; - } - - @Override - public void onClick(@NotNull CalculatorDisplay data, @NotNull Context context) { - final NumeralBase fromNumeralBase = CalculatorEngine.instance.getEngine().getNumeralBase(); - - String to; - try { - to = convert(data.getGenericResult()); - - // add prefix - if (fromNumeralBase != toNumeralBase) { - to = toNumeralBase.getJsclPrefix() + to; - } - } catch (UnitConverterViewBuilder.ConversionException e) { - to = context.getString(R.string.c_error); - } - - data.setText(to); - data.redraw(); - } - - @NotNull - private String convert(@NotNull Generic generic) throws UnitConverterViewBuilder.ConversionException { - final NumeralBase fromNumeralBase = CalculatorEngine.instance.getEngine().getNumeralBase(); - - if (fromNumeralBase != toNumeralBase) { - String from = generic.toString(); - if (!StringUtils.isEmpty(from)) { - try { - from = ToJsclTextProcessor.getInstance().process(from).getExpression(); - } catch (CalculatorParseException e) { - // ok, problems while processing occurred - } - } - - return UnitConverterViewBuilder.doConversion(AndroidNumeralBase.getConverter(), from, AndroidNumeralBase.valueOf(fromNumeralBase), AndroidNumeralBase.valueOf(toNumeralBase)); - } else { - return generic.toString(); - } - } - } - - public static enum MenuItem implements LabeledMenuItem { - - copy(R.string.c_copy) { - @Override - public void onClick(@NotNull CalculatorDisplay data, @NotNull Context context) { - CalculatorModel.copyResult(context, data); - } - }, - - convert_to_bin(R.string.convert_to_bin) { - @Override - public void onClick(@NotNull CalculatorDisplay data, @NotNull Context context) { - ConversionMenuItem.convert_to_bin.onClick(data, context); - } - - @Override - protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { - return ConversionMenuItem.convert_to_bin.isItemVisibleFor(generic, operation); - } - }, - - convert_to_dec(R.string.convert_to_dec) { - @Override - public void onClick(@NotNull CalculatorDisplay data, @NotNull Context context) { - ConversionMenuItem.convert_to_dec.onClick(data, context); - } - - @Override - protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { - return ConversionMenuItem.convert_to_dec.isItemVisibleFor(generic, operation); - } - }, - - convert_to_hex(R.string.convert_to_hex) { - @Override - public void onClick(@NotNull CalculatorDisplay data, @NotNull Context context) { - ConversionMenuItem.convert_to_hex.onClick(data, context); - } - - @Override - protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { - return ConversionMenuItem.convert_to_hex.isItemVisibleFor(generic, operation); - } - }, - - convert(R.string.c_convert) { - @Override - public void onClick(@NotNull CalculatorDisplay data, @NotNull Context context) { - new NumeralBaseConverterDialog(data.getGenericResult().toString()).show(context); - } - - @Override - protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { - return operation == JsclOperation.numeric && generic.getConstants().isEmpty(); - } - }, - - plot(R.string.c_plot) { - @Override - public void onClick(@NotNull CalculatorDisplay data, @NotNull Context context) { - final Generic generic = data.getGenericResult(); - assert generic != null; - - final Constant constant = CollectionsUtils.getFirstCollectionElement(getNotSystemConstants(generic)); - assert constant != null; - CalculatorActivityLauncher.plotGraph(context, generic, constant); - } - - @Override - protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { - boolean result = false; - - if (operation == JsclOperation.simplify) { - if (getNotSystemConstants(generic).size() == 1) { - result = true; - } - } - - return result; - } - - @NotNull - private Set getNotSystemConstants(@NotNull Generic generic) { - final Set notSystemConstants = new HashSet(); - - for (Constant constant : generic.getConstants()) { - IConstant var = CalculatorEngine.instance.getVarsRegistry().get(constant.getName()); - if (var != null && !var.isSystem() && !var.isDefined()) { - notSystemConstants.add(constant); - } - } - - return notSystemConstants; - } - }; - - private final int captionId; - - MenuItem(int captionId) { - this.captionId = captionId; - } - - public final boolean isItemVisible(@NotNull CalculatorDisplay display) { - //noinspection ConstantConditions - return display.isValid() && display.getGenericResult() != null && isItemVisibleFor(display.getGenericResult(), display.getJsclOperation()); - } - - protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { - return true; - } - - @NotNull - @Override - public String getCaption(@NotNull Context context) { - return context.getString(captionId); - } - } - - private boolean valid = true; - - @Nullable - private String errorMessage; - - @NotNull - private JsclOperation jsclOperation = JsclOperation.numeric; - - @NotNull - private final static TextProcessor textHighlighter = new TextHighlighter(Color.WHITE, false, CalculatorEngine.instance.getEngine()); - - @Nullable - private Generic genericResult; - - public CalculatorDisplay(Context context) { - super(context); - } - - public CalculatorDisplay(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public CalculatorDisplay(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - public boolean isValid() { - return valid; - } - - @Override - public void setValid(boolean valid) { - this.valid = valid; - if (valid) { - errorMessage = null; - setTextColor(getResources().getColor(R.color.default_text_color)); - } else { - setTextColor(getResources().getColor(R.color.display_error_text_color)); - } - } - - @Override - @Nullable - public String getErrorMessage() { - return errorMessage; - } - - @Override - public void setErrorMessage(@Nullable String errorMessage) { - this.errorMessage = errorMessage; - } - - @Override - public void setJsclOperation(@NotNull JsclOperation jsclOperation) { - this.jsclOperation = jsclOperation; - } - - @Override - @NotNull - public JsclOperation getJsclOperation() { - return jsclOperation; - } - - @Override - public void setText(CharSequence text, BufferType type) { - super.setText(text, type); - - setValid(true); - } - - public synchronized void redraw() { - if (isValid()) { - String text = getText().toString(); - - Log.d(this.getClass().getName(), text); - - try { - TextHighlighter.Result result = textHighlighter.process(text); - text = result.toString(); - } catch (CalculatorParseException e) { - Log.e(this.getClass().getName(), e.getMessage(), e); - } - - Log.d(this.getClass().getName(), text); - super.setText(Html.fromHtml(text), BufferType.EDITABLE); - } - - // todo serso: think where to move it (keep in mind org.solovyev.android.view.AutoResizeTextView.resetTextSize()) - setAddEllipsis(false); - setMinTextSize(10); - resizeText(); - } - - @Override - public void setGenericResult(@Nullable Generic genericResult) { - this.genericResult = genericResult; - } - - @Override - @Nullable - public Generic getGenericResult() { - return genericResult; - } - - @Override - public int getSelection() { - return this.getSelectionStart(); - } - - @Override - public void setSelection(int selection) { - // not supported by TextView - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + */ + +package org.solovyev.android.calculator; + +import android.content.Context; +import android.graphics.Color; +import android.text.Html; +import android.util.AttributeSet; +import android.util.Log; +import jscl.NumeralBase; +import jscl.math.Generic; +import jscl.math.function.Constant; +import jscl.math.function.IConstant; +import org.jetbrains.annotations.NotNull; +import org.solovyev.android.calculator.jscl.JsclOperation; +import org.solovyev.android.calculator.model.CalculatorEngine; +import org.solovyev.android.calculator.text.TextProcessor; +import org.solovyev.android.calculator.view.NumeralBaseConverterDialog; +import org.solovyev.android.calculator.view.TextHighlighter; +import org.solovyev.android.calculator.view.UnitConverterViewBuilder; +import org.solovyev.android.menu.AMenuItem; +import org.solovyev.android.menu.LabeledMenuItem; +import org.solovyev.android.view.AutoResizeTextView; +import org.solovyev.common.collections.CollectionsUtils; +import org.solovyev.common.text.StringUtils; + +import java.util.HashSet; +import java.util.Set; + +/** + * User: serso + * Date: 9/17/11 + * Time: 10:58 PM + */ +public class AndroidCalculatorDisplayView extends AutoResizeTextView implements CalculatorDisplayView { + + private static enum ConversionMenuItem implements AMenuItem { + convert_to_bin(NumeralBase.bin), + convert_to_dec(NumeralBase.dec), + convert_to_hex(NumeralBase.hex); + + @NotNull + private final NumeralBase toNumeralBase; + + private ConversionMenuItem(@NotNull NumeralBase toNumeralBase) { + this.toNumeralBase = toNumeralBase; + } + + protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { + boolean result = false; + + if (operation == JsclOperation.numeric) { + if (generic.getConstants().isEmpty()) { + try { + convert(generic); + + // conversion possible => return true + result = true; + + } catch (UnitConverterViewBuilder.ConversionException e) { + // conversion is not possible => return false + } + } + } + + return result; + } + + @Override + public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) { + final NumeralBase fromNumeralBase = CalculatorEngine.instance.getEngine().getNumeralBase(); + + final Generic lastResult = CalculatorLocatorImpl.getInstance().getCalculatorDisplay().getViewState().getResult(); + + if (lastResult != null) { + String to; + try { + to = convert(lastResult); + + // add prefix + if (fromNumeralBase != toNumeralBase) { + to = toNumeralBase.getJsclPrefix() + to; + } + } catch (UnitConverterViewBuilder.ConversionException e) { + to = context.getString(R.string.c_error); + } + + data.setText(to); + //data.redraw(); + } + } + + @NotNull + private String convert(@NotNull Generic generic) throws UnitConverterViewBuilder.ConversionException { + final NumeralBase fromNumeralBase = CalculatorEngine.instance.getEngine().getNumeralBase(); + + if (fromNumeralBase != toNumeralBase) { + String from = generic.toString(); + if (!StringUtils.isEmpty(from)) { + try { + from = ToJsclTextProcessor.getInstance().process(from).getExpression(); + } catch (CalculatorParseException e) { + // ok, problems while processing occurred + } + } + + return UnitConverterViewBuilder.doConversion(AndroidNumeralBase.getConverter(), from, AndroidNumeralBase.valueOf(fromNumeralBase), AndroidNumeralBase.valueOf(toNumeralBase)); + } else { + return generic.toString(); + } + } + } + + public static enum MenuItem implements LabeledMenuItem { + + copy(R.string.c_copy) { + @Override + public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) { + CalculatorModel.copyResult(context, data); + } + }, + + convert_to_bin(R.string.convert_to_bin) { + @Override + public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) { + ConversionMenuItem.convert_to_bin.onClick(data, context); + } + + @Override + protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { + return ConversionMenuItem.convert_to_bin.isItemVisibleFor(generic, operation); + } + }, + + convert_to_dec(R.string.convert_to_dec) { + @Override + public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) { + ConversionMenuItem.convert_to_dec.onClick(data, context); + } + + @Override + protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { + return ConversionMenuItem.convert_to_dec.isItemVisibleFor(generic, operation); + } + }, + + convert_to_hex(R.string.convert_to_hex) { + @Override + public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) { + ConversionMenuItem.convert_to_hex.onClick(data, context); + } + + @Override + protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { + return ConversionMenuItem.convert_to_hex.isItemVisibleFor(generic, operation); + } + }, + + convert(R.string.c_convert) { + @Override + public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) { + final Generic result = data.getState().getResult(); + if (result != null) { + new NumeralBaseConverterDialog(result.toString()).show(context); + } + } + + @Override + protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { + return operation == JsclOperation.numeric && generic.getConstants().isEmpty(); + } + }, + + plot(R.string.c_plot) { + @Override + public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) { + final Generic generic = data.getState().getResult(); + assert generic != null; + + final Constant constant = CollectionsUtils.getFirstCollectionElement(getNotSystemConstants(generic)); + assert constant != null; + CalculatorActivityLauncher.plotGraph(context, generic, constant); + } + + @Override + protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { + boolean result = false; + + if (operation == JsclOperation.simplify) { + if (getNotSystemConstants(generic).size() == 1) { + result = true; + } + } + + return result; + } + + @NotNull + private Set getNotSystemConstants(@NotNull Generic generic) { + final Set notSystemConstants = new HashSet(); + + for (Constant constant : generic.getConstants()) { + IConstant var = CalculatorEngine.instance.getVarsRegistry().get(constant.getName()); + if (var != null && !var.isSystem() && !var.isDefined()) { + notSystemConstants.add(constant); + } + } + + return notSystemConstants; + } + }; + + private final int captionId; + + MenuItem(int captionId) { + this.captionId = captionId; + } + + public final boolean isItemVisible(@NotNull CalculatorDisplayViewState displayViewState) { + //noinspection ConstantConditions + return displayViewState.isValid() && displayViewState.getResult() != null && isItemVisibleFor(displayViewState.getResult(), displayViewState.getOperation()); + } + + protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { + return true; + } + + @NotNull + @Override + public String getCaption(@NotNull Context context) { + return context.getString(captionId); + } + } + + @NotNull + private CalculatorDisplayViewState state = CalculatorDisplayViewStateImpl.newDefaultInstance(); + + @NotNull + private final static TextProcessor textHighlighter = new TextHighlighter(Color.WHITE, false, CalculatorEngine.instance.getEngine()); + + public AndroidCalculatorDisplayView(Context context) { + super(context); + } + + public AndroidCalculatorDisplayView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public AndroidCalculatorDisplayView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public boolean isValid() { + return this.state.isValid(); + } + + + @Override + public void setState(@NotNull CalculatorDisplayViewState state) { + this.state = state; + if ( state.isValid() ) { + setTextColor(getResources().getColor(R.color.default_text_color)); + setText(state.getStringResult()); + } else { + setTextColor(getResources().getColor(R.color.display_error_text_color)); + setText(state.getErrorMessage()); + } + } + + @NotNull + @Override + public CalculatorDisplayViewState getState() { + return this.state; + } + + @Override + public void setText(CharSequence text, BufferType type) { + super.setText(text, type); + } + + public synchronized void redraw() { + if (isValid()) { + String text = getText().toString(); + + Log.d(this.getClass().getName(), text); + + try { + TextHighlighter.Result result = textHighlighter.process(text); + text = result.toString(); + } catch (CalculatorParseException e) { + Log.e(this.getClass().getName(), e.getMessage(), e); + } + + Log.d(this.getClass().getName(), text); + super.setText(Html.fromHtml(text), BufferType.EDITABLE); + } + + // todo serso: think where to move it (keep in mind org.solovyev.android.view.AutoResizeTextView.resetTextSize()) + setAddEllipsis(false); + setMinTextSize(10); + resizeText(); + } + + @Override + public int getSelection() { + return this.getSelectionStart(); + } + + @Override + public void setSelection(int selection) { + // not supported by TextView + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorModel.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorModel.java index 2329cc0a..d3268689 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorModel.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorModel.java @@ -1,409 +1,412 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - */ - -package org.solovyev.android.calculator; - -import android.app.Activity; -import android.app.AlertDialog; -import android.content.Context; -import android.content.SharedPreferences; -import android.os.Handler; -import android.text.ClipboardManager; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.EditText; -import android.widget.TextView; -import android.widget.Toast; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.solovyev.android.CursorControl; -import org.solovyev.android.calculator.history.AndroidCalculatorHistoryImpl; -import org.solovyev.android.calculator.history.CalculatorHistoryState; -import org.solovyev.android.calculator.history.TextViewEditorAdapter; -import org.solovyev.android.calculator.jscl.JsclOperation; -import org.solovyev.android.calculator.math.MathType; -import org.solovyev.android.calculator.model.CalculatorEngine; -import org.solovyev.android.history.HistoryControl; -import org.solovyev.android.menu.AMenuBuilder; -import org.solovyev.android.menu.MenuImpl; -import org.solovyev.common.MutableObject; -import org.solovyev.common.history.HistoryAction; -import org.solovyev.common.msg.Message; -import org.solovyev.common.text.StringUtils; - -import java.util.ArrayList; -import java.util.List; - -/** - * User: serso - * Date: 9/12/11 - * Time: 11:15 PM - */ -public enum CalculatorModel implements CursorControl, HistoryControl, CalculatorEngineControl { - - instance; - - // millis to wait before evaluation after user edit action - public static final int EVAL_DELAY_MILLIS = 0; - - @NotNull - private CalculatorEditor editor; - - @NotNull - private CalculatorDisplay display; - - @NotNull - private CalculatorEngine calculatorEngine; - - public CalculatorModel init(@NotNull final Activity activity, @NotNull SharedPreferences preferences, @NotNull CalculatorEngine calculator) { - Log.d(this.getClass().getName(), "CalculatorModel initialization with activity: " + activity); - this.calculatorEngine = calculator; - - this.editor = (CalculatorEditor) activity.findViewById(R.id.calculatorEditor); - this.editor.init(preferences); - preferences.registerOnSharedPreferenceChangeListener(editor); - - this.display = (CalculatorDisplay) activity.findViewById(R.id.calculatorDisplay); - this.display.setOnClickListener(new CalculatorDisplayOnClickListener(activity)); - - final CalculatorHistoryState lastState = AndroidCalculatorHistoryImpl.instance.getLastHistoryState(); - if (lastState == null) { - saveHistoryState(); - } else { - setCurrentHistoryState(lastState); - } - - - return this; - } - - private static void showEvaluationError(@NotNull Activity activity, @NotNull final String errorMessage) { - final LayoutInflater layoutInflater = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE); - - final View errorMessageView = layoutInflater.inflate(R.layout.display_error_message, null); - ((TextView) errorMessageView.findViewById(R.id.error_message_text_view)).setText(errorMessage); - - final AlertDialog.Builder builder = new AlertDialog.Builder(activity) - .setPositiveButton(R.string.c_cancel, null) - .setView(errorMessageView); - - builder.create().show(); - } - - public void copyResult(@NotNull Context context) { - copyResult(context, display); - } - - public static void copyResult(@NotNull Context context, @NotNull final CalculatorDisplay display) { - if (display.isValid()) { - final CharSequence text = display.getText(); - if (!StringUtils.isEmpty(text)) { - final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE); - clipboard.setText(text.toString()); - Toast.makeText(context, context.getText(R.string.c_result_copied), Toast.LENGTH_SHORT).show(); - } - } - } - - private void saveHistoryState() { - AndroidCalculatorHistoryImpl.instance.addState(getCurrentHistoryState()); - } - - public void setCursorOnStart() { - editor.setSelection(0); - } - - public void setCursorOnEnd() { - editor.setSelection(editor.getText().length()); - } - - public void moveCursorLeft() { - if (editor.getSelectionStart() > 0) { - editor.setSelection(editor.getSelectionStart() - 1); - } - } - - public void moveCursorRight() { - if (editor.getSelectionStart() < editor.getText().length()) { - editor.setSelection(editor.getSelectionStart() + 1); - } - } - - public void doTextOperation(@NotNull TextOperation operation) { - doTextOperation(operation, true); - } - - public void doTextOperation(@NotNull TextOperation operation, boolean delayEvaluate) { - doTextOperation(operation, delayEvaluate, JsclOperation.numeric, false); - } - - public void doTextOperation(@NotNull TextOperation operation, boolean delayEvaluate, @NotNull JsclOperation jsclOperation, boolean forceEval) { - final String editorStateBefore = this.editor.getText().toString(); - - Log.d(CalculatorModel.class.getName(), "Editor state changed before '" + editorStateBefore + "'"); - operation.doOperation(this.editor); - //Log.d(CalculatorModel.class.getName(), "Doing text operation" + StringUtils.fromStackTrace(Thread.currentThread().getStackTrace())); - - final String editorStateAfter = this.editor.getText().toString(); - if (forceEval ||!editorStateBefore.equals(editorStateAfter)) { - - editor.redraw(); - - evaluate(delayEvaluate, editorStateAfter, jsclOperation, null); - } - } - - @NotNull - private final static MutableObject pendingOperation = new MutableObject(); - - private void evaluate(boolean delayEvaluate, - @NotNull final String expression, - @NotNull final JsclOperation operation, - @Nullable CalculatorHistoryState historyState) { - - final CalculatorHistoryState localHistoryState; - if (historyState == null) { - //this.display.setText(""); - localHistoryState = getCurrentHistoryState(); - } else { - this.display.setText(historyState.getDisplayState().getEditorState().getText()); - localHistoryState = historyState; - } - - pendingOperation.setObject(new Runnable() { - @Override - public void run() { - // allow only one runner at one time - synchronized (pendingOperation) { - //lock all operations with history - if (pendingOperation.getObject() == this) { - // actually nothing shall be logged while text operations are done - evaluate(expression, operation, this); - - if (pendingOperation.getObject() == this) { - // todo serso: of course there is small probability that someone will set pendingOperation after if statement but before .setObject(null) - pendingOperation.setObject(null); - localHistoryState.setDisplayState(getCurrentHistoryState().getDisplayState()); - } - } - } - } - }); - - if (delayEvaluate) { - if (historyState == null) { - AndroidCalculatorHistoryImpl.instance.addState(localHistoryState); - } - // todo serso: this is not correct - operation is processing still in the same thread - new Handler().postDelayed(pendingOperation.getObject(), EVAL_DELAY_MILLIS); - } else { - pendingOperation.getObject().run(); - if (historyState == null) { - AndroidCalculatorHistoryImpl.instance.addState(localHistoryState); - } - } - } - - @Override - public void evaluate() { - evaluate(false, this.editor.getText().toString(), JsclOperation.numeric, null); - } - - public void evaluate(@NotNull JsclOperation operation) { - evaluate(false, this.editor.getText().toString(), operation, null); - } - - @Override - public void simplify() { - evaluate(false, this.editor.getText().toString(), JsclOperation.simplify, null); - } - - private void evaluate(@Nullable final String expression, - @NotNull JsclOperation operation, - @NotNull Runnable currentRunner) { - - if (!StringUtils.isEmpty(expression)) { - try { - Log.d(CalculatorModel.class.getName(), "Trying to evaluate '" + operation + "': " + expression /*+ StringUtils.fromStackTrace(Thread.currentThread().getStackTrace())*/); - final CalculatorEngine.Result result = calculatorEngine.evaluate(operation, expression); - - // todo serso: second condition might replaced with expression.equals(this.editor.getText().toString()) ONLY if expression will be formatted with text highlighter - if (currentRunner == pendingOperation.getObject() && this.editor.getText().length() > 0) { - display.setText(result.getResult()); - } else { - display.setText(""); - } - display.setJsclOperation(result.getUserOperation()); - display.setGenericResult(result.getGenericResult()); - } catch (CalculatorParseException e) { - handleEvaluationException(expression, display, operation, e); - } catch (CalculatorEvalException e) { - handleEvaluationException(expression, display, operation, e); - } - } else { - this.display.setText(""); - this.display.setJsclOperation(operation); - this.display.setGenericResult(null); - } - - - - this.display.redraw(); - } - - private void handleEvaluationException(@NotNull String expression, - @NotNull CalculatorDisplay localDisplay, - @NotNull JsclOperation operation, - @NotNull Message e) { - Log.d(CalculatorModel.class.getName(), "Evaluation failed for : " + expression + ". Error message: " + e); - if ( StringUtils.isEmpty(localDisplay.getText()) ) { - // if previous display state was empty -> show error - localDisplay.setText(R.string.c_syntax_error); - } else { - // show previous result instead of error caption (actually previous result will be greyed) - } - localDisplay.setJsclOperation(operation); - localDisplay.setGenericResult(null); - localDisplay.setValid(false); - localDisplay.setErrorMessage(e.getLocalizedMessage()); - } - - public void clear() { - if (!StringUtils.isEmpty(editor.getText()) || !StringUtils.isEmpty(display.getText())) { - editor.getText().clear(); - display.setText(""); - saveHistoryState(); - } - } - - public void processDigitButtonAction(@Nullable final String text) { - processDigitButtonAction(text, true); - } - - public void processDigitButtonAction(@Nullable final String text, boolean delayEvaluate) { - - if (!StringUtils.isEmpty(text)) { - doTextOperation(new CalculatorModel.TextOperation() { - - @Override - public void doOperation(@NotNull EditText editor) { - int cursorPositionOffset = 0; - final StringBuilder textToBeInserted = new StringBuilder(text); - - final MathType.Result mathType = MathType.getType(text, 0, false); - switch (mathType.getMathType()) { - case function: - textToBeInserted.append("()"); - cursorPositionOffset = -1; - break; - case operator: - textToBeInserted.append("()"); - cursorPositionOffset = -1; - break; - case comma: - textToBeInserted.append(" "); - break; - } - - if (cursorPositionOffset == 0) { - if (MathType.openGroupSymbols.contains(text)) { - cursorPositionOffset = -1; - } - } - - editor.getText().insert(editor.getSelectionStart(), textToBeInserted.toString()); - editor.setSelection(editor.getSelectionStart() + cursorPositionOffset, editor.getSelectionEnd() + cursorPositionOffset); - } - }, delayEvaluate); - } - } - - public static interface TextOperation { - - void doOperation(@NotNull EditText editor); - - } - - @Override - public void doHistoryAction(@NotNull HistoryAction historyAction) { - synchronized (AndroidCalculatorHistoryImpl.instance) { - if (AndroidCalculatorHistoryImpl.instance.isActionAvailable(historyAction)) { - final CalculatorHistoryState newState = AndroidCalculatorHistoryImpl.instance.doAction(historyAction, getCurrentHistoryState()); - if (newState != null) { - setCurrentHistoryState(newState); - } - } - } - } - - @Override - public void setCurrentHistoryState(@NotNull CalculatorHistoryState editorHistoryState) { - synchronized (AndroidCalculatorHistoryImpl.instance) { - Log.d(this.getClass().getName(), "Saved history found: " + editorHistoryState); - - editorHistoryState.setValuesFromHistory(new TextViewEditorAdapter(this.editor), this.display); - - final String expression = this.editor.getText().toString(); - if ( !StringUtils.isEmpty(expression) ) { - if ( StringUtils.isEmpty(this.display.getText().toString()) ) { - evaluate(false, expression, this.display.getJsclOperation(), editorHistoryState); - } - } - - editor.redraw(); - display.redraw(); - } - } - - @Override - @NotNull - public CalculatorHistoryState getCurrentHistoryState() { - synchronized (AndroidCalculatorHistoryImpl.instance) { - return CalculatorHistoryState.newInstance(new TextViewEditorAdapter(this.editor), this.display); - } - } - - @NotNull - public CalculatorDisplay getDisplay() { - return display; - } - - private static class CalculatorDisplayOnClickListener implements View.OnClickListener { - - @NotNull - private final Activity activity; - - public CalculatorDisplayOnClickListener(@NotNull Activity activity) { - this.activity = activity; - } - - @Override - public void onClick(View v) { - if (v instanceof CalculatorDisplay) { - final CalculatorDisplay cd = (CalculatorDisplay) v; - - if (cd.isValid()) { - final List filteredMenuItems = new ArrayList(CalculatorDisplay.MenuItem.values().length); - for (CalculatorDisplay.MenuItem menuItem : CalculatorDisplay.MenuItem.values()) { - if (menuItem.isItemVisible(cd)) { - filteredMenuItems.add(menuItem); - } - } - - if (!filteredMenuItems.isEmpty()) { - AMenuBuilder.newInstance(activity, MenuImpl.newInstance(filteredMenuItems)).create(cd).show(); - } - - } else { - final String errorMessage = cd.getErrorMessage(); - if (errorMessage != null) { - showEvaluationError(activity, errorMessage); - } - } - } - } - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + */ + +package org.solovyev.android.calculator; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Handler; +import android.text.ClipboardManager; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.CursorControl; +import org.solovyev.android.calculator.history.AndroidCalculatorHistoryImpl; +import org.solovyev.android.calculator.history.CalculatorHistoryState; +import org.solovyev.android.calculator.history.TextViewEditorAdapter; +import org.solovyev.android.calculator.jscl.JsclOperation; +import org.solovyev.android.calculator.math.MathType; +import org.solovyev.android.calculator.model.CalculatorEngine; +import org.solovyev.android.history.HistoryControl; +import org.solovyev.android.menu.AMenuBuilder; +import org.solovyev.android.menu.MenuImpl; +import org.solovyev.common.MutableObject; +import org.solovyev.common.history.HistoryAction; +import org.solovyev.common.msg.Message; +import org.solovyev.common.text.StringUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * User: serso + * Date: 9/12/11 + * Time: 11:15 PM + */ +public enum CalculatorModel implements CursorControl, HistoryControl, CalculatorEngineControl { + + instance; + + // millis to wait before evaluation after user edit action + public static final int EVAL_DELAY_MILLIS = 0; + + @NotNull + private CalculatorEditor editor; + + @NotNull + private AndroidCalculatorDisplayView display; + + @NotNull + private CalculatorEngine calculatorEngine; + + public CalculatorModel init(@NotNull final Activity activity, @NotNull SharedPreferences preferences, @NotNull CalculatorEngine calculator) { + Log.d(this.getClass().getName(), "CalculatorModel initialization with activity: " + activity); + this.calculatorEngine = calculator; + + this.editor = (CalculatorEditor) activity.findViewById(R.id.calculatorEditor); + this.editor.init(preferences); + preferences.registerOnSharedPreferenceChangeListener(editor); + + this.display = (AndroidCalculatorDisplayView) activity.findViewById(R.id.calculatorDisplay); + this.display.setOnClickListener(new CalculatorDisplayOnClickListener(activity)); + + final CalculatorHistoryState lastState = AndroidCalculatorHistoryImpl.instance.getLastHistoryState(); + if (lastState == null) { + saveHistoryState(); + } else { + setCurrentHistoryState(lastState); + } + + + return this; + } + + private static void showEvaluationError(@NotNull Activity activity, @NotNull final String errorMessage) { + final LayoutInflater layoutInflater = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE); + + final View errorMessageView = layoutInflater.inflate(R.layout.display_error_message, null); + ((TextView) errorMessageView.findViewById(R.id.error_message_text_view)).setText(errorMessage); + + final AlertDialog.Builder builder = new AlertDialog.Builder(activity) + .setPositiveButton(R.string.c_cancel, null) + .setView(errorMessageView); + + builder.create().show(); + } + + public void copyResult(@NotNull Context context) { + copyResult(context, display); + } + + public static void copyResult(@NotNull Context context, @NotNull final CalculatorDisplayView display) { + final CalculatorDisplayViewState displayViewState = display.getState(); + if (displayViewState.isValid()) { + final CharSequence text = display.getText(); + if (!StringUtils.isEmpty(text)) { + final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE); + clipboard.setText(text.toString()); + Toast.makeText(context, context.getText(R.string.c_result_copied), Toast.LENGTH_SHORT).show(); + } + } + } + + private void saveHistoryState() { + AndroidCalculatorHistoryImpl.instance.addState(getCurrentHistoryState()); + } + + public void setCursorOnStart() { + editor.setSelection(0); + } + + public void setCursorOnEnd() { + editor.setSelection(editor.getText().length()); + } + + public void moveCursorLeft() { + if (editor.getSelectionStart() > 0) { + editor.setSelection(editor.getSelectionStart() - 1); + } + } + + public void moveCursorRight() { + if (editor.getSelectionStart() < editor.getText().length()) { + editor.setSelection(editor.getSelectionStart() + 1); + } + } + + public void doTextOperation(@NotNull TextOperation operation) { + doTextOperation(operation, true); + } + + public void doTextOperation(@NotNull TextOperation operation, boolean delayEvaluate) { + doTextOperation(operation, delayEvaluate, JsclOperation.numeric, false); + } + + public void doTextOperation(@NotNull TextOperation operation, boolean delayEvaluate, @NotNull JsclOperation jsclOperation, boolean forceEval) { + final String editorStateBefore = this.editor.getText().toString(); + + Log.d(CalculatorModel.class.getName(), "Editor state changed before '" + editorStateBefore + "'"); + operation.doOperation(this.editor); + //Log.d(CalculatorModel.class.getName(), "Doing text operation" + StringUtils.fromStackTrace(Thread.currentThread().getStackTrace())); + + final String editorStateAfter = this.editor.getText().toString(); + if (forceEval ||!editorStateBefore.equals(editorStateAfter)) { + + editor.redraw(); + + evaluate(delayEvaluate, editorStateAfter, jsclOperation, null); + } + } + + @NotNull + private final static MutableObject pendingOperation = new MutableObject(); + + private void evaluate(boolean delayEvaluate, + @NotNull final String expression, + @NotNull final JsclOperation operation, + @Nullable CalculatorHistoryState historyState) { + + final CalculatorHistoryState localHistoryState; + if (historyState == null) { + //this.display.setText(""); + localHistoryState = getCurrentHistoryState(); + } else { + this.display.setText(historyState.getDisplayState().getEditorState().getText()); + localHistoryState = historyState; + } + + pendingOperation.setObject(new Runnable() { + @Override + public void run() { + // allow only one runner at one time + synchronized (pendingOperation) { + //lock all operations with history + if (pendingOperation.getObject() == this) { + // actually nothing shall be logged while text operations are done + evaluate(expression, operation, this); + + if (pendingOperation.getObject() == this) { + // todo serso: of course there is small probability that someone will set pendingOperation after if statement but before .setObject(null) + pendingOperation.setObject(null); + localHistoryState.setDisplayState(getCurrentHistoryState().getDisplayState()); + } + } + } + } + }); + + if (delayEvaluate) { + if (historyState == null) { + AndroidCalculatorHistoryImpl.instance.addState(localHistoryState); + } + // todo serso: this is not correct - operation is processing still in the same thread + new Handler().postDelayed(pendingOperation.getObject(), EVAL_DELAY_MILLIS); + } else { + pendingOperation.getObject().run(); + if (historyState == null) { + AndroidCalculatorHistoryImpl.instance.addState(localHistoryState); + } + } + } + + @Override + public void evaluate() { + evaluate(false, this.editor.getText().toString(), JsclOperation.numeric, null); + } + + public void evaluate(@NotNull JsclOperation operation) { + evaluate(false, this.editor.getText().toString(), operation, null); + } + + @Override + public void simplify() { + evaluate(false, this.editor.getText().toString(), JsclOperation.simplify, null); + } + + private void evaluate(@Nullable final String expression, + @NotNull JsclOperation operation, + @NotNull Runnable currentRunner) { + + if (!StringUtils.isEmpty(expression)) { + try { + Log.d(CalculatorModel.class.getName(), "Trying to evaluate '" + operation + "': " + expression /*+ StringUtils.fromStackTrace(Thread.currentThread().getStackTrace())*/); + final CalculatorOutput result = calculatorEngine.evaluate(operation, expression); + + // todo serso: second condition might replaced with expression.equals(this.editor.getText().toString()) ONLY if expression will be formatted with text highlighter + if (currentRunner == pendingOperation.getObject() && this.editor.getText().length() > 0) { + display.setText(result.getStringResult()); + } else { + display.setText(""); + } + display.setJsclOperation(result.getOperation()); + display.setGenericResult(result.getResult()); + } catch (CalculatorParseException e) { + handleEvaluationException(expression, display, operation, e); + } catch (CalculatorEvalException e) { + handleEvaluationException(expression, display, operation, e); + } + } else { + this.display.setText(""); + this.display.setJsclOperation(operation); + this.display.setGenericResult(null); + } + + + + this.display.redraw(); + } + + private void handleEvaluationException(@NotNull String expression, + @NotNull AndroidCalculatorDisplayView localDisplay, + @NotNull JsclOperation operation, + @NotNull Message e) { + Log.d(CalculatorModel.class.getName(), "Evaluation failed for : " + expression + ". Error message: " + e); + if ( StringUtils.isEmpty(localDisplay.getText()) ) { + // if previous display state was empty -> show error + localDisplay.setText(R.string.c_syntax_error); + } else { + // show previous result instead of error caption (actually previous result will be greyed) + } + localDisplay.setJsclOperation(operation); + localDisplay.setGenericResult(null); + localDisplay.setValid(false); + localDisplay.setErrorMessage(e.getLocalizedMessage()); + } + + public void clear() { + if (!StringUtils.isEmpty(editor.getText()) || !StringUtils.isEmpty(display.getText())) { + editor.getText().clear(); + display.setText(""); + saveHistoryState(); + } + } + + public void processDigitButtonAction(@Nullable final String text) { + processDigitButtonAction(text, true); + } + + public void processDigitButtonAction(@Nullable final String text, boolean delayEvaluate) { + + if (!StringUtils.isEmpty(text)) { + doTextOperation(new CalculatorModel.TextOperation() { + + @Override + public void doOperation(@NotNull EditText editor) { + int cursorPositionOffset = 0; + final StringBuilder textToBeInserted = new StringBuilder(text); + + final MathType.Result mathType = MathType.getType(text, 0, false); + switch (mathType.getMathType()) { + case function: + textToBeInserted.append("()"); + cursorPositionOffset = -1; + break; + case operator: + textToBeInserted.append("()"); + cursorPositionOffset = -1; + break; + case comma: + textToBeInserted.append(" "); + break; + } + + if (cursorPositionOffset == 0) { + if (MathType.openGroupSymbols.contains(text)) { + cursorPositionOffset = -1; + } + } + + editor.getText().insert(editor.getSelectionStart(), textToBeInserted.toString()); + editor.setSelection(editor.getSelectionStart() + cursorPositionOffset, editor.getSelectionEnd() + cursorPositionOffset); + } + }, delayEvaluate); + } + } + + public static interface TextOperation { + + void doOperation(@NotNull EditText editor); + + } + + @Override + public void doHistoryAction(@NotNull HistoryAction historyAction) { + synchronized (AndroidCalculatorHistoryImpl.instance) { + if (AndroidCalculatorHistoryImpl.instance.isActionAvailable(historyAction)) { + final CalculatorHistoryState newState = AndroidCalculatorHistoryImpl.instance.doAction(historyAction, getCurrentHistoryState()); + if (newState != null) { + setCurrentHistoryState(newState); + } + } + } + } + + @Override + public void setCurrentHistoryState(@NotNull CalculatorHistoryState editorHistoryState) { + synchronized (AndroidCalculatorHistoryImpl.instance) { + Log.d(this.getClass().getName(), "Saved history found: " + editorHistoryState); + + editorHistoryState.setValuesFromHistory(new TextViewEditorAdapter(this.editor), this.display); + + final String expression = this.editor.getText().toString(); + if ( !StringUtils.isEmpty(expression) ) { + if ( StringUtils.isEmpty(this.display.getText().toString()) ) { + evaluate(false, expression, this.display.getJsclOperation(), editorHistoryState); + } + } + + editor.redraw(); + display.redraw(); + } + } + + @Override + @NotNull + public CalculatorHistoryState getCurrentHistoryState() { + synchronized (AndroidCalculatorHistoryImpl.instance) { + return CalculatorHistoryState.newInstance(new TextViewEditorAdapter(this.editor), this.display); + } + } + + @NotNull + public AndroidCalculatorDisplayView getDisplay() { + return display; + } + + private static class CalculatorDisplayOnClickListener implements View.OnClickListener { + + @NotNull + private final Activity activity; + + public CalculatorDisplayOnClickListener(@NotNull Activity activity) { + this.activity = activity; + } + + @Override + public void onClick(View v) { + if (v instanceof CalculatorDisplayView) { + final CalculatorDisplay cd = CalculatorLocatorImpl.getInstance().getCalculatorDisplay(); + + final CalculatorDisplayViewState displayViewState = cd.getViewState(); + + if (displayViewState.isValid()) { + final List filteredMenuItems = new ArrayList(AndroidCalculatorDisplayView.MenuItem.values().length); + for (AndroidCalculatorDisplayView.MenuItem menuItem : AndroidCalculatorDisplayView.MenuItem.values()) { + if (menuItem.isItemVisible(displayViewState)) { + filteredMenuItems.add(menuItem); + } + } + + if (!filteredMenuItems.isEmpty()) { + AMenuBuilder.newInstance(activity, MenuImpl.newInstance(filteredMenuItems)).create(cd).show(); + } + + } else { + final String errorMessage = displayViewState.getErrorMessage(); + if (errorMessage != null) { + showEvaluationError(activity, errorMessage); + } + } + } + } + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/CalculatorEngine.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/model/CalculatorEngine.java index 00d4da93..4dfa07f0 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/CalculatorEngine.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/model/CalculatorEngine.java @@ -1,437 +1,404 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - */ - -package org.solovyev.android.calculator.model; - -import android.content.Context; -import android.content.SharedPreferences; -import jscl.*; -import jscl.math.Generic; -import jscl.math.function.Function; -import jscl.math.function.IConstant; -import jscl.math.operator.Operator; -import jscl.text.ParseInterruptedException; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.solovyev.android.calculator.*; -import org.solovyev.android.calculator.jscl.JsclOperation; -import org.solovyev.android.calculator.text.TextProcessor; -import org.solovyev.android.msg.AndroidMessage; -import org.solovyev.android.prefs.BooleanPreference; -import org.solovyev.android.prefs.Preference; -import org.solovyev.android.prefs.StringPreference; -import org.solovyev.common.MutableObject; -import org.solovyev.common.msg.MessageRegistry; -import org.solovyev.common.msg.MessageType; -import org.solovyev.common.text.EnumMapper; -import org.solovyev.common.text.NumberMapper; -import org.solovyev.common.text.StringUtils; - -import java.text.DecimalFormatSymbols; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -/** - * User: serso - * Date: 9/12/11 - * Time: 11:38 PM - */ - -public enum CalculatorEngine implements JCalculatorEngine { - - instance; - - private static final String GROUPING_SEPARATOR_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_calc_grouping_separator"; - - private static final String MULTIPLICATION_SIGN_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_calc_multiplication_sign"; - private static final String MULTIPLICATION_SIGN_DEFAULT = "×"; - - private static final String MAX_CALCULATION_TIME_P_KEY = "calculation.max_calculation_time"; - private static final String MAX_CALCULATION_TIME_DEFAULT = "5"; - - private static final String SCIENCE_NOTATION_P_KEY = "calculation.output.science_notation"; - private static final boolean SCIENCE_NOTATION_DEFAULT = false; - - private static final String ROUND_RESULT_P_KEY = "org.solovyev.android.calculator.CalculatorModel_round_result"; - private static final boolean ROUND_RESULT_DEFAULT = true; - - private static final String RESULT_PRECISION_P_KEY = "org.solovyev.android.calculator.CalculatorModel_result_precision"; - private static final String RESULT_PRECISION_DEFAULT = "5"; - - private static final String NUMERAL_BASES_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_numeral_bases"; - private static final String NUMERAL_BASES_DEFAULT = "dec"; - - private static final String ANGLE_UNITS_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_angle_units"; - private static final String ANGLE_UNITS_DEFAULT = "deg"; - - public static class Preferences { - public static final Preference groupingSeparator = StringPreference.newInstance(GROUPING_SEPARATOR_P_KEY, JsclMathEngine.GROUPING_SEPARATOR_DEFAULT); - public static final Preference multiplicationSign = StringPreference.newInstance(MULTIPLICATION_SIGN_P_KEY, MULTIPLICATION_SIGN_DEFAULT); - public static final Preference precision = StringPreference.newInstance(RESULT_PRECISION_P_KEY, RESULT_PRECISION_DEFAULT, new NumberMapper(Integer.class)); - public static final Preference roundResult = new BooleanPreference(ROUND_RESULT_P_KEY, ROUND_RESULT_DEFAULT); - public static final Preference numeralBase = StringPreference.newInstance(NUMERAL_BASES_P_KEY, NUMERAL_BASES_DEFAULT, EnumMapper.newInstance(NumeralBase.class)); - public static final Preference angleUnit = StringPreference.newInstance(ANGLE_UNITS_P_KEY, ANGLE_UNITS_DEFAULT, EnumMapper.newInstance(AngleUnit.class)); - public static final Preference scienceNotation = new BooleanPreference(SCIENCE_NOTATION_P_KEY, SCIENCE_NOTATION_DEFAULT); - public static final Preference maxCalculationTime = StringPreference.newInstance(MAX_CALCULATION_TIME_P_KEY, MAX_CALCULATION_TIME_DEFAULT, new NumberMapper(Integer.class)); - - private static final List preferenceKeys = new ArrayList(); - - static { - preferenceKeys.add(groupingSeparator.getKey()); - preferenceKeys.add(multiplicationSign.getKey()); - preferenceKeys.add(precision.getKey()); - preferenceKeys.add(roundResult.getKey()); - preferenceKeys.add(numeralBase.getKey()); - preferenceKeys.add(angleUnit.getKey()); - preferenceKeys.add(scienceNotation.getKey()); - preferenceKeys.add(maxCalculationTime.getKey()); - } - - @NotNull - public static List getPreferenceKeys() { - return Collections.unmodifiableList(preferenceKeys); - } - } - - @NotNull - private final Object lock = new Object(); - - @NotNull - private MathEngine engine = JsclMathEngine.instance; - - @NotNull - public final TextProcessor preprocessor = ToJsclTextProcessor.getInstance(); - - @NotNull - private final AndroidMathRegistry varsRegistry = new AndroidVarsRegistryImpl(engine.getConstantsRegistry()); - - @NotNull - private final AndroidMathRegistry functionsRegistry = new AndroidFunctionsMathRegistry(engine.getFunctionsRegistry()); - - @NotNull - private final AndroidMathRegistry operatorsRegistry = new AndroidOperatorsMathRegistry(engine.getOperatorsRegistry()); - - private final AndroidMathRegistry postfixFunctionsRegistry = new AndroidPostfixFunctionsRegistry(engine.getPostfixFunctionsRegistry()); - - @Nullable - private ThreadKiller threadKiller = new AndroidThreadKiller(); - - // calculation thread timeout in seconds, after timeout thread would be interrupted - private int timeout = Integer.valueOf(MAX_CALCULATION_TIME_DEFAULT); - - @NotNull - private String multiplicationSign = MULTIPLICATION_SIGN_DEFAULT; - - CalculatorEngine() { - this.engine.setRoundResult(true); - this.engine.setUseGroupingSeparator(true); - } - - @Override - @NotNull - public String getMultiplicationSign() { - return multiplicationSign; - } - - public void setMultiplicationSign(@NotNull String multiplicationSign) { - this.multiplicationSign = multiplicationSign; - } - - public static class Result { - - @NotNull - private Generic genericResult; - - @NotNull - private String result; - - @NotNull - private JsclOperation userOperation; - - public Result(@NotNull String result, @NotNull JsclOperation userOperation, @NotNull Generic genericResult) { - this.result = result; - this.userOperation = userOperation; - this.genericResult = genericResult; - } - - @NotNull - public String getResult() { - return result; - } - - @NotNull - public JsclOperation getUserOperation() { - return userOperation; - } - - @NotNull - public Generic getGenericResult() { - return genericResult; - } - } - - public Result evaluate(@NotNull JsclOperation operation, - @NotNull String expression) throws CalculatorParseException, CalculatorEvalException { - return evaluate(operation, expression, null); - } - - public Result evaluate(@NotNull final JsclOperation operation, - @NotNull String expression, - @Nullable MessageRegistry mr) throws CalculatorParseException, CalculatorEvalException { - synchronized (lock) { - final StringBuilder sb = new StringBuilder(); - - final PreparedExpression preparedExpression = preprocessor.process(expression); - sb.append(preparedExpression); - - //Log.d(CalculatorEngine.class.getName(), "Preprocessed expression: " + preparedExpression); - /*if (operation == JsclOperation.numeric && preparedExpression.isExistsUndefinedVar()) { - operation = JsclOperation.simplify; - - if (mr != null) { - final String undefinedVars = CollectionsUtils.formatValue(preparedExpression.getUndefinedVars(), ", ", new Formatter() { - @Override - public String formatValue(@Nullable Var var) throws IllegalArgumentException { - return var != null ? var.getName() : ""; - } - }); - - mr.addMessage(new AndroidMessage(R.string.c_simplify_instead_of_numeric, MessageType.info, undefinedVars)); - } - }*/ - - final String jsclExpression = sb.toString(); - - final MutableObject calculationResult = new MutableObject(null); - final MutableObject parseException = new MutableObject(null); - final MutableObject evalException = new MutableObject(null); - final MutableObject calculationThread = new MutableObject(null); - - final CountDownLatch latch = new CountDownLatch(1); - - new Thread(new Runnable() { - @Override - public void run() { - final Thread thread = Thread.currentThread(); - try { - //Log.d(CalculatorEngine.class.getName(), "Calculation thread started work: " + thread.getName()); - //System.out.println(jsclExpression); - calculationThread.setObject(thread); - final Generic genericResult = operation.evaluateGeneric(jsclExpression); - - // NOTE: toString() method must be called here as ArithmeticOperationException may occur in it (just to avoid later check!) - genericResult.toString(); - - calculationResult.setObject(genericResult); - } catch (AbstractJsclArithmeticException e) { - evalException.setObject(new CalculatorEvalException(e, e, jsclExpression)); - } catch (ArithmeticException e) { - //System.out.println(e.getMessage()); - final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_1, MessageType.error, CalculatorApplication.getInstance(), e.getMessage()); - parseException.setObject(new CalculatorParseException(jsclExpression, androidMessage)); - } catch (StackOverflowError e) { - //System.out.println(StringUtils.fromStackTrace(e.getStackTrace())); - final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_2, MessageType.error, CalculatorApplication.getInstance()); - parseException.setObject(new CalculatorParseException(jsclExpression, androidMessage)); - } catch (jscl.text.ParseException e) { - //System.out.println(e.getMessage()); - parseException.setObject(new CalculatorParseException(e)); - } catch (ParseInterruptedException e) { - //System.out.println(e.getMessage()); - // do nothing - we ourselves interrupt the calculations - } finally { - //Log.d(CalculatorEngine.class.getName(), "Calculation thread ended work: " + thread.getName()); - calculationThread.setObject(null); - latch.countDown(); - } - } - }).start(); - - try { - //Log.d(CalculatorEngine.class.getName(), "Main thread is waiting: " + Thread.currentThread().getName()); - latch.await(timeout, TimeUnit.SECONDS); - //Log.d(CalculatorEngine.class.getName(), "Main thread got up: " + Thread.currentThread().getName()); - - final CalculatorParseException parseExceptionObject = parseException.getObject(); - final CalculatorEvalException evalExceptionObject = evalException.getObject(); - final Object calculationResultLocal = calculationResult.getObject(); - final Thread calculationThreadLocal = calculationThread.getObject(); - - if (calculationThreadLocal != null) { - if (threadKiller != null) { - threadKiller.killThread(calculationThreadLocal); - } - //calculationThreadLocal.stop(); - } - - if (parseExceptionObject != null || evalExceptionObject != null) { - if (operation == JsclOperation.numeric && - (preparedExpression.isExistsUndefinedVar() || (evalExceptionObject != null && evalExceptionObject.getCause() instanceof NumeralBaseException))) { - return evaluate(JsclOperation.simplify, expression, mr); - } - - if (parseExceptionObject != null) { - throw parseExceptionObject; - } else { - throw evalExceptionObject; - } - } - - if (calculationResultLocal == null) { - final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_3, MessageType.error, CalculatorApplication.getInstance()); - throw new CalculatorParseException(jsclExpression, androidMessage); - } - - } catch (InterruptedException e) { - final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_4, MessageType.error, CalculatorApplication.getInstance()); - throw new CalculatorParseException(jsclExpression, androidMessage); - } - - final Generic genericResult = calculationResult.getObject(); - - return new Result(operation.getFromProcessor().process(genericResult), operation, genericResult); - } - } - - public void setPrecision(int precision) { - this.getEngine().setPrecision(precision); - } - - public void setRoundResult(boolean roundResult) { - this.getEngine().setRoundResult(roundResult); - } - - public void init(@Nullable Context context, @Nullable SharedPreferences preferences) { - synchronized (lock) { - reset(context, preferences); - } - } - - public void reset(@Nullable Context context, @Nullable SharedPreferences preferences) { - synchronized (lock) { - softReset(context, preferences); - - varsRegistry.load(context, preferences); - functionsRegistry.load(context, preferences); - operatorsRegistry.load(context, preferences); - postfixFunctionsRegistry.load(context, preferences); - } - } - - public void softReset(@Nullable Context context, @Nullable SharedPreferences preferences) { - synchronized (lock) { - if (preferences != null) { - this.setPrecision(Preferences.precision.getPreference(preferences)); - this.setRoundResult(Preferences.roundResult.getPreference(preferences)); - this.setAngleUnits(getAngleUnitsFromPrefs(preferences)); - this.setNumeralBase(getNumeralBaseFromPrefs(preferences)); - this.setMultiplicationSign(Preferences.multiplicationSign.getPreference(preferences)); - this.setScienceNotation(Preferences.scienceNotation.getPreference(preferences)); - this.setTimeout(Preferences.maxCalculationTime.getPreference(preferences)); - - final String groupingSeparator = Preferences.groupingSeparator.getPreference(preferences); - if (StringUtils.isEmpty(groupingSeparator)) { - this.getEngine().setUseGroupingSeparator(false); - } else { - this.getEngine().setUseGroupingSeparator(true); - this.getEngine().setGroupingSeparator(groupingSeparator.charAt(0)); - } - } - } - } - - - @NotNull - public NumeralBase getNumeralBaseFromPrefs(@NotNull SharedPreferences preferences) { - return Preferences.numeralBase.getPreference(preferences); - } - - @NotNull - public AngleUnit getAngleUnitsFromPrefs(@NotNull SharedPreferences preferences) { - return Preferences.angleUnit.getPreference(preferences); - } - - //for tests only - void setDecimalGroupSymbols(@NotNull DecimalFormatSymbols decimalGroupSymbols) { - synchronized (lock) { - this.getEngine().setDecimalGroupSymbols(decimalGroupSymbols); - } - } - - @Override - @NotNull - public AndroidMathRegistry getVarsRegistry() { - return varsRegistry; - } - - @Override - @NotNull - public AndroidMathRegistry getFunctionsRegistry() { - return functionsRegistry; - } - - @Override - @NotNull - public AndroidMathRegistry getOperatorsRegistry() { - return operatorsRegistry; - } - - @Override - @NotNull - public AndroidMathRegistry getPostfixFunctionsRegistry() { - return postfixFunctionsRegistry; - } - - @Override - @NotNull - public MathEngine getEngine() { - return engine; - } - - // package protected for tests - void setTimeout(int timeout) { - this.timeout = timeout; - } - - public void setAngleUnits(@NotNull AngleUnit angleUnits) { - getEngine().setAngleUnits(angleUnits); - } - - public void setScienceNotation(boolean scienceNotation) { - getEngine().setScienceNotation(scienceNotation); - } - - public void setNumeralBase(@NotNull NumeralBase numeralBase) { - getEngine().setNumeralBase(numeralBase); - } - - // for tests only - void setThreadKiller(@Nullable ThreadKiller threadKiller) { - this.threadKiller = threadKiller; - } - - private static interface ThreadKiller { - void killThread(@NotNull Thread thread); - } - - private static class AndroidThreadKiller implements ThreadKiller { - @Override - public void killThread(@NotNull Thread thread) { - thread.setPriority(Thread.MIN_PRIORITY); - thread.interrupt(); - } - } - - public static class ThreadKillerImpl implements ThreadKiller { - @Override - public void killThread(@NotNull Thread thread) { - thread.setPriority(Thread.MIN_PRIORITY); - thread.stop(); - } - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + */ + +package org.solovyev.android.calculator.model; + +import android.content.Context; +import android.content.SharedPreferences; +import jscl.*; +import jscl.math.Generic; +import jscl.math.function.Function; +import jscl.math.function.IConstant; +import jscl.math.operator.Operator; +import jscl.text.ParseInterruptedException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.calculator.*; +import org.solovyev.android.calculator.jscl.JsclOperation; +import org.solovyev.android.calculator.text.TextProcessor; +import org.solovyev.android.msg.AndroidMessage; +import org.solovyev.android.prefs.BooleanPreference; +import org.solovyev.android.prefs.Preference; +import org.solovyev.android.prefs.StringPreference; +import org.solovyev.common.MutableObject; +import org.solovyev.common.msg.MessageRegistry; +import org.solovyev.common.msg.MessageType; +import org.solovyev.common.text.EnumMapper; +import org.solovyev.common.text.NumberMapper; +import org.solovyev.common.text.StringUtils; + +import java.text.DecimalFormatSymbols; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * User: serso + * Date: 9/12/11 + * Time: 11:38 PM + */ + +public enum CalculatorEngine implements JCalculatorEngine { + + instance; + + private static final String GROUPING_SEPARATOR_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_calc_grouping_separator"; + + private static final String MULTIPLICATION_SIGN_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_calc_multiplication_sign"; + private static final String MULTIPLICATION_SIGN_DEFAULT = "×"; + + private static final String MAX_CALCULATION_TIME_P_KEY = "calculation.max_calculation_time"; + private static final String MAX_CALCULATION_TIME_DEFAULT = "5"; + + private static final String SCIENCE_NOTATION_P_KEY = "calculation.output.science_notation"; + private static final boolean SCIENCE_NOTATION_DEFAULT = false; + + private static final String ROUND_RESULT_P_KEY = "org.solovyev.android.calculator.CalculatorModel_round_result"; + private static final boolean ROUND_RESULT_DEFAULT = true; + + private static final String RESULT_PRECISION_P_KEY = "org.solovyev.android.calculator.CalculatorModel_result_precision"; + private static final String RESULT_PRECISION_DEFAULT = "5"; + + private static final String NUMERAL_BASES_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_numeral_bases"; + private static final String NUMERAL_BASES_DEFAULT = "dec"; + + private static final String ANGLE_UNITS_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_angle_units"; + private static final String ANGLE_UNITS_DEFAULT = "deg"; + + public static class Preferences { + public static final Preference groupingSeparator = StringPreference.newInstance(GROUPING_SEPARATOR_P_KEY, JsclMathEngine.GROUPING_SEPARATOR_DEFAULT); + public static final Preference multiplicationSign = StringPreference.newInstance(MULTIPLICATION_SIGN_P_KEY, MULTIPLICATION_SIGN_DEFAULT); + public static final Preference precision = StringPreference.newInstance(RESULT_PRECISION_P_KEY, RESULT_PRECISION_DEFAULT, new NumberMapper(Integer.class)); + public static final Preference roundResult = new BooleanPreference(ROUND_RESULT_P_KEY, ROUND_RESULT_DEFAULT); + public static final Preference numeralBase = StringPreference.newInstance(NUMERAL_BASES_P_KEY, NUMERAL_BASES_DEFAULT, EnumMapper.newInstance(NumeralBase.class)); + public static final Preference angleUnit = StringPreference.newInstance(ANGLE_UNITS_P_KEY, ANGLE_UNITS_DEFAULT, EnumMapper.newInstance(AngleUnit.class)); + public static final Preference scienceNotation = new BooleanPreference(SCIENCE_NOTATION_P_KEY, SCIENCE_NOTATION_DEFAULT); + public static final Preference maxCalculationTime = StringPreference.newInstance(MAX_CALCULATION_TIME_P_KEY, MAX_CALCULATION_TIME_DEFAULT, new NumberMapper(Integer.class)); + + private static final List preferenceKeys = new ArrayList(); + + static { + preferenceKeys.add(groupingSeparator.getKey()); + preferenceKeys.add(multiplicationSign.getKey()); + preferenceKeys.add(precision.getKey()); + preferenceKeys.add(roundResult.getKey()); + preferenceKeys.add(numeralBase.getKey()); + preferenceKeys.add(angleUnit.getKey()); + preferenceKeys.add(scienceNotation.getKey()); + preferenceKeys.add(maxCalculationTime.getKey()); + } + + @NotNull + public static List getPreferenceKeys() { + return Collections.unmodifiableList(preferenceKeys); + } + } + + @NotNull + private final Object lock = new Object(); + + @NotNull + private MathEngine engine = JsclMathEngine.instance; + + @NotNull + public final TextProcessor preprocessor = ToJsclTextProcessor.getInstance(); + + @NotNull + private final AndroidMathRegistry varsRegistry = new AndroidVarsRegistryImpl(engine.getConstantsRegistry()); + + @NotNull + private final AndroidMathRegistry functionsRegistry = new AndroidFunctionsMathRegistry(engine.getFunctionsRegistry()); + + @NotNull + private final AndroidMathRegistry operatorsRegistry = new AndroidOperatorsMathRegistry(engine.getOperatorsRegistry()); + + private final AndroidMathRegistry postfixFunctionsRegistry = new AndroidPostfixFunctionsRegistry(engine.getPostfixFunctionsRegistry()); + + @Nullable + private ThreadKiller threadKiller = new AndroidThreadKiller(); + + // calculation thread timeout in seconds, after timeout thread would be interrupted + private int timeout = Integer.valueOf(MAX_CALCULATION_TIME_DEFAULT); + + @NotNull + private String multiplicationSign = MULTIPLICATION_SIGN_DEFAULT; + + CalculatorEngine() { + this.engine.setRoundResult(true); + this.engine.setUseGroupingSeparator(true); + } + + @Override + @NotNull + public String getMultiplicationSign() { + return multiplicationSign; + } + + public void setMultiplicationSign(@NotNull String multiplicationSign) { + this.multiplicationSign = multiplicationSign; + } + + public CalculatorOutput evaluate(@NotNull JsclOperation operation, + @NotNull String expression) throws CalculatorParseException, CalculatorEvalException { + return evaluate(operation, expression, null); + } + + public CalculatorOutput evaluate(@NotNull final JsclOperation operation, + @NotNull String expression, + @Nullable MessageRegistry mr) throws CalculatorParseException, CalculatorEvalException { + synchronized (lock) { + final StringBuilder sb = new StringBuilder(); + + final PreparedExpression preparedExpression = preprocessor.process(expression); + sb.append(preparedExpression); + + //Log.d(CalculatorEngine.class.getName(), "Preprocessed expression: " + preparedExpression); + /*if (operation == JsclOperation.numeric && preparedExpression.isExistsUndefinedVar()) { + operation = JsclOperation.simplify; + + if (mr != null) { + final String undefinedVars = CollectionsUtils.formatValue(preparedExpression.getUndefinedVars(), ", ", new Formatter() { + @Override + public String formatValue(@Nullable Var var) throws IllegalArgumentException { + return var != null ? var.getName() : ""; + } + }); + + mr.addMessage(new AndroidMessage(R.string.c_simplify_instead_of_numeric, MessageType.info, undefinedVars)); + } + }*/ + + final String jsclExpression = sb.toString(); + + final MutableObject calculationResult = new MutableObject(null); + final MutableObject parseException = new MutableObject(null); + final MutableObject evalException = new MutableObject(null); + final MutableObject calculationThread = new MutableObject(null); + + final CountDownLatch latch = new CountDownLatch(1); + + new Thread(new Runnable() { + @Override + public void run() { + final Thread thread = Thread.currentThread(); + try { + //Log.d(CalculatorEngine.class.getName(), "Calculation thread started work: " + thread.getName()); + //System.out.println(jsclExpression); + calculationThread.setObject(thread); + final Generic genericResult = operation.evaluateGeneric(jsclExpression); + + // NOTE: toString() method must be called here as ArithmeticOperationException may occur in it (just to avoid later check!) + genericResult.toString(); + + calculationResult.setObject(genericResult); + } catch (AbstractJsclArithmeticException e) { + evalException.setObject(new CalculatorEvalException(e, e, jsclExpression)); + } catch (ArithmeticException e) { + //System.out.println(e.getMessage()); + final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_1, MessageType.error, CalculatorApplication.getInstance(), e.getMessage()); + parseException.setObject(new CalculatorParseException(jsclExpression, androidMessage)); + } catch (StackOverflowError e) { + //System.out.println(StringUtils.fromStackTrace(e.getStackTrace())); + final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_2, MessageType.error, CalculatorApplication.getInstance()); + parseException.setObject(new CalculatorParseException(jsclExpression, androidMessage)); + } catch (jscl.text.ParseException e) { + //System.out.println(e.getMessage()); + parseException.setObject(new CalculatorParseException(e)); + } catch (ParseInterruptedException e) { + //System.out.println(e.getMessage()); + // do nothing - we ourselves interrupt the calculations + } finally { + //Log.d(CalculatorEngine.class.getName(), "Calculation thread ended work: " + thread.getName()); + calculationThread.setObject(null); + latch.countDown(); + } + } + }).start(); + + try { + //Log.d(CalculatorEngine.class.getName(), "Main thread is waiting: " + Thread.currentThread().getName()); + latch.await(timeout, TimeUnit.SECONDS); + //Log.d(CalculatorEngine.class.getName(), "Main thread got up: " + Thread.currentThread().getName()); + + final CalculatorParseException parseExceptionObject = parseException.getObject(); + final CalculatorEvalException evalExceptionObject = evalException.getObject(); + final Object calculationResultLocal = calculationResult.getObject(); + final Thread calculationThreadLocal = calculationThread.getObject(); + + if (calculationThreadLocal != null) { + if (threadKiller != null) { + threadKiller.killThread(calculationThreadLocal); + } + //calculationThreadLocal.stop(); + } + + if (parseExceptionObject != null || evalExceptionObject != null) { + if (operation == JsclOperation.numeric && + (preparedExpression.isExistsUndefinedVar() || (evalExceptionObject != null && evalExceptionObject.getCause() instanceof NumeralBaseException))) { + return evaluate(JsclOperation.simplify, expression, mr); + } + + if (parseExceptionObject != null) { + throw parseExceptionObject; + } else { + throw evalExceptionObject; + } + } + + if (calculationResultLocal == null) { + final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_3, MessageType.error, CalculatorApplication.getInstance()); + throw new CalculatorParseException(jsclExpression, androidMessage); + } + + } catch (InterruptedException e) { + final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_4, MessageType.error, CalculatorApplication.getInstance()); + throw new CalculatorParseException(jsclExpression, androidMessage); + } + + final Generic genericResult = calculationResult.getObject(); + + return new CalculatorOutputImpl(operation.getFromProcessor().process(genericResult), operation, genericResult); + } + } + + public void setPrecision(int precision) { + this.getEngine().setPrecision(precision); + } + + public void setRoundResult(boolean roundResult) { + this.getEngine().setRoundResult(roundResult); + } + + public void init(@Nullable Context context, @Nullable SharedPreferences preferences) { + synchronized (lock) { + reset(context, preferences); + } + } + + public void reset(@Nullable Context context, @Nullable SharedPreferences preferences) { + synchronized (lock) { + softReset(context, preferences); + + varsRegistry.load(context, preferences); + functionsRegistry.load(context, preferences); + operatorsRegistry.load(context, preferences); + postfixFunctionsRegistry.load(context, preferences); + } + } + + public void softReset(@Nullable Context context, @Nullable SharedPreferences preferences) { + synchronized (lock) { + if (preferences != null) { + this.setPrecision(Preferences.precision.getPreference(preferences)); + this.setRoundResult(Preferences.roundResult.getPreference(preferences)); + this.setAngleUnits(getAngleUnitsFromPrefs(preferences)); + this.setNumeralBase(getNumeralBaseFromPrefs(preferences)); + this.setMultiplicationSign(Preferences.multiplicationSign.getPreference(preferences)); + this.setScienceNotation(Preferences.scienceNotation.getPreference(preferences)); + this.setTimeout(Preferences.maxCalculationTime.getPreference(preferences)); + + final String groupingSeparator = Preferences.groupingSeparator.getPreference(preferences); + if (StringUtils.isEmpty(groupingSeparator)) { + this.getEngine().setUseGroupingSeparator(false); + } else { + this.getEngine().setUseGroupingSeparator(true); + this.getEngine().setGroupingSeparator(groupingSeparator.charAt(0)); + } + } + } + } + + + @NotNull + public NumeralBase getNumeralBaseFromPrefs(@NotNull SharedPreferences preferences) { + return Preferences.numeralBase.getPreference(preferences); + } + + @NotNull + public AngleUnit getAngleUnitsFromPrefs(@NotNull SharedPreferences preferences) { + return Preferences.angleUnit.getPreference(preferences); + } + + //for tests only + void setDecimalGroupSymbols(@NotNull DecimalFormatSymbols decimalGroupSymbols) { + synchronized (lock) { + this.getEngine().setDecimalGroupSymbols(decimalGroupSymbols); + } + } + + @Override + @NotNull + public AndroidMathRegistry getVarsRegistry() { + return varsRegistry; + } + + @Override + @NotNull + public AndroidMathRegistry getFunctionsRegistry() { + return functionsRegistry; + } + + @Override + @NotNull + public AndroidMathRegistry getOperatorsRegistry() { + return operatorsRegistry; + } + + @Override + @NotNull + public AndroidMathRegistry getPostfixFunctionsRegistry() { + return postfixFunctionsRegistry; + } + + @Override + @NotNull + public MathEngine getEngine() { + return engine; + } + + // package protected for tests + void setTimeout(int timeout) { + this.timeout = timeout; + } + + public void setAngleUnits(@NotNull AngleUnit angleUnits) { + getEngine().setAngleUnits(angleUnits); + } + + public void setScienceNotation(boolean scienceNotation) { + getEngine().setScienceNotation(scienceNotation); + } + + public void setNumeralBase(@NotNull NumeralBase numeralBase) { + getEngine().setNumeralBase(numeralBase); + } + + // for tests only + void setThreadKiller(@Nullable ThreadKiller threadKiller) { + this.threadKiller = threadKiller; + } + + private static interface ThreadKiller { + void killThread(@NotNull Thread thread); + } + + private static class AndroidThreadKiller implements ThreadKiller { + @Override + public void killThread(@NotNull Thread thread) { + thread.setPriority(Thread.MIN_PRIORITY); + thread.interrupt(); + } + } + + public static class ThreadKillerImpl implements ThreadKiller { + @Override + public void killThread(@NotNull Thread thread) { + thread.setPriority(Thread.MIN_PRIORITY); + thread.stop(); + } + } +} diff --git a/calculatorpp/src/test/java/org/solovyev/android/calculator/history/HistoryUtilsTest.java b/calculatorpp/src/test/java/org/solovyev/android/calculator/history/HistoryUtilsTest.java index 7d427a1d..4247f1b8 100644 --- a/calculatorpp/src/test/java/org/solovyev/android/calculator/history/HistoryUtilsTest.java +++ b/calculatorpp/src/test/java/org/solovyev/android/calculator/history/HistoryUtilsTest.java @@ -1,321 +1,321 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - * or visit http://se.solovyev.org - */ - -package org.solovyev.android.calculator.history; - -import jscl.math.Generic; -import junit.framework.Assert; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.junit.Test; -import org.solovyev.android.calculator.Editor; -import org.solovyev.android.calculator.JCalculatorDisplay; -import org.solovyev.android.calculator.jscl.JsclOperation; -import org.solovyev.common.equals.CollectionEqualizer; -import org.solovyev.common.equals.EqualsTool; -import org.solovyev.common.history.HistoryHelper; -import org.solovyev.common.history.SimpleHistoryHelper; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -/** - * User: serso - * Date: 12/17/11 - * Time: 10:01 PM - */ -public class HistoryUtilsTest { - - @Test - public void testFromXml() throws Exception { - - } - - private static final String emptyHistory = "\n" + - " \n" + - ""; - - private static final String toXml1 = "\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " 3\n" + - " 1+1\n" + - " \n" + - " \n" + - " \n" + - " 1\n" + - " Error\n" + - " \n" + - " simplify\n" + - " \n" + - " \n" + - " \n" + - ""; - - private static final String toXml2 = "\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " 3\n" + - " 1+1\n" + - " \n" + - " \n" + - " \n" + - " 1\n" + - " Error\n" + - " \n" + - " simplify\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " 2\n" + - " 5/6\n" + - " \n" + - " \n" + - " \n" + - " 3\n" + - " 5/6\n" + - " \n" + - " numeric\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " 1\n" + - " null\n" + - " \n" + - " \n" + - " \n" + - " 1\n" + - " Error\n" + - " \n" + - " elementary\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " 0\n" + - " 4+5/35sin(41)+dfdsfsdfs\n" + - " \n" + - " \n" + - " \n" + - " 1\n" + - " 4+5/35sin(41)+dfdsfsdfs\n" + - " \n" + - " numeric\n" + - " \n" + - " \n" + - " \n" + - ""; - - @Test - public void testToXml() throws Exception { - final Date date = new Date(100000000); - - HistoryHelper history = new SimpleHistoryHelper(); - - JCalculatorDisplay calculatorDisplay = new TestCalculatorDisplay(); - calculatorDisplay.setErrorMessage("error_msg1"); - calculatorDisplay.setText("Error"); - calculatorDisplay.setSelection(1); - calculatorDisplay.setJsclOperation(JsclOperation.simplify); - - Editor calculatorEditor = new TestEditor(); - calculatorEditor.setSelection(3); - calculatorEditor.setText("1+1"); - - CalculatorHistoryState state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay); - state.setTime(date.getTime()); - history.addState(state); - - Assert.assertEquals(emptyHistory, HistoryUtils.toXml(history.getStates())); - - - state.setSaved(true); - - Assert.assertEquals(toXml1, HistoryUtils.toXml(history.getStates())); - - calculatorDisplay = new TestCalculatorDisplay(); - calculatorDisplay.setErrorMessage(null); - calculatorDisplay.setText("5/6"); - calculatorDisplay.setSelection(3); - calculatorDisplay.setJsclOperation(JsclOperation.numeric); - - calculatorEditor = new TestEditor(); - calculatorEditor.setSelection(2); - calculatorEditor.setText("5/6"); - - state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay); - state.setSaved(true); - state.setTime(date.getTime()); - history.addState(state); - - calculatorDisplay = new TestCalculatorDisplay(); - calculatorDisplay.setErrorMessage("error_msg2"); - calculatorDisplay.setText("Error"); - calculatorDisplay.setSelection(1); - calculatorDisplay.setJsclOperation(JsclOperation.elementary); - - calculatorEditor = new TestEditor(); - calculatorEditor.setSelection(1); - calculatorEditor.setText(null); - - state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay); - state.setSaved(true); - state.setTime(date.getTime()); - history.addState(state); - - calculatorDisplay = new TestCalculatorDisplay(); - calculatorDisplay.setErrorMessage(null); - calculatorDisplay.setText("4+5/35sin(41)+dfdsfsdfs"); - calculatorDisplay.setSelection(1); - calculatorDisplay.setJsclOperation(JsclOperation.numeric); - - calculatorEditor = new TestEditor(); - calculatorEditor.setSelection(0); - calculatorEditor.setText("4+5/35sin(41)+dfdsfsdfs"); - - state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay); - state.setSaved(true); - state.setTime(date.getTime()); - history.addState(state); - - String xml = HistoryUtils.toXml(history.getStates()); - Assert.assertEquals(toXml2, xml); - - final List fromXml = new ArrayList(); - final HistoryHelper historyFromXml = new SimpleHistoryHelper(); - HistoryUtils.fromXml(xml, fromXml); - for (CalculatorHistoryState historyState : fromXml) { - historyFromXml.addState(historyState); - } - - Assert.assertEquals(history.getStates().size(), historyFromXml.getStates().size()); - - for (CalculatorHistoryState historyState : history.getStates()) { - historyState.setId(0); - historyState.setSaved(true); - } - for (CalculatorHistoryState historyState : historyFromXml.getStates()) { - historyState.setId(0); - historyState.setSaved(true); - } - Assert.assertTrue(EqualsTool.areEqual(history.getStates(), historyFromXml.getStates(), new CollectionEqualizer(null))); - } - - - private static class TestCalculatorDisplay implements JCalculatorDisplay { - - @NotNull - private final TestEditor testEditor = new TestEditor(); - - private boolean valid; - - private String errorMessage; - - private JsclOperation operation; - - private Generic genericResult; - - @Override - public boolean isValid() { - return this.valid; - } - - @Override - public void setValid(boolean valid) { - this.valid = valid; - } - - @Override - public String getErrorMessage() { - return this.errorMessage; - } - - @Override - public void setErrorMessage(@Nullable String errorMessage) { - this.errorMessage = errorMessage; - } - - @Override - public void setJsclOperation(@NotNull JsclOperation jsclOperation) { - this.operation = jsclOperation; - } - - @NotNull - @Override - public JsclOperation getJsclOperation() { - return this.operation; - } - - @Override - public void setGenericResult(@Nullable Generic genericResult) { - this.genericResult = genericResult; - } - - @Override - public Generic getGenericResult() { - return this.genericResult; - } - - @Override - public CharSequence getText() { - return this.testEditor.getText(); - } - - @Override - public void setText(@Nullable CharSequence text) { - this.testEditor.setText(text); - } - - @Override - public int getSelection() { - return this.testEditor.getSelection(); - } - - @Override - public void setSelection(int selection) { - this.testEditor.setSelection(selection); - } - } - - private static class TestEditor implements Editor { - - @Nullable - private CharSequence text; - - private int selection; - - @Nullable - @Override - public CharSequence getText() { - return this.text; - } - - @Override - public void setText(@Nullable CharSequence text) { - this.text = text; - } - - @Override - public int getSelection() { - return this.selection; - } - - @Override - public void setSelection(int selection) { - this.selection = selection; - } - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + * or visit http://se.solovyev.org + */ + +package org.solovyev.android.calculator.history; + +import jscl.math.Generic; +import junit.framework.Assert; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.junit.Test; +import org.solovyev.android.calculator.CalculatorDisplay; +import org.solovyev.android.calculator.Editor; +import org.solovyev.android.calculator.jscl.JsclOperation; +import org.solovyev.common.equals.CollectionEqualizer; +import org.solovyev.common.equals.EqualsTool; +import org.solovyev.common.history.HistoryHelper; +import org.solovyev.common.history.SimpleHistoryHelper; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * User: serso + * Date: 12/17/11 + * Time: 10:01 PM + */ +public class HistoryUtilsTest { + + @Test + public void testFromXml() throws Exception { + + } + + private static final String emptyHistory = "\n" + + " \n" + + ""; + + private static final String toXml1 = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " 3\n" + + " 1+1\n" + + " \n" + + " \n" + + " \n" + + " 1\n" + + " Error\n" + + " \n" + + " simplify\n" + + " \n" + + " \n" + + " \n" + + ""; + + private static final String toXml2 = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " 3\n" + + " 1+1\n" + + " \n" + + " \n" + + " \n" + + " 1\n" + + " Error\n" + + " \n" + + " simplify\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " 2\n" + + " 5/6\n" + + " \n" + + " \n" + + " \n" + + " 3\n" + + " 5/6\n" + + " \n" + + " numeric\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " 1\n" + + " null\n" + + " \n" + + " \n" + + " \n" + + " 1\n" + + " Error\n" + + " \n" + + " elementary\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " 0\n" + + " 4+5/35sin(41)+dfdsfsdfs\n" + + " \n" + + " \n" + + " \n" + + " 1\n" + + " 4+5/35sin(41)+dfdsfsdfs\n" + + " \n" + + " numeric\n" + + " \n" + + " \n" + + " \n" + + ""; + + @Test + public void testToXml() throws Exception { + final Date date = new Date(100000000); + + HistoryHelper history = new SimpleHistoryHelper(); + + CalculatorDisplay calculatorDisplay = new TestCalculatorDisplay(); + calculatorDisplay.setErrorMessage("error_msg1"); + calculatorDisplay.setText("Error"); + calculatorDisplay.setSelection(1); + calculatorDisplay.setJsclOperation(JsclOperation.simplify); + + Editor calculatorEditor = new TestEditor(); + calculatorEditor.setSelection(3); + calculatorEditor.setText("1+1"); + + CalculatorHistoryState state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay); + state.setTime(date.getTime()); + history.addState(state); + + Assert.assertEquals(emptyHistory, HistoryUtils.toXml(history.getStates())); + + + state.setSaved(true); + + Assert.assertEquals(toXml1, HistoryUtils.toXml(history.getStates())); + + calculatorDisplay = new TestCalculatorDisplay(); + calculatorDisplay.setErrorMessage(null); + calculatorDisplay.setText("5/6"); + calculatorDisplay.setSelection(3); + calculatorDisplay.setJsclOperation(JsclOperation.numeric); + + calculatorEditor = new TestEditor(); + calculatorEditor.setSelection(2); + calculatorEditor.setText("5/6"); + + state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay); + state.setSaved(true); + state.setTime(date.getTime()); + history.addState(state); + + calculatorDisplay = new TestCalculatorDisplay(); + calculatorDisplay.setErrorMessage("error_msg2"); + calculatorDisplay.setText("Error"); + calculatorDisplay.setSelection(1); + calculatorDisplay.setJsclOperation(JsclOperation.elementary); + + calculatorEditor = new TestEditor(); + calculatorEditor.setSelection(1); + calculatorEditor.setText(null); + + state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay); + state.setSaved(true); + state.setTime(date.getTime()); + history.addState(state); + + calculatorDisplay = new TestCalculatorDisplay(); + calculatorDisplay.setErrorMessage(null); + calculatorDisplay.setText("4+5/35sin(41)+dfdsfsdfs"); + calculatorDisplay.setSelection(1); + calculatorDisplay.setJsclOperation(JsclOperation.numeric); + + calculatorEditor = new TestEditor(); + calculatorEditor.setSelection(0); + calculatorEditor.setText("4+5/35sin(41)+dfdsfsdfs"); + + state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay); + state.setSaved(true); + state.setTime(date.getTime()); + history.addState(state); + + String xml = HistoryUtils.toXml(history.getStates()); + Assert.assertEquals(toXml2, xml); + + final List fromXml = new ArrayList(); + final HistoryHelper historyFromXml = new SimpleHistoryHelper(); + HistoryUtils.fromXml(xml, fromXml); + for (CalculatorHistoryState historyState : fromXml) { + historyFromXml.addState(historyState); + } + + Assert.assertEquals(history.getStates().size(), historyFromXml.getStates().size()); + + for (CalculatorHistoryState historyState : history.getStates()) { + historyState.setId(0); + historyState.setSaved(true); + } + for (CalculatorHistoryState historyState : historyFromXml.getStates()) { + historyState.setId(0); + historyState.setSaved(true); + } + Assert.assertTrue(EqualsTool.areEqual(history.getStates(), historyFromXml.getStates(), new CollectionEqualizer(null))); + } + + + private static class TestCalculatorDisplay implements CalculatorDisplay { + + @NotNull + private final TestEditor testEditor = new TestEditor(); + + private boolean valid; + + private String errorMessage; + + private JsclOperation operation; + + private Generic genericResult; + + @Override + public boolean isValid() { + return this.valid; + } + + @Override + public void setValid(boolean valid) { + this.valid = valid; + } + + @Override + public String getErrorMessage() { + return this.errorMessage; + } + + @Override + public void setErrorMessage(@Nullable String errorMessage) { + this.errorMessage = errorMessage; + } + + @Override + public void setJsclOperation(@NotNull JsclOperation jsclOperation) { + this.operation = jsclOperation; + } + + @NotNull + @Override + public JsclOperation getJsclOperation() { + return this.operation; + } + + @Override + public void setGenericResult(@Nullable Generic genericResult) { + this.genericResult = genericResult; + } + + @Override + public Generic getGenericResult() { + return this.genericResult; + } + + @Override + public CharSequence getText() { + return this.testEditor.getText(); + } + + @Override + public void setText(@Nullable CharSequence text) { + this.testEditor.setText(text); + } + + @Override + public int getSelection() { + return this.testEditor.getSelection(); + } + + @Override + public void setSelection(int selection) { + this.testEditor.setSelection(selection); + } + } + + private static class TestEditor implements Editor { + + @Nullable + private CharSequence text; + + private int selection; + + @Nullable + @Override + public CharSequence getText() { + return this.text; + } + + @Override + public void setText(@Nullable CharSequence text) { + this.text = text; + } + + @Override + public int getSelection() { + return this.selection; + } + + @Override + public void setSelection(int selection) { + this.selection = selection; + } + } +} diff --git a/calculatorpp/src/test/java/org/solovyev/android/calculator/model/CalculatorEngineTest.java b/calculatorpp/src/test/java/org/solovyev/android/calculator/model/CalculatorEngineTest.java index 8bd96dcd..375969ef 100644 --- a/calculatorpp/src/test/java/org/solovyev/android/calculator/model/CalculatorEngineTest.java +++ b/calculatorpp/src/test/java/org/solovyev/android/calculator/model/CalculatorEngineTest.java @@ -1,445 +1,445 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - */ - -package org.solovyev.android.calculator.model; - -import jscl.AngleUnit; -import jscl.JsclMathEngine; -import jscl.NumeralBase; -import jscl.math.Expression; -import jscl.math.Generic; -import jscl.math.function.Constant; -import jscl.math.function.CustomFunction; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; -import org.solovyev.android.calculator.CalculatorEvalException; -import org.solovyev.android.calculator.CalculatorParseException; -import org.solovyev.android.calculator.jscl.JsclOperation; - -import java.text.DecimalFormatSymbols; -import java.util.Locale; - -import static junit.framework.Assert.fail; - -/** - * User: serso - * Date: 9/17/11 - * Time: 9:47 PM - */ - -public class CalculatorEngineTest { - - @BeforeClass - public static void setUp() throws Exception { - CalculatorEngine.instance.init(null, null); - CalculatorEngine.instance.setPrecision(3); - CalculatorEngine.instance.setThreadKiller(new CalculatorEngine.ThreadKillerImpl()); - } - - @Test - public void testDegrees() throws Exception { - final CalculatorEngine cm = CalculatorEngine.instance; - - final AngleUnit defaultAngleUnit = cm.getEngine().getAngleUnits(); - try { - cm.getEngine().setAngleUnits(AngleUnit.rad); - cm.setPrecision(3); - try { - Assert.assertEquals("0.017", cm.evaluate(JsclOperation.numeric, "°")); - fail(); - } catch (CalculatorParseException e) { - - } - - Assert.assertEquals("0.017", cm.evaluate(JsclOperation.numeric, "1°").getResult()); - Assert.assertEquals("0.349", cm.evaluate(JsclOperation.numeric, "20.0°").getResult()); - Assert.assertEquals("0.5", cm.evaluate(JsclOperation.numeric, "sin(30°)").getResult()); - Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "asin(sin(30°))").getResult()); - Assert.assertEquals("∂(cos(t), t, t, 1°)", cm.evaluate(JsclOperation.numeric, "∂(cos(t),t,t,1°)").getResult()); - - Assert.assertEquals("∂(cos(t), t, t, 1°)", cm.evaluate(JsclOperation.simplify, "∂(cos(t),t,t,1°)").getResult()); - } finally { - cm.getEngine().setAngleUnits(defaultAngleUnit); - } - } - - @Test - public void testLongExecution() throws Exception { - final CalculatorEngine cm = CalculatorEngine.instance; - - try { - cm.evaluate(JsclOperation.numeric, "3^10^10^10"); - Assert.fail(); - } catch (CalculatorParseException e) { - if (e.getMessageCode().equals(Messages.msg_3)) { - - } else { - System.out.print(e.getCause().getMessage()); - Assert.fail(); - } - } - - try { - cm.evaluate(JsclOperation.numeric, "9999999!"); - Assert.fail(); - } catch (CalculatorParseException e) { - if (e.getMessageCode().equals(Messages.msg_3)) { - - } else { - System.out.print(e.getCause().getMessage()); - Assert.fail(); - } - } - - /*final long start = System.currentTimeMillis(); - try { - cm.evaluate(JsclOperation.numeric, "3^10^10^10"); - Assert.fail(); - } catch (ParseException e) { - if (e.getMessage().startsWith("Too long calculation")) { - final long end = System.currentTimeMillis(); - Assert.assertTrue(end - start < 1000); - } else { - Assert.fail(); - } - }*/ - - } - - @Test - public void testEvaluate() throws Exception { - final CalculatorEngine cm = CalculatorEngine.instance; - - Assert.assertEquals("cos(t)+10%", cm.evaluate(JsclOperation.simplify, "cos(t)+10%").getResult()); - - final Generic expression = cm.getEngine().simplifyGeneric("cos(t)+10%"); - expression.substitute(new Constant("t"), Expression.valueOf(100d)); - - Assert.assertEquals("it", cm.evaluate(JsclOperation.simplify, "it").getResult()); - Assert.assertEquals("10%", cm.evaluate(JsclOperation.simplify, "10%").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(0, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq(1, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq( 1, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.simplify, "eq( 1, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "lg(10)").getResult()); - Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "2+2").getResult()); - final AngleUnit defaultAngleUnit = cm.getEngine().getAngleUnits(); - try { - cm.getEngine().setAngleUnits(AngleUnit.rad); - Assert.assertEquals("-0.757", cm.evaluate(JsclOperation.numeric, "sin(4)").getResult()); - Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "asin(0.5)").getResult()); - Assert.assertEquals("-0.396", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)").getResult()); - Assert.assertEquals("-0.56", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)√(2)").getResult()); - Assert.assertEquals("-0.56", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)√(2)").getResult()); - } finally { - cm.getEngine().setAngleUnits(defaultAngleUnit); - } - Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "e^2").getResult()); - Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "exp(1)^2").getResult()); - Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "exp(2)").getResult()); - Assert.assertEquals("2+i", cm.evaluate(JsclOperation.numeric, "2*1+√(-1)").getResult()); - try { - cm.getEngine().setAngleUnits(AngleUnit.rad); - Assert.assertEquals("0.921+Πi", cm.evaluate(JsclOperation.numeric, "ln(5cosh(38π√(2cos(2))))").getResult()); - Assert.assertEquals("-3.41+3.41i", cm.evaluate(JsclOperation.numeric, "(5tan(2i)+2i)/(1-i)").getResult()); - } finally { - cm.getEngine().setAngleUnits(defaultAngleUnit); - } - Assert.assertEquals("7.389i", cm.evaluate(JsclOperation.numeric, "iexp(2)").getResult()); - Assert.assertEquals("2+7.389i", cm.evaluate(JsclOperation.numeric, "2+iexp(2)").getResult()); - Assert.assertEquals("2+7.389i", cm.evaluate(JsclOperation.numeric, "2+√(-1)exp(2)").getResult()); - Assert.assertEquals("2-2.5i", cm.evaluate(JsclOperation.numeric, "2-2.5i").getResult()); - Assert.assertEquals("-2-2.5i", cm.evaluate(JsclOperation.numeric, "-2-2.5i").getResult()); - Assert.assertEquals("-2+2.5i", cm.evaluate(JsclOperation.numeric, "-2+2.5i").getResult()); - Assert.assertEquals("-2+2.1i", cm.evaluate(JsclOperation.numeric, "-2+2.1i").getResult()); - Assert.assertEquals("-0.1-0.2i", cm.evaluate(JsclOperation.numeric, "(1-i)/(2+6i)").getResult()); - - junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "4!").getResult()); - junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "(2+2)!").getResult()); - junit.framework.Assert.assertEquals("120", cm.evaluate(JsclOperation.numeric, "(2+2+1)!").getResult()); - junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "(2.0+2.0)!").getResult()); - junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "4.0!").getResult()); - junit.framework.Assert.assertEquals("720", cm.evaluate(JsclOperation.numeric, "(3!)!").getResult()); - junit.framework.Assert.assertEquals("36", Expression.valueOf("3!^2").numeric().toString()); - junit.framework.Assert.assertEquals("3", Expression.valueOf("cubic(27)").numeric().toString()); - try { - junit.framework.Assert.assertEquals("√(-1)!", cm.evaluate(JsclOperation.numeric, "i!").getResult()); - fail(); - } catch (CalculatorParseException e) { - } - - junit.framework.Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "(π/π)!").getResult()); - - try { - junit.framework.Assert.assertEquals("i", cm.evaluate(JsclOperation.numeric, "(-1)i!").getResult()); - fail(); - } catch (CalculatorParseException e) { - - } - junit.framework.Assert.assertEquals("24i", cm.evaluate(JsclOperation.numeric, "4!i").getResult()); - - CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("si", 5d)); - - try { - cm.getEngine().setAngleUnits(AngleUnit.rad); - Assert.assertEquals("0.451", cm.evaluate(JsclOperation.numeric, "acos(0.8999999999999811)").getResult()); - Assert.assertEquals("-0.959", cm.evaluate(JsclOperation.numeric, "sin(5)").getResult()); - Assert.assertEquals("-4.795", cm.evaluate(JsclOperation.numeric, "sin(5)si").getResult()); - Assert.assertEquals("-23.973", cm.evaluate(JsclOperation.numeric, "sisin(5)si").getResult()); - Assert.assertEquals("-23.973", cm.evaluate(JsclOperation.numeric, "si*sin(5)si").getResult()); - Assert.assertEquals("-3.309", cm.evaluate(JsclOperation.numeric, "sisin(5si)si").getResult()); - } finally { - cm.getEngine().setAngleUnits(defaultAngleUnit); - } - - CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("s", 1d)); - Assert.assertEquals("5", cm.evaluate(JsclOperation.numeric, "si").getResult()); - - CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("k", 3.5d)); - CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("k1", 4d)); - Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "k11").getResult()); - - CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t", (String) null)); - Assert.assertEquals("11t", cm.evaluate(JsclOperation.numeric, "t11").getResult()); - Assert.assertEquals("11et", cm.evaluate(JsclOperation.numeric, "t11e").getResult()); - Assert.assertEquals("∞", cm.evaluate(JsclOperation.numeric, "∞").getResult()); - Assert.assertEquals("∞", cm.evaluate(JsclOperation.numeric, "Infinity").getResult()); - Assert.assertEquals("11∞t", cm.evaluate(JsclOperation.numeric, "t11∞").getResult()); - Assert.assertEquals("-t+t^3", cm.evaluate(JsclOperation.numeric, "t(t-1)(t+1)").getResult()); - - Assert.assertEquals("100", cm.evaluate(JsclOperation.numeric, "0.1E3").getResult()); - Assert.assertEquals("3.957", cm.evaluate(JsclOperation.numeric, "ln(8)lg(8)+ln(8)").getResult()); - - Assert.assertEquals("0.933", cm.evaluate(JsclOperation.numeric, "0x:E/0x:F").getResult()); - - try { - cm.getEngine().setNumeralBase(NumeralBase.hex); - Assert.assertEquals("E/F", cm.evaluate(JsclOperation.numeric, "0x:E/0x:F").getResult()); - Assert.assertEquals("E/F", cm.evaluate(JsclOperation.simplify, "0x:E/0x:F").getResult()); - Assert.assertEquals("E/F", cm.evaluate(JsclOperation.numeric, "E/F").getResult()); - Assert.assertEquals("E/F", cm.evaluate(JsclOperation.simplify, "E/F").getResult()); - } finally { - cm.getEngine().setNumeralBase(NumeralBase.dec); - } - - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "((((((0))))))").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((0))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))").getResult()); - - - /* Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "30°").getResult()); - Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "(10+20)°").getResult()); - Assert.assertEquals("1.047", cm.evaluate(JsclOperation.numeric, "(10+20)°*2").getResult()); - try { - Assert.assertEquals("0.278", cm.evaluate(JsclOperation.numeric, "30°^2").getResult()); - junit.framework.Assert.fail(); - } catch (ParseException e) { - if ( !e.getMessage().equals("Power operation after postfix function is currently unsupported!") ) { - junit.framework.Assert.fail(); - } - }*/ - -/* try { - cm.setTimeout(5000); - Assert.assertEquals("2", cm.evaluate(JsclOperation.numeric, "2!").getResult()); - } finally { - cm.setTimeout(3000); - }*/ - - CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t", (String) null)); - Assert.assertEquals("2t", cm.evaluate(JsclOperation.simplify, "∂(t^2,t)").getResult()); - Assert.assertEquals("2t", cm.evaluate(JsclOperation.numeric, "∂(t^2,t)").getResult()); - CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t", "2")); - Assert.assertEquals("2t", cm.evaluate(JsclOperation.simplify, "∂(t^2,t)").getResult()); - Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "∂(t^2,t)").getResult()); - - Assert.assertEquals("-x+x*ln(x)", cm.getEngine().simplify("∫(ln(x), x)")); - Assert.assertEquals("-(x-x*ln(x))/(ln(2)+ln(5))", cm.getEngine().simplify("∫(log(10, x), x)")); - - Assert.assertEquals("∫((ln(2)+ln(5))/ln(x), x)", cm.getEngine().simplify("∫(ln(10)/ln(x), x)")); - Assert.assertEquals("∫(ln(10)/ln(x), x)", Expression.valueOf("∫(log(x, 10), x)").expand().toString()); - Assert.assertEquals("∫((ln(2)+ln(5))/ln(x), x)", cm.getEngine().simplify("∫(log(x, 10), x)")); - } - - @Test - public void testFormatting() throws Exception { - final CalculatorEngine ce = CalculatorEngine.instance; - - Assert.assertEquals("12 345", ce.evaluate(JsclOperation.simplify, "12345").getResult()); - - } - - @Test - public void testI() throws CalculatorParseException, CalculatorEvalException { - final CalculatorEngine cm = CalculatorEngine.instance; - - Assert.assertEquals("-i", cm.evaluate(JsclOperation.numeric, "i^3").getResult()); - for (int i = 0; i < 1000; i++) { - double real = (Math.random()-0.5) * 1000; - double imag = (Math.random()-0.5) * 1000; - int exp = (int)(Math.random() * 10); - - final StringBuilder sb = new StringBuilder(); - sb.append(real); - if ( imag > 0 ) { - sb.append("+"); - } - sb.append(imag); - sb.append("^").append(exp); - try { - cm.evaluate(JsclOperation.numeric, sb.toString()).getResult(); - } catch (Throwable e) { - fail(sb.toString()); - } - } - } - - @Test - public void testEmptyFunction() throws Exception { - final CalculatorEngine cm = CalculatorEngine.instance; - try { - cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(acos(acos(acos(acos(acos(acos(acos(acos(cos(cos(cos(cos(cosh(acos(cos(cos(cos(cos(cos(acos(acos(acos(acos(acos(acos(acos(acos(cos(cos(cos(cos(cosh(acos(cos())))))))))))))))))))))))))))))))))))))"); - Assert.fail(); - } catch (CalculatorParseException e) { - } - Assert.assertEquals("0.34+1.382i", cm.evaluate(JsclOperation.numeric, "ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(100)))))))))))))))").getResult()); - try { - cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos())))))))))))))))))))))))))))))))))))"); - Assert.fail(); - } catch (CalculatorParseException e) { - } - - final AngleUnit defaultAngleUnit = cm.getEngine().getAngleUnits(); - try { - cm.getEngine().setAngleUnits(AngleUnit.rad); - Assert.assertEquals("0.739", cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(1))))))))))))))))))))))))))))))))))))").getResult()); - } finally { - cm.getEngine().setAngleUnits(defaultAngleUnit); - } - - CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("si", 5d)); - Assert.assertEquals("5", cm.evaluate(JsclOperation.numeric, "si").getResult()); - - try { - cm.evaluate(JsclOperation.numeric, "sin"); - Assert.fail(); - } catch (CalculatorParseException e) { - } - } - - @Test - public void testRounding() throws Exception { - final CalculatorEngine cm = CalculatorEngine.instance; - - try { - DecimalFormatSymbols decimalGroupSymbols = new DecimalFormatSymbols(Locale.getDefault()); - decimalGroupSymbols.setDecimalSeparator('.'); - decimalGroupSymbols.setGroupingSeparator('\''); - cm.setDecimalGroupSymbols(decimalGroupSymbols); - cm.setPrecision(2); - Assert.assertEquals("12'345'678.9", cm.evaluate(JsclOperation.numeric, "1.23456789E7").getResult()); - cm.setPrecision(10); - Assert.assertEquals("12'345'678.9", cm.evaluate(JsclOperation.numeric, "1.23456789E7").getResult()); - Assert.assertEquals("123'456'789", cm.evaluate(JsclOperation.numeric, "1.234567890E8").getResult()); - Assert.assertEquals("1'234'567'890.1", cm.evaluate(JsclOperation.numeric, "1.2345678901E9").getResult()); - } finally { - cm.setPrecision(3); - DecimalFormatSymbols decimalGroupSymbols = new DecimalFormatSymbols(Locale.getDefault()); - decimalGroupSymbols.setDecimalSeparator('.'); - decimalGroupSymbols.setGroupingSeparator(JsclMathEngine.GROUPING_SEPARATOR_DEFAULT.charAt(0)); - cm.setDecimalGroupSymbols(decimalGroupSymbols); - } - } - - @Test - public void testComparisonFunction() throws Exception { - final CalculatorEngine cm = CalculatorEngine.instance; - - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(0, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq(1, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq(1, 1.0)").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(1, 1.000000000000001)").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(1, 0)").getResult()); - - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "lt(0, 1)").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "lt(1, 1)").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "lt(1, 0)").getResult()); - - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "gt(0, 1)").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "gt(1, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "gt(1, 0)").getResult()); - - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ne(0, 1)").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ne(1, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ne(1, 0)").getResult()); - - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "le(0, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "le(1, 1)").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "le(1, 0)").getResult()); - - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ge(0, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ge(1, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ge(1, 0)").getResult()); - - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ap(0, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ap(1, 1)").getResult()); - //Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ap(1, 1.000000000000001)").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ap(1, 0)").getResult()); - - } - - - @Test - public void testNumeralSystems() throws Exception { - final CalculatorEngine cm = CalculatorEngine.instance; - - Assert.assertEquals("11 259 375", cm.evaluate(JsclOperation.numeric, "0x:ABCDEF").getResult()); - Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "0x:ABCDEF*e").getResult()); - Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "e*0x:ABCDEF").getResult()); - Assert.assertEquals("e", cm.evaluate(JsclOperation.numeric, "e*0x:ABCDEF/0x:ABCDEF").getResult()); - Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "0x:ABCDEF*e*0x:ABCDEF/0x:ABCDEF").getResult()); - Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "c+0x:ABCDEF*e*0x:ABCDEF/0x:ABCDEF-c+0x:C-0x:C").getResult()); - Assert.assertEquals("1 446 257 064 651.832", cm.evaluate(JsclOperation.numeric, "28*28 * sin(28) - 0b:1101 + √(28) + exp ( 28) ").getResult()); - Assert.assertEquals("13", cm.evaluate(JsclOperation.numeric, "0b:1101").getResult()); - - try { - cm.evaluate(JsclOperation.numeric, "0b:π").getResult(); - Assert.fail(); - } catch (CalculatorParseException e) { - // ok - } - - final NumeralBase defaultNumeralBase = cm.getEngine().getNumeralBase(); - try{ - cm.getEngine().setNumeralBase(NumeralBase.bin); - Assert.assertEquals("101", cm.evaluate(JsclOperation.numeric, "10+11").getResult()); - Assert.assertEquals("10/11", cm.evaluate(JsclOperation.numeric, "10/11").getResult()); - - cm.getEngine().setNumeralBase(NumeralBase.hex); - Assert.assertEquals("63 7B", cm.evaluate(JsclOperation.numeric, "56CE+CAD").getResult()); - Assert.assertEquals("E", cm.evaluate(JsclOperation.numeric, "E").getResult()); - } finally { - cm.setNumeralBase(defaultNumeralBase); - } - } - - @Test - public void testLog() throws Exception { - final CalculatorEngine cm = CalculatorEngine.instance; - - Assert.assertEquals("∞", Expression.valueOf("1/0").numeric().toString()); - Assert.assertEquals("∞", Expression.valueOf("ln(10)/ln(1)").numeric().toString()); - - // logarithm - Assert.assertEquals("ln(x)/ln(base)", ((CustomFunction) cm.getFunctionsRegistry().get("log")).getContent()); - Assert.assertEquals("∞", cm.evaluate(JsclOperation.numeric, "log(1, 10)").getResult()); - Assert.assertEquals("3.322", cm.evaluate(JsclOperation.numeric, "log(2, 10)").getResult()); - Assert.assertEquals("1.431", cm.evaluate(JsclOperation.numeric, "log(5, 10)").getResult()); - Assert.assertEquals("0.96", cm.evaluate(JsclOperation.numeric, "log(11, 10)").getResult()); - Assert.assertEquals("1/(bln(a))", cm.evaluate(JsclOperation.simplify, "∂(log(a, b), b)").getResult()); - Assert.assertEquals("-ln(b)/(aln(a)^2)", cm.evaluate(JsclOperation.simplify, "∂(log(a, b), a)").getResult()); - - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + */ + +package org.solovyev.android.calculator.model; + +import jscl.AngleUnit; +import jscl.JsclMathEngine; +import jscl.NumeralBase; +import jscl.math.Expression; +import jscl.math.Generic; +import jscl.math.function.Constant; +import jscl.math.function.CustomFunction; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.solovyev.android.calculator.CalculatorEvalException; +import org.solovyev.android.calculator.CalculatorParseException; +import org.solovyev.android.calculator.jscl.JsclOperation; + +import java.text.DecimalFormatSymbols; +import java.util.Locale; + +import static junit.framework.Assert.fail; + +/** + * User: serso + * Date: 9/17/11 + * Time: 9:47 PM + */ + +public class CalculatorEngineTest { + + @BeforeClass + public static void setUp() throws Exception { + CalculatorEngine.instance.init(null, null); + CalculatorEngine.instance.setPrecision(3); + CalculatorEngine.instance.setThreadKiller(new CalculatorEngine.ThreadKillerImpl()); + } + + @Test + public void testDegrees() throws Exception { + final CalculatorEngine cm = CalculatorEngine.instance; + + final AngleUnit defaultAngleUnit = cm.getEngine().getAngleUnits(); + try { + cm.getEngine().setAngleUnits(AngleUnit.rad); + cm.setPrecision(3); + try { + Assert.assertEquals("0.017", cm.evaluate(JsclOperation.numeric, "°")); + fail(); + } catch (CalculatorParseException e) { + + } + + Assert.assertEquals("0.017", cm.evaluate(JsclOperation.numeric, "1°").getStringResult()); + Assert.assertEquals("0.349", cm.evaluate(JsclOperation.numeric, "20.0°").getStringResult()); + Assert.assertEquals("0.5", cm.evaluate(JsclOperation.numeric, "sin(30°)").getStringResult()); + Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "asin(sin(30°))").getStringResult()); + Assert.assertEquals("∂(cos(t), t, t, 1°)", cm.evaluate(JsclOperation.numeric, "∂(cos(t),t,t,1°)").getStringResult()); + + Assert.assertEquals("∂(cos(t), t, t, 1°)", cm.evaluate(JsclOperation.simplify, "∂(cos(t),t,t,1°)").getStringResult()); + } finally { + cm.getEngine().setAngleUnits(defaultAngleUnit); + } + } + + @Test + public void testLongExecution() throws Exception { + final CalculatorEngine cm = CalculatorEngine.instance; + + try { + cm.evaluate(JsclOperation.numeric, "3^10^10^10"); + Assert.fail(); + } catch (CalculatorParseException e) { + if (e.getMessageCode().equals(Messages.msg_3)) { + + } else { + System.out.print(e.getCause().getMessage()); + Assert.fail(); + } + } + + try { + cm.evaluate(JsclOperation.numeric, "9999999!"); + Assert.fail(); + } catch (CalculatorParseException e) { + if (e.getMessageCode().equals(Messages.msg_3)) { + + } else { + System.out.print(e.getCause().getMessage()); + Assert.fail(); + } + } + + /*final long start = System.currentTimeMillis(); + try { + cm.evaluate(JsclOperation.numeric, "3^10^10^10"); + Assert.fail(); + } catch (ParseException e) { + if (e.getMessage().startsWith("Too long calculation")) { + final long end = System.currentTimeMillis(); + Assert.assertTrue(end - start < 1000); + } else { + Assert.fail(); + } + }*/ + + } + + @Test + public void testEvaluate() throws Exception { + final CalculatorEngine cm = CalculatorEngine.instance; + + Assert.assertEquals("cos(t)+10%", cm.evaluate(JsclOperation.simplify, "cos(t)+10%").getStringResult()); + + final Generic expression = cm.getEngine().simplifyGeneric("cos(t)+10%"); + expression.substitute(new Constant("t"), Expression.valueOf(100d)); + + Assert.assertEquals("it", cm.evaluate(JsclOperation.simplify, "it").getStringResult()); + Assert.assertEquals("10%", cm.evaluate(JsclOperation.simplify, "10%").getStringResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(0, 1)").getStringResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq(1, 1)").getStringResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq( 1, 1)").getStringResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.simplify, "eq( 1, 1)").getStringResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "lg(10)").getStringResult()); + Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "2+2").getStringResult()); + final AngleUnit defaultAngleUnit = cm.getEngine().getAngleUnits(); + try { + cm.getEngine().setAngleUnits(AngleUnit.rad); + Assert.assertEquals("-0.757", cm.evaluate(JsclOperation.numeric, "sin(4)").getStringResult()); + Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "asin(0.5)").getStringResult()); + Assert.assertEquals("-0.396", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)").getStringResult()); + Assert.assertEquals("-0.56", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)√(2)").getStringResult()); + Assert.assertEquals("-0.56", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)√(2)").getStringResult()); + } finally { + cm.getEngine().setAngleUnits(defaultAngleUnit); + } + Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "e^2").getStringResult()); + Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "exp(1)^2").getStringResult()); + Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "exp(2)").getStringResult()); + Assert.assertEquals("2+i", cm.evaluate(JsclOperation.numeric, "2*1+√(-1)").getStringResult()); + try { + cm.getEngine().setAngleUnits(AngleUnit.rad); + Assert.assertEquals("0.921+Πi", cm.evaluate(JsclOperation.numeric, "ln(5cosh(38π√(2cos(2))))").getStringResult()); + Assert.assertEquals("-3.41+3.41i", cm.evaluate(JsclOperation.numeric, "(5tan(2i)+2i)/(1-i)").getStringResult()); + } finally { + cm.getEngine().setAngleUnits(defaultAngleUnit); + } + Assert.assertEquals("7.389i", cm.evaluate(JsclOperation.numeric, "iexp(2)").getStringResult()); + Assert.assertEquals("2+7.389i", cm.evaluate(JsclOperation.numeric, "2+iexp(2)").getStringResult()); + Assert.assertEquals("2+7.389i", cm.evaluate(JsclOperation.numeric, "2+√(-1)exp(2)").getStringResult()); + Assert.assertEquals("2-2.5i", cm.evaluate(JsclOperation.numeric, "2-2.5i").getStringResult()); + Assert.assertEquals("-2-2.5i", cm.evaluate(JsclOperation.numeric, "-2-2.5i").getStringResult()); + Assert.assertEquals("-2+2.5i", cm.evaluate(JsclOperation.numeric, "-2+2.5i").getStringResult()); + Assert.assertEquals("-2+2.1i", cm.evaluate(JsclOperation.numeric, "-2+2.1i").getStringResult()); + Assert.assertEquals("-0.1-0.2i", cm.evaluate(JsclOperation.numeric, "(1-i)/(2+6i)").getStringResult()); + + junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "4!").getStringResult()); + junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "(2+2)!").getStringResult()); + junit.framework.Assert.assertEquals("120", cm.evaluate(JsclOperation.numeric, "(2+2+1)!").getStringResult()); + junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "(2.0+2.0)!").getStringResult()); + junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "4.0!").getStringResult()); + junit.framework.Assert.assertEquals("720", cm.evaluate(JsclOperation.numeric, "(3!)!").getStringResult()); + junit.framework.Assert.assertEquals("36", Expression.valueOf("3!^2").numeric().toString()); + junit.framework.Assert.assertEquals("3", Expression.valueOf("cubic(27)").numeric().toString()); + try { + junit.framework.Assert.assertEquals("√(-1)!", cm.evaluate(JsclOperation.numeric, "i!").getStringResult()); + fail(); + } catch (CalculatorParseException e) { + } + + junit.framework.Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "(π/π)!").getStringResult()); + + try { + junit.framework.Assert.assertEquals("i", cm.evaluate(JsclOperation.numeric, "(-1)i!").getStringResult()); + fail(); + } catch (CalculatorParseException e) { + + } + junit.framework.Assert.assertEquals("24i", cm.evaluate(JsclOperation.numeric, "4!i").getStringResult()); + + CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("si", 5d)); + + try { + cm.getEngine().setAngleUnits(AngleUnit.rad); + Assert.assertEquals("0.451", cm.evaluate(JsclOperation.numeric, "acos(0.8999999999999811)").getStringResult()); + Assert.assertEquals("-0.959", cm.evaluate(JsclOperation.numeric, "sin(5)").getStringResult()); + Assert.assertEquals("-4.795", cm.evaluate(JsclOperation.numeric, "sin(5)si").getStringResult()); + Assert.assertEquals("-23.973", cm.evaluate(JsclOperation.numeric, "sisin(5)si").getStringResult()); + Assert.assertEquals("-23.973", cm.evaluate(JsclOperation.numeric, "si*sin(5)si").getStringResult()); + Assert.assertEquals("-3.309", cm.evaluate(JsclOperation.numeric, "sisin(5si)si").getStringResult()); + } finally { + cm.getEngine().setAngleUnits(defaultAngleUnit); + } + + CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("s", 1d)); + Assert.assertEquals("5", cm.evaluate(JsclOperation.numeric, "si").getStringResult()); + + CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("k", 3.5d)); + CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("k1", 4d)); + Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "k11").getStringResult()); + + CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t", (String) null)); + Assert.assertEquals("11t", cm.evaluate(JsclOperation.numeric, "t11").getStringResult()); + Assert.assertEquals("11et", cm.evaluate(JsclOperation.numeric, "t11e").getStringResult()); + Assert.assertEquals("∞", cm.evaluate(JsclOperation.numeric, "∞").getStringResult()); + Assert.assertEquals("∞", cm.evaluate(JsclOperation.numeric, "Infinity").getStringResult()); + Assert.assertEquals("11∞t", cm.evaluate(JsclOperation.numeric, "t11∞").getStringResult()); + Assert.assertEquals("-t+t^3", cm.evaluate(JsclOperation.numeric, "t(t-1)(t+1)").getStringResult()); + + Assert.assertEquals("100", cm.evaluate(JsclOperation.numeric, "0.1E3").getStringResult()); + Assert.assertEquals("3.957", cm.evaluate(JsclOperation.numeric, "ln(8)lg(8)+ln(8)").getStringResult()); + + Assert.assertEquals("0.933", cm.evaluate(JsclOperation.numeric, "0x:E/0x:F").getStringResult()); + + try { + cm.getEngine().setNumeralBase(NumeralBase.hex); + Assert.assertEquals("E/F", cm.evaluate(JsclOperation.numeric, "0x:E/0x:F").getStringResult()); + Assert.assertEquals("E/F", cm.evaluate(JsclOperation.simplify, "0x:E/0x:F").getStringResult()); + Assert.assertEquals("E/F", cm.evaluate(JsclOperation.numeric, "E/F").getStringResult()); + Assert.assertEquals("E/F", cm.evaluate(JsclOperation.simplify, "E/F").getStringResult()); + } finally { + cm.getEngine().setNumeralBase(NumeralBase.dec); + } + + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "((((((0))))))").getStringResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((0))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))").getStringResult()); + + + /* Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "30°").getResult()); + Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "(10+20)°").getResult()); + Assert.assertEquals("1.047", cm.evaluate(JsclOperation.numeric, "(10+20)°*2").getResult()); + try { + Assert.assertEquals("0.278", cm.evaluate(JsclOperation.numeric, "30°^2").getResult()); + junit.framework.Assert.fail(); + } catch (ParseException e) { + if ( !e.getMessage().equals("Power operation after postfix function is currently unsupported!") ) { + junit.framework.Assert.fail(); + } + }*/ + +/* try { + cm.setTimeout(5000); + Assert.assertEquals("2", cm.evaluate(JsclOperation.numeric, "2!").getResult()); + } finally { + cm.setTimeout(3000); + }*/ + + CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t", (String) null)); + Assert.assertEquals("2t", cm.evaluate(JsclOperation.simplify, "∂(t^2,t)").getStringResult()); + Assert.assertEquals("2t", cm.evaluate(JsclOperation.numeric, "∂(t^2,t)").getStringResult()); + CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t", "2")); + Assert.assertEquals("2t", cm.evaluate(JsclOperation.simplify, "∂(t^2,t)").getStringResult()); + Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "∂(t^2,t)").getStringResult()); + + Assert.assertEquals("-x+x*ln(x)", cm.getEngine().simplify("∫(ln(x), x)")); + Assert.assertEquals("-(x-x*ln(x))/(ln(2)+ln(5))", cm.getEngine().simplify("∫(log(10, x), x)")); + + Assert.assertEquals("∫((ln(2)+ln(5))/ln(x), x)", cm.getEngine().simplify("∫(ln(10)/ln(x), x)")); + Assert.assertEquals("∫(ln(10)/ln(x), x)", Expression.valueOf("∫(log(x, 10), x)").expand().toString()); + Assert.assertEquals("∫((ln(2)+ln(5))/ln(x), x)", cm.getEngine().simplify("∫(log(x, 10), x)")); + } + + @Test + public void testFormatting() throws Exception { + final CalculatorEngine ce = CalculatorEngine.instance; + + Assert.assertEquals("12 345", ce.evaluate(JsclOperation.simplify, "12345").getStringResult()); + + } + + @Test + public void testI() throws CalculatorParseException, CalculatorEvalException { + final CalculatorEngine cm = CalculatorEngine.instance; + + Assert.assertEquals("-i", cm.evaluate(JsclOperation.numeric, "i^3").getStringResult()); + for (int i = 0; i < 1000; i++) { + double real = (Math.random()-0.5) * 1000; + double imag = (Math.random()-0.5) * 1000; + int exp = (int)(Math.random() * 10); + + final StringBuilder sb = new StringBuilder(); + sb.append(real); + if ( imag > 0 ) { + sb.append("+"); + } + sb.append(imag); + sb.append("^").append(exp); + try { + cm.evaluate(JsclOperation.numeric, sb.toString()).getStringResult(); + } catch (Throwable e) { + fail(sb.toString()); + } + } + } + + @Test + public void testEmptyFunction() throws Exception { + final CalculatorEngine cm = CalculatorEngine.instance; + try { + cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(acos(acos(acos(acos(acos(acos(acos(acos(cos(cos(cos(cos(cosh(acos(cos(cos(cos(cos(cos(acos(acos(acos(acos(acos(acos(acos(acos(cos(cos(cos(cos(cosh(acos(cos())))))))))))))))))))))))))))))))))))))"); + Assert.fail(); + } catch (CalculatorParseException e) { + } + Assert.assertEquals("0.34+1.382i", cm.evaluate(JsclOperation.numeric, "ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(100)))))))))))))))").getStringResult()); + try { + cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos())))))))))))))))))))))))))))))))))))"); + Assert.fail(); + } catch (CalculatorParseException e) { + } + + final AngleUnit defaultAngleUnit = cm.getEngine().getAngleUnits(); + try { + cm.getEngine().setAngleUnits(AngleUnit.rad); + Assert.assertEquals("0.739", cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(1))))))))))))))))))))))))))))))))))))").getStringResult()); + } finally { + cm.getEngine().setAngleUnits(defaultAngleUnit); + } + + CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("si", 5d)); + Assert.assertEquals("5", cm.evaluate(JsclOperation.numeric, "si").getStringResult()); + + try { + cm.evaluate(JsclOperation.numeric, "sin"); + Assert.fail(); + } catch (CalculatorParseException e) { + } + } + + @Test + public void testRounding() throws Exception { + final CalculatorEngine cm = CalculatorEngine.instance; + + try { + DecimalFormatSymbols decimalGroupSymbols = new DecimalFormatSymbols(Locale.getDefault()); + decimalGroupSymbols.setDecimalSeparator('.'); + decimalGroupSymbols.setGroupingSeparator('\''); + cm.setDecimalGroupSymbols(decimalGroupSymbols); + cm.setPrecision(2); + Assert.assertEquals("12'345'678.9", cm.evaluate(JsclOperation.numeric, "1.23456789E7").getStringResult()); + cm.setPrecision(10); + Assert.assertEquals("12'345'678.9", cm.evaluate(JsclOperation.numeric, "1.23456789E7").getStringResult()); + Assert.assertEquals("123'456'789", cm.evaluate(JsclOperation.numeric, "1.234567890E8").getStringResult()); + Assert.assertEquals("1'234'567'890.1", cm.evaluate(JsclOperation.numeric, "1.2345678901E9").getStringResult()); + } finally { + cm.setPrecision(3); + DecimalFormatSymbols decimalGroupSymbols = new DecimalFormatSymbols(Locale.getDefault()); + decimalGroupSymbols.setDecimalSeparator('.'); + decimalGroupSymbols.setGroupingSeparator(JsclMathEngine.GROUPING_SEPARATOR_DEFAULT.charAt(0)); + cm.setDecimalGroupSymbols(decimalGroupSymbols); + } + } + + @Test + public void testComparisonFunction() throws Exception { + final CalculatorEngine cm = CalculatorEngine.instance; + + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(0, 1)").getStringResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq(1, 1)").getStringResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq(1, 1.0)").getStringResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(1, 1.000000000000001)").getStringResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(1, 0)").getStringResult()); + + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "lt(0, 1)").getStringResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "lt(1, 1)").getStringResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "lt(1, 0)").getStringResult()); + + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "gt(0, 1)").getStringResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "gt(1, 1)").getStringResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "gt(1, 0)").getStringResult()); + + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ne(0, 1)").getStringResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ne(1, 1)").getStringResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ne(1, 0)").getStringResult()); + + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "le(0, 1)").getStringResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "le(1, 1)").getStringResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "le(1, 0)").getStringResult()); + + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ge(0, 1)").getStringResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ge(1, 1)").getStringResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ge(1, 0)").getStringResult()); + + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ap(0, 1)").getStringResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ap(1, 1)").getStringResult()); + //Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ap(1, 1.000000000000001)").getResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ap(1, 0)").getStringResult()); + + } + + + @Test + public void testNumeralSystems() throws Exception { + final CalculatorEngine cm = CalculatorEngine.instance; + + Assert.assertEquals("11 259 375", cm.evaluate(JsclOperation.numeric, "0x:ABCDEF").getStringResult()); + Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "0x:ABCDEF*e").getStringResult()); + Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "e*0x:ABCDEF").getStringResult()); + Assert.assertEquals("e", cm.evaluate(JsclOperation.numeric, "e*0x:ABCDEF/0x:ABCDEF").getStringResult()); + Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "0x:ABCDEF*e*0x:ABCDEF/0x:ABCDEF").getStringResult()); + Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "c+0x:ABCDEF*e*0x:ABCDEF/0x:ABCDEF-c+0x:C-0x:C").getStringResult()); + Assert.assertEquals("1 446 257 064 651.832", cm.evaluate(JsclOperation.numeric, "28*28 * sin(28) - 0b:1101 + √(28) + exp ( 28) ").getStringResult()); + Assert.assertEquals("13", cm.evaluate(JsclOperation.numeric, "0b:1101").getStringResult()); + + try { + cm.evaluate(JsclOperation.numeric, "0b:π").getStringResult(); + Assert.fail(); + } catch (CalculatorParseException e) { + // ok + } + + final NumeralBase defaultNumeralBase = cm.getEngine().getNumeralBase(); + try{ + cm.getEngine().setNumeralBase(NumeralBase.bin); + Assert.assertEquals("101", cm.evaluate(JsclOperation.numeric, "10+11").getStringResult()); + Assert.assertEquals("10/11", cm.evaluate(JsclOperation.numeric, "10/11").getStringResult()); + + cm.getEngine().setNumeralBase(NumeralBase.hex); + Assert.assertEquals("63 7B", cm.evaluate(JsclOperation.numeric, "56CE+CAD").getStringResult()); + Assert.assertEquals("E", cm.evaluate(JsclOperation.numeric, "E").getStringResult()); + } finally { + cm.setNumeralBase(defaultNumeralBase); + } + } + + @Test + public void testLog() throws Exception { + final CalculatorEngine cm = CalculatorEngine.instance; + + Assert.assertEquals("∞", Expression.valueOf("1/0").numeric().toString()); + Assert.assertEquals("∞", Expression.valueOf("ln(10)/ln(1)").numeric().toString()); + + // logarithm + Assert.assertEquals("ln(x)/ln(base)", ((CustomFunction) cm.getFunctionsRegistry().get("log")).getContent()); + Assert.assertEquals("∞", cm.evaluate(JsclOperation.numeric, "log(1, 10)").getStringResult()); + Assert.assertEquals("3.322", cm.evaluate(JsclOperation.numeric, "log(2, 10)").getStringResult()); + Assert.assertEquals("1.431", cm.evaluate(JsclOperation.numeric, "log(5, 10)").getStringResult()); + Assert.assertEquals("0.96", cm.evaluate(JsclOperation.numeric, "log(11, 10)").getStringResult()); + Assert.assertEquals("1/(bln(a))", cm.evaluate(JsclOperation.simplify, "∂(log(a, b), b)").getStringResult()); + Assert.assertEquals("-ln(b)/(aln(a)^2)", cm.evaluate(JsclOperation.simplify, "∂(log(a, b), a)").getStringResult()); + + } +} diff --git a/calculatorpp/src/test/java/org/solovyev/android/calculator/model/NumeralBaseTest.java b/calculatorpp/src/test/java/org/solovyev/android/calculator/model/NumeralBaseTest.java index bd658b31..5e05495c 100644 --- a/calculatorpp/src/test/java/org/solovyev/android/calculator/model/NumeralBaseTest.java +++ b/calculatorpp/src/test/java/org/solovyev/android/calculator/model/NumeralBaseTest.java @@ -1,148 +1,148 @@ -package org.solovyev.android.calculator.model; - -import au.com.bytecode.opencsv.CSVReader; -import jscl.JsclMathEngine; -import jscl.MathEngine; -import jscl.math.Expression; -import jscl.text.ParseException; -import jscl.util.ExpressionGeneratorWithInput; -import org.jetbrains.annotations.NotNull; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; -import org.solovyev.android.calculator.CalculatorEvalException; -import org.solovyev.android.calculator.CalculatorParseException; -import org.solovyev.android.calculator.jscl.JsclOperation; -import org.solovyev.common.Converter; - -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.List; - -/** - * User: serso - * Date: 12/14/11 - * Time: 4:16 PM - */ -public class NumeralBaseTest { - - @BeforeClass - public static void setUp() throws Exception { - CalculatorEngine.instance.init(null, null); - CalculatorEngine.instance.setPrecision(3); - CalculatorEngine.instance.setThreadKiller(new CalculatorEngine.ThreadKillerImpl()); - } - - @Test - public void testConversion() throws Exception { - CSVReader reader = null; - try { - final MathEngine me = JsclMathEngine.instance; - - reader = new CSVReader(new InputStreamReader(NumeralBaseTest.class.getResourceAsStream("/org/solovyev/android/calculator/model/nb_table.csv")), '\t'); - - // skip first line - reader.readNext(); - - String[] line = reader.readNext(); - for (; line != null; line = reader.readNext()) { - testExpression(line, new DummyExpression()); - testExpression(line, new Expression1()); - testExpression(line, new Expression2()); - testExpression(line, new Expression3()); - - final String dec = line[0].toUpperCase(); - final String hex = "0x:" + line[1].toUpperCase(); - final String bin = "0b:" + line[2].toUpperCase(); - - final List input = new ArrayList(); - input.add(dec); - input.add(hex); - input.add(bin); - - //System.out.println("Dec: " + dec); - //System.out.println("Hex: " + hex); - //System.out.println("Bin: " + bin); - - final ExpressionGeneratorWithInput eg = new ExpressionGeneratorWithInput(input, 20); - final List expressions = eg.generate(); - - final String decExpression = expressions.get(0); - final String hexExpression = expressions.get(1); - final String binExpression = expressions.get(2); - - //System.out.println("Dec expression: " + decExpression); - //System.out.println("Hex expression: " + hexExpression); - //System.out.println("Bin expression: " + binExpression); - - final String decResult = Expression.valueOf(decExpression).numeric().toString(); - //System.out.println("Dec result: " + decResult); - - final String hexResult = Expression.valueOf(hexExpression).numeric().toString(); - //System.out.println("Hex result: " + hexResult); - - final String binResult = Expression.valueOf(binExpression).numeric().toString(); - //System.out.println("Bin result: " + binResult); - - Assert.assertEquals("dec-hex: " + decExpression + " : " + hexExpression, decResult, hexResult); - Assert.assertEquals("dec-bin: " + decExpression + " : " + binExpression, decResult, binResult); - } - } finally { - if (reader != null) { - reader.close(); - } - } - } - - public static void testExpression(@NotNull String[] line, @NotNull Converter converter) throws ParseException, CalculatorEvalException, CalculatorParseException { - final String dec = line[0].toUpperCase(); - final String hex = "0x:" + line[1].toUpperCase(); - final String bin = "0b:" + line[2].toUpperCase(); - - final String decExpression = converter.convert(dec); - final String decResult = CalculatorEngine.instance.evaluate(JsclOperation.numeric, decExpression).getResult(); - final String hexExpression = converter.convert(hex); - final String hexResult = CalculatorEngine.instance.evaluate(JsclOperation.numeric, hexExpression).getResult(); - final String binExpression = converter.convert(bin); - final String binResult = CalculatorEngine.instance.evaluate(JsclOperation.numeric, binExpression).getResult(); - - Assert.assertEquals("dec-hex: " + decExpression + " : " + hexExpression, decResult, hexResult); - Assert.assertEquals("dec-bin: " + decExpression + " : " + binExpression, decResult, binResult); - } - - private static class DummyExpression implements Converter { - - @NotNull - @Override - public String convert(@NotNull String s) { - return s; - } - } - - private static class Expression1 implements Converter { - - @NotNull - @Override - public String convert(@NotNull String s) { - return s + "*" + s; - } - } - - private static class Expression2 implements Converter { - - @NotNull - @Override - public String convert(@NotNull String s) { - return s + "*" + s + " * sin(" + s + ") - 0b:1101"; - } - } - - private static class Expression3 implements Converter { - - @NotNull - @Override - public String convert(@NotNull String s) { - return s + "*" + s + " * sin(" + s + ") - 0b:1101 + √(" + s + ") + exp ( " + s + ")"; - } - } -} +package org.solovyev.android.calculator.model; + +import au.com.bytecode.opencsv.CSVReader; +import jscl.JsclMathEngine; +import jscl.MathEngine; +import jscl.math.Expression; +import jscl.text.ParseException; +import jscl.util.ExpressionGeneratorWithInput; +import org.jetbrains.annotations.NotNull; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.solovyev.android.calculator.CalculatorEvalException; +import org.solovyev.android.calculator.CalculatorParseException; +import org.solovyev.android.calculator.jscl.JsclOperation; +import org.solovyev.common.Converter; + +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + +/** + * User: serso + * Date: 12/14/11 + * Time: 4:16 PM + */ +public class NumeralBaseTest { + + @BeforeClass + public static void setUp() throws Exception { + CalculatorEngine.instance.init(null, null); + CalculatorEngine.instance.setPrecision(3); + CalculatorEngine.instance.setThreadKiller(new CalculatorEngine.ThreadKillerImpl()); + } + + @Test + public void testConversion() throws Exception { + CSVReader reader = null; + try { + final MathEngine me = JsclMathEngine.instance; + + reader = new CSVReader(new InputStreamReader(NumeralBaseTest.class.getResourceAsStream("/org/solovyev/android/calculator/model/nb_table.csv")), '\t'); + + // skip first line + reader.readNext(); + + String[] line = reader.readNext(); + for (; line != null; line = reader.readNext()) { + testExpression(line, new DummyExpression()); + testExpression(line, new Expression1()); + testExpression(line, new Expression2()); + testExpression(line, new Expression3()); + + final String dec = line[0].toUpperCase(); + final String hex = "0x:" + line[1].toUpperCase(); + final String bin = "0b:" + line[2].toUpperCase(); + + final List input = new ArrayList(); + input.add(dec); + input.add(hex); + input.add(bin); + + //System.out.println("Dec: " + dec); + //System.out.println("Hex: " + hex); + //System.out.println("Bin: " + bin); + + final ExpressionGeneratorWithInput eg = new ExpressionGeneratorWithInput(input, 20); + final List expressions = eg.generate(); + + final String decExpression = expressions.get(0); + final String hexExpression = expressions.get(1); + final String binExpression = expressions.get(2); + + //System.out.println("Dec expression: " + decExpression); + //System.out.println("Hex expression: " + hexExpression); + //System.out.println("Bin expression: " + binExpression); + + final String decResult = Expression.valueOf(decExpression).numeric().toString(); + //System.out.println("Dec result: " + decResult); + + final String hexResult = Expression.valueOf(hexExpression).numeric().toString(); + //System.out.println("Hex result: " + hexResult); + + final String binResult = Expression.valueOf(binExpression).numeric().toString(); + //System.out.println("Bin result: " + binResult); + + Assert.assertEquals("dec-hex: " + decExpression + " : " + hexExpression, decResult, hexResult); + Assert.assertEquals("dec-bin: " + decExpression + " : " + binExpression, decResult, binResult); + } + } finally { + if (reader != null) { + reader.close(); + } + } + } + + public static void testExpression(@NotNull String[] line, @NotNull Converter converter) throws ParseException, CalculatorEvalException, CalculatorParseException { + final String dec = line[0].toUpperCase(); + final String hex = "0x:" + line[1].toUpperCase(); + final String bin = "0b:" + line[2].toUpperCase(); + + final String decExpression = converter.convert(dec); + final String decResult = CalculatorEngine.instance.evaluate(JsclOperation.numeric, decExpression).getStringResult(); + final String hexExpression = converter.convert(hex); + final String hexResult = CalculatorEngine.instance.evaluate(JsclOperation.numeric, hexExpression).getStringResult(); + final String binExpression = converter.convert(bin); + final String binResult = CalculatorEngine.instance.evaluate(JsclOperation.numeric, binExpression).getStringResult(); + + Assert.assertEquals("dec-hex: " + decExpression + " : " + hexExpression, decResult, hexResult); + Assert.assertEquals("dec-bin: " + decExpression + " : " + binExpression, decResult, binResult); + } + + private static class DummyExpression implements Converter { + + @NotNull + @Override + public String convert(@NotNull String s) { + return s; + } + } + + private static class Expression1 implements Converter { + + @NotNull + @Override + public String convert(@NotNull String s) { + return s + "*" + s; + } + } + + private static class Expression2 implements Converter { + + @NotNull + @Override + public String convert(@NotNull String s) { + return s + "*" + s + " * sin(" + s + ") - 0b:1101"; + } + } + + private static class Expression3 implements Converter { + + @NotNull + @Override + public String convert(@NotNull String s) { + return s + "*" + s + " * sin(" + s + ") - 0b:1101 + √(" + s + ") + exp ( " + s + ")"; + } + } +}