diff --git a/calculatorpp-core/pom.xml b/calculatorpp-core/pom.xml new file mode 100644 index 00000000..17ae2ad2 --- /dev/null +++ b/calculatorpp-core/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + + + org.solovyev.android + calculatorpp-parent + 1.3.1 + + + org.solovyev.android + calculatorpp-core + 1.3.1 + Calculator++ Application Core + + jar + + + + + junit + junit + test + + + + com.intellij + annotations + + + + org.solovyev + jscl + + + + org.simpleframework + simple-xml + + + + + + \ No newline at end of file diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/AbstractNumberBuilder.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/AbstractNumberBuilder.java similarity index 95% rename from calculatorpp/src/main/java/org/solovyev/android/calculator/model/AbstractNumberBuilder.java rename to calculatorpp-core/src/main/java/org/solovyev/android/calculator/AbstractNumberBuilder.java index d4dac24b..a95e6703 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/AbstractNumberBuilder.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/AbstractNumberBuilder.java @@ -1,85 +1,85 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - * or visit http://se.solovyev.org - */ - -package org.solovyev.android.calculator.model; - -import jscl.MathEngine; -import jscl.NumeralBase; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.solovyev.android.calculator.math.MathType; -import org.solovyev.common.text.StringUtils; - -/** - * User: serso - * Date: 12/15/11 - * Time: 9:01 PM - */ -public abstract class AbstractNumberBuilder { - - @NotNull - protected final MathEngine engine; - - @Nullable - protected StringBuilder numberBuilder = null; - - @Nullable - protected NumeralBase nb; - - protected AbstractNumberBuilder(@NotNull MathEngine engine) { - this.engine = engine; - this.nb = engine.getNumeralBase(); - } - - /** - * Method determines if we can continue to process current number - * - * @param mathTypeResult current math type result - * @return true if we can continue of processing of current number, if false - new number should be constructed - */ - protected boolean canContinue(@NotNull MathType.Result mathTypeResult) { - boolean result = mathTypeResult.getMathType().getGroupType() == MathType.MathGroupType.number && - !spaceBefore(mathTypeResult) && - numeralBaseCheck(mathTypeResult) && - numeralBaseInTheStart(mathTypeResult.getMathType()) || isSignAfterE(mathTypeResult); - return result; - } - - private boolean spaceBefore(@NotNull MathType.Result mathTypeResult) { - return numberBuilder == null && StringUtils.isEmpty(mathTypeResult.getMatch().trim()); - } - - private boolean numeralBaseInTheStart(@NotNull MathType mathType) { - return mathType != MathType.numeral_base || numberBuilder == null; - } - - private boolean numeralBaseCheck(@NotNull MathType.Result mathType) { - return mathType.getMathType() != MathType.digit || getNumeralBase().getAcceptableCharacters().contains(mathType.getMatch().charAt(0)); - } - - private boolean isSignAfterE(@NotNull MathType.Result mathTypeResult) { - if (!isHexMode()) { - if ("-".equals(mathTypeResult.getMatch()) || "+".equals(mathTypeResult.getMatch())) { - final StringBuilder localNb = numberBuilder; - if (localNb != null && localNb.length() > 0) { - if (localNb.charAt(localNb.length() - 1) == MathType.POWER_10) { - return true; - } - } - } - } - return false; - } - - public boolean isHexMode() { - return nb == NumeralBase.hex || (nb == null && engine.getNumeralBase() == NumeralBase.hex); - } - - @NotNull - protected NumeralBase getNumeralBase() { - return nb == null ? engine.getNumeralBase() : nb; - } -} +/* + * 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.MathEngine; +import jscl.NumeralBase; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.calculator.math.MathType; +import org.solovyev.common.text.StringUtils; + +/** + * User: serso + * Date: 12/15/11 + * Time: 9:01 PM + */ +public abstract class AbstractNumberBuilder { + + @NotNull + protected final MathEngine engine; + + @Nullable + protected StringBuilder numberBuilder = null; + + @Nullable + protected NumeralBase nb; + + protected AbstractNumberBuilder(@NotNull MathEngine engine) { + this.engine = engine; + this.nb = engine.getNumeralBase(); + } + + /** + * Method determines if we can continue to process current number + * + * @param mathTypeResult current math type result + * @return true if we can continue of processing of current number, if false - new number should be constructed + */ + protected boolean canContinue(@NotNull MathType.Result mathTypeResult) { + boolean result = mathTypeResult.getMathType().getGroupType() == MathType.MathGroupType.number && + !spaceBefore(mathTypeResult) && + numeralBaseCheck(mathTypeResult) && + numeralBaseInTheStart(mathTypeResult.getMathType()) || isSignAfterE(mathTypeResult); + return result; + } + + private boolean spaceBefore(@NotNull MathType.Result mathTypeResult) { + return numberBuilder == null && StringUtils.isEmpty(mathTypeResult.getMatch().trim()); + } + + private boolean numeralBaseInTheStart(@NotNull MathType mathType) { + return mathType != MathType.numeral_base || numberBuilder == null; + } + + private boolean numeralBaseCheck(@NotNull MathType.Result mathType) { + return mathType.getMathType() != MathType.digit || getNumeralBase().getAcceptableCharacters().contains(mathType.getMatch().charAt(0)); + } + + private boolean isSignAfterE(@NotNull MathType.Result mathTypeResult) { + if (!isHexMode()) { + if ("-".equals(mathTypeResult.getMatch()) || "+".equals(mathTypeResult.getMatch())) { + final StringBuilder localNb = numberBuilder; + if (localNb != null && localNb.length() > 0) { + if (localNb.charAt(localNb.length() - 1) == MathType.POWER_10) { + return true; + } + } + } + } + return false; + } + + public boolean isHexMode() { + return nb == NumeralBase.hex || (nb == null && engine.getNumeralBase() == NumeralBase.hex); + } + + @NotNull + protected NumeralBase getNumeralBase() { + return nb == null ? engine.getNumeralBase() : nb; + } +} 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 new file mode 100644 index 00000000..433c2163 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/Calculator.java @@ -0,0 +1,30 @@ +package org.solovyev.android.calculator; + +import jscl.NumeralBase; +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.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); + + @NotNull + CalculatorEventDataId convert(@NotNull Generic generic, @NotNull NumeralBase to); +} 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/src/main/java/org/solovyev/android/calculator/CalculatorEngineControl.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEngineControl.java similarity index 94% rename from calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorEngineControl.java rename to calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEngineControl.java index 6de56347..43e2614d 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorEngineControl.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEngineControl.java @@ -1,19 +1,19 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - * or visit http://se.solovyev.org - */ - -package org.solovyev.android.calculator; - -/** - * User: serso - * Date: 10/24/11 - * Time: 9:55 PM - */ -public interface CalculatorEngineControl { - - void evaluate(); - - void simplify(); -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + * or visit http://se.solovyev.org + */ + +package org.solovyev.android.calculator; + +/** + * User: serso + * Date: 10/24/11 + * Time: 9:55 PM + */ +public interface CalculatorEngineControl { + + void evaluate(); + + void simplify(); +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/CalculatorEvalException.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEvalException.java similarity index 92% rename from calculatorpp/src/main/java/org/solovyev/android/calculator/model/CalculatorEvalException.java rename to calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEvalException.java index 5c4e4416..8aba31d2 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/CalculatorEvalException.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEvalException.java @@ -1,73 +1,72 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - * or visit http://se.solovyev.org - */ - -package org.solovyev.android.calculator.model; - -import jscl.AbstractJsclArithmeticException; -import org.jetbrains.annotations.NotNull; -import org.solovyev.common.exceptions.SersoException; -import org.solovyev.common.msg.Message; -import org.solovyev.common.msg.MessageType; - -import java.util.List; -import java.util.Locale; - -/** - * User: serso - * Date: 12/8/11 - * Time: 1:27 AM - */ -public class CalculatorEvalException extends SersoException implements Message { - - @NotNull - private final Message message; - - @NotNull - private final String expression; - - public CalculatorEvalException(@NotNull Message message, @NotNull Throwable cause, String expression) { - super(cause); - this.message = message; - this.expression = expression; - } - - - @NotNull - public String getExpression() { - return expression; - } - - @NotNull - @Override - public String getMessageCode() { - return this.message.getMessageCode(); - } - - @NotNull - @Override - public List getParameters() { - return this.message.getParameters(); - } - - @NotNull - @Override - public MessageType getMessageType() { - return this.message.getMessageType(); - } - - @Override - @NotNull - public String getLocalizedMessage() { - return this.message.getLocalizedMessage(Locale.getDefault()); - } - - @NotNull - @Override - public String getLocalizedMessage(@NotNull Locale locale) { - return this.message.getLocalizedMessage(locale); - } -} - +/* + * 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.solovyev.common.exceptions.SersoException; +import org.solovyev.common.msg.Message; +import org.solovyev.common.msg.MessageType; + +import java.util.List; +import java.util.Locale; + +/** + * User: serso + * Date: 12/8/11 + * Time: 1:27 AM + */ +public class CalculatorEvalException extends SersoException implements Message { + + @NotNull + private final Message message; + + @NotNull + private final String expression; + + public CalculatorEvalException(@NotNull Message message, @NotNull Throwable cause, String expression) { + super(cause); + this.message = message; + this.expression = expression; + } + + + @NotNull + public String getExpression() { + return expression; + } + + @NotNull + @Override + public String getMessageCode() { + return this.message.getMessageCode(); + } + + @NotNull + @Override + public List getParameters() { + return this.message.getParameters(); + } + + @NotNull + @Override + public MessageType getMessageType() { + return this.message.getMessageType(); + } + + @Override + @NotNull + public String getLocalizedMessage() { + return this.message.getLocalizedMessage(Locale.getDefault()); + } + + @NotNull + @Override + public String getLocalizedMessage(@NotNull Locale locale) { + return this.message.getLocalizedMessage(locale); + } +} + 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..9df965a0 --- /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 getSequenceId() { + return calculatorEventData.getSequenceId(); + } + + @Override + public boolean isAfter(@NotNull CalculatorEventDataId calculatorEventDataId) { + return calculatorEventData.isAfter(calculatorEventDataId); + } +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventContainer.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventContainer.java new file mode 100644 index 00000000..20d918d6 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventContainer.java @@ -0,0 +1,57 @@ +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 16:39 + */ +public interface CalculatorEventContainer { + + void addCalculatorEventListener(@NotNull CalculatorEventListener calculatorEventListener); + + void removeCalculatorEventListener(@NotNull CalculatorEventListener calculatorEventListener); + + void fireCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data); + + void fireCalculatorEvents(@NotNull List calculatorEvents); + + public static class CalculatorEvent { + + @NotNull + private CalculatorEventData calculatorEventData; + + @NotNull + private CalculatorEventType calculatorEventType; + + @Nullable + private Object data; + + public CalculatorEvent(@NotNull CalculatorEventData calculatorEventData, + @NotNull CalculatorEventType calculatorEventType, + @Nullable Object data) { + this.calculatorEventData = calculatorEventData; + this.calculatorEventType = calculatorEventType; + this.data = data; + } + + @NotNull + public CalculatorEventData getCalculatorEventData() { + return calculatorEventData; + } + + @NotNull + public CalculatorEventType getCalculatorEventType() { + return calculatorEventType; + } + + @Nullable + public Object getData() { + return data; + } + } +} 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 new file mode 100644 index 00000000..567808a7 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventData.java @@ -0,0 +1,10 @@ +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 new file mode 100644 index 00000000..783d82d6 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataId.java @@ -0,0 +1,21 @@ +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(); + + // the higher id => the later event + @Nullable + Long getSequenceId(); + + 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 new file mode 100644 index 00000000..ad2e2ca9 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataIdImpl.java @@ -0,0 +1,64 @@ +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 sequenceId; + + private CalculatorEventDataIdImpl(long id, @Nullable Long sequenceId) { + this.eventId = id; + this.sequenceId = sequenceId; + } + + @NotNull + static CalculatorEventDataId newInstance(long id, @Nullable Long sequenceId) { + return new CalculatorEventDataIdImpl(id, sequenceId); + } + + @Override + public long getEventId() { + return this.eventId; + } + + @Nullable + @Override + public Long getSequenceId() { + return this.sequenceId; + } + + @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 (sequenceId != null ? !sequenceId.equals(that.sequenceId) : that.sequenceId != null) + return false; + + return true; + } + + @Override + public int hashCode() { + int result = (int) (eventId ^ (eventId >>> 32)); + result = 31 * result + (sequenceId != null ? sequenceId.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 new file mode 100644 index 00000000..34b37d2b --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataImpl.java @@ -0,0 +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; + + 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 getSequenceId() { + return calculatorEventDataId.getSequenceId(); + } + + @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/CalculatorEventListener.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventListener.java new file mode 100644 index 00000000..ea4a34f6 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventListener.java @@ -0,0 +1,17 @@ +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.EventListener; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 16:39 + */ +public interface CalculatorEventListener extends EventListener { + + void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data); + +} 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 new file mode 100644 index 00000000..1fd179b6 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventType.java @@ -0,0 +1,55 @@ +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, + + /* + ********************************************************************** + * + * CONVERSION + * + ********************************************************************** + */ + conversion_started, + + // @NotNull String conversion result + conversion_finished; + + 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 new file mode 100644 index 00000000..3db62bfb --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorImpl.java @@ -0,0 +1,291 @@ +package org.solovyev.android.calculator; + +import jscl.AbstractJsclArithmeticException; +import jscl.NumeralBase; +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 org.solovyev.common.text.StringUtils; +import org.solovyev.math.units.UnitConverter; +import org.solovyev.math.units.UnitImpl; +import org.solovyev.math.units.UnitType; + +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 + public static String doConversion(@NotNull UnitConverter converter, + @Nullable String from, + @NotNull UnitType fromUnitType, + @NotNull UnitType toUnitType) throws ConversionException{ + final String result; + + if (StringUtils.isEmpty(from)) { + result = ""; + } else { + + String to = null; + try { + if (converter.isSupported(fromUnitType, toUnitType)) { + to = converter.convert(UnitImpl.newInstance(from, fromUnitType), toUnitType).getValue(); + } + } catch (RuntimeException e) { + throw new ConversionException(e); + } + + result = to; + } + + return result; + } + + @NotNull + private CalculatorEventDataId nextCalculatorEventDataId() { + long eventId = counter.incrementAndGet(); + return CalculatorEventDataIdImpl.newInstance(eventId, eventId); + } + + @NotNull + private CalculatorEventDataId nextEventDataId(@NotNull Long sequenceId) { + long eventId = counter.incrementAndGet(); + return CalculatorEventDataIdImpl.newInstance(eventId, sequenceId); + } + + /* + ********************************************************************** + * + * 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.getSequenceId(), operation, expression, mr); + } + }); + + return eventDataId; + } + + @NotNull + @Override + public CalculatorEventDataId convert(@NotNull final Generic generic, + @NotNull final NumeralBase to) { + final CalculatorEventDataId eventDataId = nextCalculatorEventDataId(); + + threadPoolExecutor.execute(new Runnable() { + @Override + public void run() { + final Long sequenceId = eventDataId.getSequenceId(); + assert sequenceId != null; + + fireCalculatorEvent(newConversionEventData(sequenceId), CalculatorEventType.conversion_started, null); + + final NumeralBase from = CalculatorLocatorImpl.getInstance().getCalculatorEngine().getEngine().getNumeralBase(); + + if (from != to) { + String fromString = generic.toString(); + if (!StringUtils.isEmpty(fromString)) { + try { + fromString = ToJsclTextProcessor.getInstance().process(fromString).getExpression(); + } catch (CalculatorParseException e) { + // ok, problems while processing occurred + } + } + + // todo serso: continue + //doConversion(AndroidNumeralBase.getConverter(), fromString, AndroidNumeralBase.valueOf(fromString), AndroidNumeralBase.valueOf(to)); + } else { + fireCalculatorEvent(newConversionEventData(sequenceId), CalculatorEventType.conversion_finished, generic.toString()); + } + } + }); + + return eventDataId; + } + + @NotNull + private CalculatorEventData newConversionEventData(@NotNull Long sequenceId) { + return CalculatorEventDataImpl.newInstance(nextEventDataId(sequenceId)); + } + + private void evaluate(@NotNull Long sequenceId, + @NotNull JsclOperation operation, + @NotNull String expression, + @Nullable MessageRegistry mr) { + synchronized (lock) { + + PreparedExpression preparedExpression = null; + + fireCalculatorEvent(newCalculationEventData(operation, expression, sequenceId), 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, sequenceId), CalculatorEventType.calculation_result, data); + + } catch (AbstractJsclArithmeticException e) { + handleException(sequenceId, operation, expression, mr, new CalculatorEvalException(e, e, jsclExpression)); + } + + } catch (ArithmeticException e) { + handleException(sequenceId, operation, expression, mr, preparedExpression, new CalculatorParseException(expression, new CalculatorMessage(CalculatorMessages.msg_001, MessageType.error, e.getMessage()))); + } catch (StackOverflowError e) { + handleException(sequenceId, operation, expression, mr, preparedExpression, new CalculatorParseException(expression, new CalculatorMessage(CalculatorMessages.msg_002, MessageType.error))); + } catch (jscl.text.ParseException e) { + handleException(sequenceId, operation, expression, mr, preparedExpression, new CalculatorParseException(e)); + } catch (ParseInterruptedException e) { + + // do nothing - we ourselves interrupt the calculations + fireCalculatorEvent(newCalculationEventData(operation, expression, sequenceId), CalculatorEventType.calculation_cancelled, null); + + } catch (CalculatorParseException e) { + handleException(sequenceId, operation, expression, mr, preparedExpression, e); + } finally { + fireCalculatorEvent(newCalculationEventData(operation, expression, sequenceId), 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); + } + + public static final class ConversionException extends Exception { + private ConversionException() { + } + + private ConversionException(Throwable throwable) { + super(throwable); + } + } +} 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 new file mode 100644 index 00000000..3b04e8be --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorLocator.java @@ -0,0 +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(); + + @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 new file mode 100644 index 00000000..989e1445 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorLocatorImpl.java @@ -0,0 +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 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/src/main/java/org/solovyev/android/calculator/model/CalculatorParseException.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorParseException.java similarity index 71% rename from calculatorpp/src/main/java/org/solovyev/android/calculator/model/CalculatorParseException.java rename to calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorParseException.java index e164578f..8ebfccf7 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/CalculatorParseException.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorParseException.java @@ -4,12 +4,10 @@ * or visit http://se.solovyev.org */ -package org.solovyev.android.calculator.model; +package org.solovyev.android.calculator; -import android.app.Application; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.solovyev.android.msg.AndroidMessage; import org.solovyev.common.exceptions.SersoException; import org.solovyev.common.msg.Message; import org.solovyev.common.msg.MessageType; @@ -39,14 +37,17 @@ public class CalculatorParseException extends SersoException implements Message this.position = jsclParseException.getPosition(); } - public CalculatorParseException(@NotNull Integer messageId, @NotNull Application application, @Nullable Integer position, @NotNull String expression, Object... parameters) { - this.message = new AndroidMessage(messageId, MessageType.error, application, parameters); + public CalculatorParseException(@Nullable Integer position, + @NotNull String expression, + @NotNull Message message) { + this.message = message; this.expression = expression; this.position = position; } - public CalculatorParseException(@NotNull Integer messageId, @NotNull Application application, @NotNull String expression, Object... parameters) { - this(messageId, application, null, expression, parameters); + public CalculatorParseException(@NotNull String expression, + @NotNull Message message) { + this(null, expression, message); } @NotNull diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/Editor.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/Editor.java similarity index 86% rename from calculatorpp/src/main/java/org/solovyev/android/calculator/history/Editor.java rename to calculatorpp-core/src/main/java/org/solovyev/android/calculator/Editor.java index c60aa66e..e2f53a0b 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/Editor.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/Editor.java @@ -1,27 +1,27 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - * or visit http://se.solovyev.org - */ - -package org.solovyev.android.calculator.history; - -import org.jetbrains.annotations.Nullable; - -/** - * User: serso - * Date: 12/17/11 - * Time: 9:37 PM - */ -public interface Editor { - - @Nullable - CharSequence getText(); - - void setText(@Nullable CharSequence text); - - int getSelection(); - - void setSelection(int selection); - -} +/* + * 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.Nullable; + +/** + * User: serso + * Date: 12/17/11 + * Time: 9:37 PM + */ +public interface Editor { + + @Nullable + CharSequence getText(); + + void setText(@Nullable CharSequence text); + + int getSelection(); + + void setSelection(int selection); + +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/JCalculatorEngine.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/JCalculatorEngine.java new file mode 100644 index 00000000..c069e378 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/JCalculatorEngine.java @@ -0,0 +1,34 @@ +package org.solovyev.android.calculator; + +import jscl.MathEngine; +import jscl.math.function.Function; +import jscl.math.function.IConstant; +import jscl.math.operator.Operator; +import org.jetbrains.annotations.NotNull; +import org.solovyev.common.math.MathRegistry; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 12:43 + */ +public interface JCalculatorEngine { + + @NotNull + String getMultiplicationSign(); + + @NotNull + MathRegistry getVarsRegistry(); + + @NotNull + MathRegistry getFunctionsRegistry(); + + @NotNull + MathRegistry getOperatorsRegistry(); + + @NotNull + MathRegistry getPostfixFunctionsRegistry(); + + @NotNull + MathEngine getEngine(); +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/ListCalculatorEventContainer.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/ListCalculatorEventContainer.java new file mode 100644 index 00000000..09f18ce8 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/ListCalculatorEventContainer.java @@ -0,0 +1,50 @@ +package org.solovyev.android.calculator; + +import android.util.Log; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.common.utils.ListListenersContainer; + +import java.util.Arrays; +import java.util.List; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 16:42 + */ +public class ListCalculatorEventContainer implements CalculatorEventContainer { + + @NotNull + private static final String TAG = "CalculatorEventData"; + + @NotNull + private final ListListenersContainer listeners = new ListListenersContainer(); + + @Override + public void addCalculatorEventListener(@NotNull CalculatorEventListener calculatorEventListener) { + listeners.addListener(calculatorEventListener); + } + + @Override + public void removeCalculatorEventListener(@NotNull CalculatorEventListener calculatorEventListener) { + listeners.removeListener(calculatorEventListener); + } + + @Override + public void fireCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) { + fireCalculatorEvents(Arrays.asList(new CalculatorEvent(calculatorEventData, calculatorEventType, data))); + } + + @Override + public void fireCalculatorEvents(@NotNull List calculatorEvents) { + final List listeners = this.listeners.getListeners(); + + for (CalculatorEvent e : calculatorEvents) { + Log.d(TAG, "Event: " + e.getCalculatorEventType() + " with data: " + e.getData()); + for (CalculatorEventListener listener : listeners) { + listener.onCalculatorEvent(e.getCalculatorEventData(), e.getCalculatorEventType(), e.getData()); + } + } + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/LiteNumberBuilder.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/LiteNumberBuilder.java similarity index 93% rename from calculatorpp/src/main/java/org/solovyev/android/calculator/model/LiteNumberBuilder.java rename to calculatorpp-core/src/main/java/org/solovyev/android/calculator/LiteNumberBuilder.java index 606e742f..8d02d869 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/LiteNumberBuilder.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/LiteNumberBuilder.java @@ -1,55 +1,55 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - * or visit http://se.solovyev.org - */ - -package org.solovyev.android.calculator.model; - -import jscl.MathEngine; -import jscl.NumeralBase; -import org.jetbrains.annotations.NotNull; -import org.solovyev.android.calculator.math.MathType; - -/** - * User: serso - * Date: 12/15/11 - * Time: 8:33 PM - */ - -public class LiteNumberBuilder extends AbstractNumberBuilder { - - public LiteNumberBuilder(@NotNull MathEngine engine) { - super(engine); - this.nb = engine.getNumeralBase(); - } - - public void process(@NotNull MathType.Result mathTypeResult) { - if (canContinue(mathTypeResult)) { - // let's continue building number - if (numberBuilder == null) { - // if new number => create new builder - numberBuilder = new StringBuilder(); - } - - if (mathTypeResult.getMathType() != MathType.numeral_base) { - // just add matching string - numberBuilder.append(mathTypeResult.getMatch()); - } else { - // set explicitly numeral base (do not include it into number) - nb = NumeralBase.getByPrefix(mathTypeResult.getMatch()); - } - - } else { - // process current number (and go to the next one) - if (numberBuilder != null) { - numberBuilder = null; - - // must set default numeral base (exit numeral base mode) - nb = engine.getNumeralBase(); - } - } - } - -} - +/* + * 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.MathEngine; +import jscl.NumeralBase; +import org.jetbrains.annotations.NotNull; +import org.solovyev.android.calculator.math.MathType; + +/** + * User: serso + * Date: 12/15/11 + * Time: 8:33 PM + */ + +public class LiteNumberBuilder extends AbstractNumberBuilder { + + public LiteNumberBuilder(@NotNull MathEngine engine) { + super(engine); + this.nb = engine.getNumeralBase(); + } + + public void process(@NotNull MathType.Result mathTypeResult) { + if (canContinue(mathTypeResult)) { + // let's continue building number + if (numberBuilder == null) { + // if new number => create new builder + numberBuilder = new StringBuilder(); + } + + if (mathTypeResult.getMathType() != MathType.numeral_base) { + // just add matching string + numberBuilder.append(mathTypeResult.getMatch()); + } else { + // set explicitly numeral base (do not include it into number) + nb = NumeralBase.getByPrefix(mathTypeResult.getMatch()); + } + + } else { + // process current number (and go to the next one) + if (numberBuilder != null) { + numberBuilder = null; + + // must set default numeral base (exit numeral base mode) + nb = engine.getNumeralBase(); + } + } + } + +} + diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/NumberBuilder.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/NumberBuilder.java similarity index 94% rename from calculatorpp/src/main/java/org/solovyev/android/calculator/model/NumberBuilder.java rename to calculatorpp-core/src/main/java/org/solovyev/android/calculator/NumberBuilder.java index 33dac4ee..24183e24 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/NumberBuilder.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/NumberBuilder.java @@ -1,202 +1,203 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - * or visit http://se.solovyev.org - */ - -package org.solovyev.android.calculator.model; - -import jscl.MathContext; -import jscl.MathEngine; -import jscl.NumeralBase; -import jscl.math.numeric.Real; -import jscl.text.*; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.solovyev.android.calculator.math.MathType; -import org.solovyev.common.MutableObject; - -import java.util.ArrayList; -import java.util.List; - -/** - * User: serso - * Date: 10/23/11 - * Time: 2:57 PM - */ -public class NumberBuilder extends AbstractNumberBuilder { - - public NumberBuilder(@NotNull MathEngine engine) { - super(engine); - } - - /** - * Method replaces number in text according to some rules (e.g. formatting) - * - * @param text text where number can be replaced - * @param mathTypeResult math type result of current token - * @param offset offset between new number length and old number length (newNumberLength - oldNumberLength) - * - * - * @return new math type result (as one can be changed due to substituting of number with constant) - */ - @NotNull - public MathType.Result process(@NotNull StringBuilder text, @NotNull MathType.Result mathTypeResult, @Nullable MutableObject offset) { - final MathType.Result possibleResult; - if (canContinue(mathTypeResult)) { - // let's continue building number - if (numberBuilder == null) { - // if new number => create new builder - numberBuilder = new StringBuilder(); - } - - if (mathTypeResult.getMathType() != MathType.numeral_base) { - // just add matching string - numberBuilder.append(mathTypeResult.getMatch()); - } else { - // set explicitly numeral base (do not include it into number) - nb = NumeralBase.getByPrefix(mathTypeResult.getMatch()); - } - - possibleResult = null; - } else { - // process current number (and go to the next one) - possibleResult = processNumber(text, offset); - } - - return possibleResult == null ? mathTypeResult : possibleResult; - } - - /** - * Method replaces number in text according to some rules (e.g. formatting) - * - * @param text text where number can be replaced - * @param offset offset between new number length and old number length (newNumberLength - oldNumberLength) - * - * @return new math type result (as one can be changed due to substituting of number with constant) - */ - @Nullable - public MathType.Result processNumber(@NotNull StringBuilder text, @Nullable MutableObject offset) { - // total number of trimmed chars - int trimmedChars = 0; - - String number = null; - - // save numeral base (as later it might be replaced) - final NumeralBase localNb = getNumeralBase(); - - if (numberBuilder != null) { - try { - number = numberBuilder.toString(); - - // let's get rid of unnecessary characters (grouping separators, + after E) - final List tokens = new ArrayList(); - tokens.addAll(MathType.grouping_separator.getTokens()); - // + after E can be omitted: 10+E = 10E (NOTE: - cannot be omitted ) - tokens.add("+"); - for (String groupingSeparator : tokens) { - final String trimmedNumber = number.replace(groupingSeparator, ""); - trimmedChars += number.length() - trimmedNumber.length(); - number = trimmedNumber; - } - - // check if number still valid - toDouble(number, getNumeralBase(), engine); - - } catch (NumberFormatException e) { - // number is not valid => stop - number = null; - } - - numberBuilder = null; - - // must set default numeral base (exit numeral base mode) - nb = engine.getNumeralBase(); - } - - return replaceNumberInText(text, number, trimmedChars, offset, localNb, engine); - } - - @Nullable - private static MathType.Result replaceNumberInText(@NotNull StringBuilder text, - @Nullable String number, - int trimmedChars, - @Nullable MutableObject offset, - @NotNull NumeralBase nb, - @NotNull final MathEngine engine) { - MathType.Result result = null; - - if (number != null) { - // in any case remove old number from text - final int oldNumberLength = number.length() + trimmedChars; - text.delete(text.length() - oldNumberLength, text.length()); - - final String newNumber = formatNumber(number, nb, engine); - if (offset != null) { - // register offset between old number and new number - offset.setObject(newNumber.length() - oldNumberLength); - } - text.append(newNumber); - } - - return result; - } - - @NotNull - private static String formatNumber(@NotNull String number, @NotNull NumeralBase nb, @NotNull MathEngine engine) { - String result; - - int indexOfDot = number.indexOf('.'); - - if (indexOfDot < 0) { - int indexOfE; - if (nb == NumeralBase.hex) { - indexOfE = -1; - } else { - indexOfE = number.indexOf(MathType.POWER_10); - } - if (indexOfE < 0) { - result = engine.addGroupingSeparators(nb, number); - } else { - final String partBeforeE; - if (indexOfE != 0) { - partBeforeE = engine.addGroupingSeparators(nb, number.substring(0, indexOfE)); - } else { - partBeforeE = ""; - } - result = partBeforeE + number.substring(indexOfE); - } - } else { - final String integerPart; - if (indexOfDot != 0) { - integerPart = engine.addGroupingSeparators(nb, number.substring(0, indexOfDot)); - } else { - integerPart = ""; - } - result = integerPart + number.substring(indexOfDot); - } - - return result; - } - - @NotNull - private static Double toDouble(@NotNull String s, @NotNull NumeralBase nb, @NotNull final MathContext mc) throws NumberFormatException { - final NumeralBase defaultNb = mc.getNumeralBase(); - try { - mc.setNumeralBase(nb); - - try { - return JsclIntegerParser.parser.parse(Parser.Parameters.newInstance(s, new MutableInt(0), mc), null).content().doubleValue(); - } catch (ParseException e) { - try { - return ((Real) DoubleParser.parser.parse(Parser.Parameters.newInstance(s, new MutableInt(0), mc), null).content()).doubleValue(); - } catch (ParseException e1) { - throw new NumberFormatException(); - } - } - - } finally { - mc.setNumeralBase(defaultNb); - } - } -} +/* + * 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.MathContext; +import jscl.MathEngine; +import jscl.NumeralBase; +import jscl.math.numeric.Real; +import jscl.text.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.calculator.AbstractNumberBuilder; +import org.solovyev.android.calculator.math.MathType; +import org.solovyev.common.MutableObject; + +import java.util.ArrayList; +import java.util.List; + +/** + * User: serso + * Date: 10/23/11 + * Time: 2:57 PM + */ +public class NumberBuilder extends AbstractNumberBuilder { + + public NumberBuilder(@NotNull MathEngine engine) { + super(engine); + } + + /** + * Method replaces number in text according to some rules (e.g. formatting) + * + * @param text text where number can be replaced + * @param mathTypeResult math type result of current token + * @param offset offset between new number length and old number length (newNumberLength - oldNumberLength) + * + * + * @return new math type result (as one can be changed due to substituting of number with constant) + */ + @NotNull + public MathType.Result process(@NotNull StringBuilder text, @NotNull MathType.Result mathTypeResult, @Nullable MutableObject offset) { + final MathType.Result possibleResult; + if (canContinue(mathTypeResult)) { + // let's continue building number + if (numberBuilder == null) { + // if new number => create new builder + numberBuilder = new StringBuilder(); + } + + if (mathTypeResult.getMathType() != MathType.numeral_base) { + // just add matching string + numberBuilder.append(mathTypeResult.getMatch()); + } else { + // set explicitly numeral base (do not include it into number) + nb = NumeralBase.getByPrefix(mathTypeResult.getMatch()); + } + + possibleResult = null; + } else { + // process current number (and go to the next one) + possibleResult = processNumber(text, offset); + } + + return possibleResult == null ? mathTypeResult : possibleResult; + } + + /** + * Method replaces number in text according to some rules (e.g. formatting) + * + * @param text text where number can be replaced + * @param offset offset between new number length and old number length (newNumberLength - oldNumberLength) + * + * @return new math type result (as one can be changed due to substituting of number with constant) + */ + @Nullable + public MathType.Result processNumber(@NotNull StringBuilder text, @Nullable MutableObject offset) { + // total number of trimmed chars + int trimmedChars = 0; + + String number = null; + + // toXml numeral base (as later it might be replaced) + final NumeralBase localNb = getNumeralBase(); + + if (numberBuilder != null) { + try { + number = numberBuilder.toString(); + + // let's get rid of unnecessary characters (grouping separators, + after E) + final List tokens = new ArrayList(); + tokens.addAll(MathType.grouping_separator.getTokens()); + // + after E can be omitted: 10+E = 10E (NOTE: - cannot be omitted ) + tokens.add("+"); + for (String groupingSeparator : tokens) { + final String trimmedNumber = number.replace(groupingSeparator, ""); + trimmedChars += number.length() - trimmedNumber.length(); + number = trimmedNumber; + } + + // check if number still valid + toDouble(number, getNumeralBase(), engine); + + } catch (NumberFormatException e) { + // number is not valid => stop + number = null; + } + + numberBuilder = null; + + // must set default numeral base (exit numeral base mode) + nb = engine.getNumeralBase(); + } + + return replaceNumberInText(text, number, trimmedChars, offset, localNb, engine); + } + + @Nullable + private static MathType.Result replaceNumberInText(@NotNull StringBuilder text, + @Nullable String number, + int trimmedChars, + @Nullable MutableObject offset, + @NotNull NumeralBase nb, + @NotNull final MathEngine engine) { + MathType.Result result = null; + + if (number != null) { + // in any case remove old number from text + final int oldNumberLength = number.length() + trimmedChars; + text.delete(text.length() - oldNumberLength, text.length()); + + final String newNumber = formatNumber(number, nb, engine); + if (offset != null) { + // register offset between old number and new number + offset.setObject(newNumber.length() - oldNumberLength); + } + text.append(newNumber); + } + + return result; + } + + @NotNull + private static String formatNumber(@NotNull String number, @NotNull NumeralBase nb, @NotNull MathEngine engine) { + String result; + + int indexOfDot = number.indexOf('.'); + + if (indexOfDot < 0) { + int indexOfE; + if (nb == NumeralBase.hex) { + indexOfE = -1; + } else { + indexOfE = number.indexOf(MathType.POWER_10); + } + if (indexOfE < 0) { + result = engine.addGroupingSeparators(nb, number); + } else { + final String partBeforeE; + if (indexOfE != 0) { + partBeforeE = engine.addGroupingSeparators(nb, number.substring(0, indexOfE)); + } else { + partBeforeE = ""; + } + result = partBeforeE + number.substring(indexOfE); + } + } else { + final String integerPart; + if (indexOfDot != 0) { + integerPart = engine.addGroupingSeparators(nb, number.substring(0, indexOfDot)); + } else { + integerPart = ""; + } + result = integerPart + number.substring(indexOfDot); + } + + return result; + } + + @NotNull + private static Double toDouble(@NotNull String s, @NotNull NumeralBase nb, @NotNull final MathContext mc) throws NumberFormatException { + final NumeralBase defaultNb = mc.getNumeralBase(); + try { + mc.setNumeralBase(nb); + + try { + return JsclIntegerParser.parser.parse(Parser.Parameters.newInstance(s, new MutableInt(0), mc), null).content().doubleValue(); + } catch (ParseException e) { + try { + return ((Real) DoubleParser.parser.parse(Parser.Parameters.newInstance(s, new MutableInt(0), mc), null).content()).doubleValue(); + } catch (ParseException e1) { + throw new NumberFormatException(); + } + } + + } finally { + mc.setNumeralBase(defaultNb); + } + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/PreparedExpression.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/PreparedExpression.java similarity index 91% rename from calculatorpp/src/main/java/org/solovyev/android/calculator/model/PreparedExpression.java rename to calculatorpp-core/src/main/java/org/solovyev/android/calculator/PreparedExpression.java index 879b99f0..976de25d 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/PreparedExpression.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/PreparedExpression.java @@ -1,65 +1,65 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - * or visit http://se.solovyev.org - */ - -package org.solovyev.android.calculator.model; - -import jscl.math.function.IConstant; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -/** - * User: serso - * Date: 10/18/11 - * Time: 10:07 PM - */ -public class PreparedExpression implements CharSequence{ - - @NotNull - private String expression; - - @NotNull - private List undefinedVars; - - public PreparedExpression(@NotNull String expression, @NotNull List undefinedVars) { - this.expression = expression; - this.undefinedVars = undefinedVars; - } - - @NotNull - public String getExpression() { - return expression; - } - - public boolean isExistsUndefinedVar() { - return !this.undefinedVars.isEmpty(); - } - - @NotNull - public List getUndefinedVars() { - return undefinedVars; - } - - @Override - public int length() { - return expression.length(); - } - - @Override - public char charAt(int i) { - return expression.charAt(i); - } - - @Override - public CharSequence subSequence(int i, int i1) { - return expression.subSequence(i, i1); - } - - @Override - public String toString() { - return this.expression; - } -} +/* + * 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.function.IConstant; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * User: serso + * Date: 10/18/11 + * Time: 10:07 PM + */ +public class PreparedExpression implements CharSequence{ + + @NotNull + private String expression; + + @NotNull + private List undefinedVars; + + public PreparedExpression(@NotNull String expression, @NotNull List undefinedVars) { + this.expression = expression; + this.undefinedVars = undefinedVars; + } + + @NotNull + public String getExpression() { + return expression; + } + + public boolean isExistsUndefinedVar() { + return !this.undefinedVars.isEmpty(); + } + + @NotNull + public List getUndefinedVars() { + return undefinedVars; + } + + @Override + public int length() { + return expression.length(); + } + + @Override + public char charAt(int i) { + return expression.charAt(i); + } + + @Override + public CharSequence subSequence(int i, int i1) { + return expression.subSequence(i, i1); + } + + @Override + public String toString() { + return this.expression; + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/ToJsclTextProcessor.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/ToJsclTextProcessor.java similarity index 82% rename from calculatorpp/src/main/java/org/solovyev/android/calculator/model/ToJsclTextProcessor.java rename to calculatorpp-core/src/main/java/org/solovyev/android/calculator/ToJsclTextProcessor.java index d12f3ca2..e83002f4 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/ToJsclTextProcessor.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/ToJsclTextProcessor.java @@ -4,15 +4,20 @@ * or visit http://se.solovyev.org */ -package org.solovyev.android.calculator.model; +package org.solovyev.android.calculator; import jscl.math.function.IConstant; import org.jetbrains.annotations.NotNull; import org.solovyev.android.calculator.CalculatorApplication; +import org.solovyev.android.calculator.CalculatorParseException; +import org.solovyev.android.calculator.PreparedExpression; import org.solovyev.android.calculator.R; +import org.solovyev.android.calculator.text.TextProcessor; +import org.solovyev.android.msg.AndroidMessage; import org.solovyev.common.StartsWithFinder; import org.solovyev.android.calculator.math.MathType; import org.solovyev.common.collections.CollectionsUtils; +import org.solovyev.common.msg.MessageType; import java.util.ArrayList; import java.util.List; @@ -75,7 +80,8 @@ public class ToJsclTextProcessor implements TextProcessor undefinedVars) throws CalculatorParseException { if (depth >= MAX_DEPTH) { - throw new CalculatorParseException(R.string.msg_6, CalculatorApplication.getInstance(), s); + final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_6, MessageType.error, CalculatorApplication.getInstance()); + throw new CalculatorParseException(s, androidMessage); } else { depth++; } diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/AbstractHistoryState.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/AbstractHistoryState.java similarity index 94% rename from calculatorpp/src/main/java/org/solovyev/android/calculator/history/AbstractHistoryState.java rename to calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/AbstractHistoryState.java index 39d942b9..633d8a7f 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/AbstractHistoryState.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/AbstractHistoryState.java @@ -1,81 +1,80 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - * or visit http://se.solovyev.org - */ - -package org.solovyev.android.calculator.history; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Transient; - -import java.util.Date; - -/** - * User: serso - * Date: 10/15/11 - * Time: 1:45 PM - */ -public class AbstractHistoryState implements Cloneable{ - - @Element - private long time = new Date().getTime(); - - @Element(required = false) - @Nullable - private String comment; - - @Transient - private boolean saved; - - @Transient - private int id = 0; - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public long getTime() { - return time; - } - - public void setTime(long time) { - this.time = time; - } - - @Nullable - public String getComment() { - return comment; - } - - public void setComment(@Nullable String comment) { - this.comment = comment; - } - - public boolean isSaved() { - return saved; - } - - public void setSaved(boolean saved) { - this.saved = saved; - } - - @Override - protected AbstractHistoryState clone() { - AbstractHistoryState clone; - - try { - clone = (AbstractHistoryState)super.clone(); - } catch (CloneNotSupportedException e) { - throw new UnsupportedOperationException(e); - } - - return clone; - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + * or visit http://se.solovyev.org + */ + +package org.solovyev.android.calculator.history; + +import org.jetbrains.annotations.Nullable; +import org.simpleframework.xml.Element; +import org.simpleframework.xml.Transient; + +import java.util.Date; + +/** + * User: serso + * Date: 10/15/11 + * Time: 1:45 PM + */ +public class AbstractHistoryState implements Cloneable{ + + @Element + private long time = new Date().getTime(); + + @Element(required = false) + @Nullable + private String comment; + + @Transient + private boolean saved; + + @Transient + private int id = 0; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public long getTime() { + return time; + } + + public void setTime(long time) { + this.time = time; + } + + @Nullable + public String getComment() { + return comment; + } + + public void setComment(@Nullable String comment) { + this.comment = comment; + } + + public boolean isSaved() { + return saved; + } + + public void setSaved(boolean saved) { + this.saved = saved; + } + + @Override + protected AbstractHistoryState clone() { + AbstractHistoryState clone; + + try { + clone = (AbstractHistoryState)super.clone(); + } catch (CloneNotSupportedException e) { + throw new UnsupportedOperationException(e); + } + + return clone; + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/CalculatorDisplayHistoryState.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorDisplayHistoryState.java similarity index 68% rename from calculatorpp/src/main/java/org/solovyev/android/calculator/history/CalculatorDisplayHistoryState.java rename to calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorDisplayHistoryState.java index af6713f5..c8c37c41 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/CalculatorDisplayHistoryState.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorDisplayHistoryState.java @@ -11,8 +11,11 @@ import org.jetbrains.annotations.Nullable; import org.simpleframework.xml.Element; import org.simpleframework.xml.Root; import org.simpleframework.xml.Transient; -import org.solovyev.android.calculator.ICalculatorDisplay; +import org.solovyev.android.calculator.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 @@ -47,24 +50,27 @@ public class CalculatorDisplayHistoryState implements Cloneable { } @NotNull - public static CalculatorDisplayHistoryState newInstance(@NotNull ICalculatorDisplay display) { + public static CalculatorDisplayHistoryState newInstance(@NotNull CalculatorDisplay 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(); + 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 ICalculatorDisplay display) { - this.getEditorState().setValuesFromHistory(display); - display.setValid(this.isValid()); - display.setErrorMessage(this.getErrorMessage()); - display.setJsclOperation(this.getJsclOperation()); - display.setGenericResult(this.getGenericResult()); + public void setValuesFromHistory(@NotNull CalculatorDisplay display) { + if ( this.isValid() ) { + display.setViewState(CalculatorDisplayViewStateImpl.newValidState(this.getJsclOperation(), this.getGenericResult(), StringUtils.getNotEmpty(this.getEditorState().getText(), ""), this.getEditorState().getCursorPosition())); + } else { + display.setViewState(CalculatorDisplayViewStateImpl.newErrorState(this.getJsclOperation(), StringUtils.getNotEmpty(this.getErrorMessage(), ""))); + } } diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorHistory.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorHistory.java new file mode 100644 index 00000000..c7f8f828 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorHistory.java @@ -0,0 +1,20 @@ +package org.solovyev.android.calculator.history; + +import org.jetbrains.annotations.NotNull; +import org.solovyev.common.history.HistoryHelper; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 16:11 + */ +public interface CalculatorHistory extends HistoryHelper { + + void fromXml(@NotNull String xml); + + String toXml(); + + void clearSavedHistory(); + + void removeSavedHistory(@NotNull CalculatorHistoryState historyState); +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorHistoryImpl.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorHistoryImpl.java new file mode 100644 index 00000000..fc881c5c --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorHistoryImpl.java @@ -0,0 +1,131 @@ +package org.solovyev.android.calculator.history; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.common.history.HistoryAction; +import org.solovyev.common.history.HistoryHelper; +import org.solovyev.common.history.SimpleHistoryHelper; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 16:12 + */ +public class CalculatorHistoryImpl implements CalculatorHistory { + + private final AtomicInteger counter = new AtomicInteger(0); + + @NotNull + private final HistoryHelper history = new SimpleHistoryHelper(); + + @NotNull + private final List savedHistory = new ArrayList(); + + @Override + public boolean isEmpty() { + return this.history.isEmpty(); + } + + @Override + public CalculatorHistoryState getLastHistoryState() { + return this.history.getLastHistoryState(); + } + + @Override + public boolean isUndoAvailable() { + return history.isUndoAvailable(); + } + + @Override + public CalculatorHistoryState undo(@Nullable CalculatorHistoryState currentState) { + return history.undo(currentState); + } + + @Override + public boolean isRedoAvailable() { + return history.isRedoAvailable(); + } + + @Override + public CalculatorHistoryState redo(@Nullable CalculatorHistoryState currentState) { + return history.redo(currentState); + } + + @Override + public boolean isActionAvailable(@NotNull HistoryAction historyAction) { + return history.isActionAvailable(historyAction); + } + + @Override + public CalculatorHistoryState doAction(@NotNull HistoryAction historyAction, @Nullable CalculatorHistoryState currentState) { + return history.doAction(historyAction, currentState); + } + + @Override + public void addState(@Nullable CalculatorHistoryState currentState) { + history.addState(currentState); + } + + @NotNull + @Override + public List getStates() { + return history.getStates(); + } + + @Override + public void clear() { + this.history.clear(); + } + + @NotNull + public List getSavedHistory() { + return Collections.unmodifiableList(savedHistory); + } + + @NotNull + public CalculatorHistoryState addSavedState(@NotNull CalculatorHistoryState historyState) { + if (historyState.isSaved()) { + return historyState; + } else { + final CalculatorHistoryState savedState = historyState.clone(); + + savedState.setId(counter.incrementAndGet()); + savedState.setSaved(true); + + savedHistory.add(savedState); + + return savedState; + } + } + + @Override + public void fromXml(@NotNull String xml) { + clearSavedHistory(); + + HistoryUtils.fromXml(xml, this.savedHistory); + for (CalculatorHistoryState historyState : savedHistory) { + historyState.setSaved(true); + historyState.setId(counter.incrementAndGet()); + } + } + + @Override + public String toXml() { + return HistoryUtils.toXml(this.savedHistory); + } + + @Override + public void clearSavedHistory() { + this.savedHistory.clear(); + } + + @Override + public void removeSavedHistory(@NotNull CalculatorHistoryState historyState) { + this.savedHistory.remove(historyState); + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/CalculatorHistoryState.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorHistoryState.java similarity index 93% rename from calculatorpp/src/main/java/org/solovyev/android/calculator/history/CalculatorHistoryState.java rename to calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorHistoryState.java index e9cb74e2..f866596a 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/CalculatorHistoryState.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorHistoryState.java @@ -8,7 +8,8 @@ package org.solovyev.android.calculator.history; import org.jetbrains.annotations.NotNull; import org.simpleframework.xml.Element; import org.simpleframework.xml.Root; -import org.solovyev.android.calculator.ICalculatorDisplay; +import org.solovyev.android.calculator.CalculatorDisplay; +import org.solovyev.android.calculator.Editor; /** * User: serso @@ -37,7 +38,7 @@ public class CalculatorHistoryState extends AbstractHistoryState { this.displayState = displayState; } - public static CalculatorHistoryState newInstance(@NotNull Editor editor, @NotNull ICalculatorDisplay display) { + 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); @@ -93,7 +94,7 @@ public class CalculatorHistoryState extends AbstractHistoryState { return result; } - public void setValuesFromHistory(@NotNull Editor editor, @NotNull ICalculatorDisplay display) { + public void setValuesFromHistory(@NotNull Editor editor, @NotNull CalculatorDisplay display) { this.getEditorState().setValuesFromHistory(editor); this.getDisplayState().setValuesFromHistory(display); } diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/EditorHistoryState.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/EditorHistoryState.java similarity index 82% rename from calculatorpp/src/main/java/org/solovyev/android/calculator/history/EditorHistoryState.java rename to calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/EditorHistoryState.java index f087312b..2ffa241b 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/EditorHistoryState.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/EditorHistoryState.java @@ -9,6 +9,8 @@ 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{ @@ -34,6 +36,16 @@ public class EditorHistoryState implements Cloneable{ 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()); diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/History.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/History.java similarity index 95% rename from calculatorpp/src/main/java/org/solovyev/android/calculator/history/History.java rename to calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/History.java index d8be7587..9c3973ad 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/History.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/History.java @@ -1,33 +1,33 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - * or visit http://se.solovyev.org - */ - -package org.solovyev.android.calculator.history; - -import org.simpleframework.xml.ElementList; -import org.simpleframework.xml.Root; - -import java.util.ArrayList; -import java.util.List; - -/** - * User: serso - * Date: 12/17/11 - * Time: 9:30 PM - */ - -@Root -public class History { - - @ElementList - private List historyItems = new ArrayList(); - - public History() { - } - - public List getHistoryItems() { - return historyItems; - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + * or visit http://se.solovyev.org + */ + +package org.solovyev.android.calculator.history; + +import org.simpleframework.xml.ElementList; +import org.simpleframework.xml.Root; + +import java.util.ArrayList; +import java.util.List; + +/** + * User: serso + * Date: 12/17/11 + * Time: 9:30 PM + */ + +@Root +public class History { + + @ElementList + private List historyItems = new ArrayList(); + + public History() { + } + + public List getHistoryItems() { + return historyItems; + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/HistoryUtils.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/HistoryUtils.java similarity index 96% rename from calculatorpp/src/main/java/org/solovyev/android/calculator/history/HistoryUtils.java rename to calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/HistoryUtils.java index d2f50d36..bda4ca11 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/HistoryUtils.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/HistoryUtils.java @@ -1,61 +1,61 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - * or visit http://se.solovyev.org - */ - -package org.solovyev.android.calculator.history; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.simpleframework.xml.Serializer; -import org.simpleframework.xml.core.Persister; - -import java.io.StringWriter; -import java.util.List; - -/** - * User: serso - * Date: 12/17/11 - * Time: 9:59 PM - */ -class HistoryUtils { - - // not intended for instantiation - private HistoryUtils() { - throw new AssertionError(); - } - - public static void fromXml(@Nullable String xml, @NotNull List historyItems) { - if (xml != null) { - final Serializer serializer = new Persister(); - try { - final History history = serializer.read(History.class, xml); - for (CalculatorHistoryState historyItem : history.getHistoryItems()) { - historyItems.add(historyItem); - } - } catch (Exception e) { - throw new RuntimeException(e); - } - } - } - - @NotNull - public static String toXml(@NotNull List historyItems) { - final History history = new History(); - for (CalculatorHistoryState historyState : historyItems) { - if (historyState.isSaved()) { - history.getHistoryItems().add(historyState); - } - } - - final StringWriter xml = new StringWriter(); - final Serializer serializer = new Persister(); - try { - serializer.write(history, xml); - } catch (Exception e) { - throw new RuntimeException(e); - } - return xml.toString(); - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + * or visit http://se.solovyev.org + */ + +package org.solovyev.android.calculator.history; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.simpleframework.xml.Serializer; +import org.simpleframework.xml.core.Persister; + +import java.io.StringWriter; +import java.util.List; + +/** + * User: serso + * Date: 12/17/11 + * Time: 9:59 PM + */ +class HistoryUtils { + + // not intended for instantiation + private HistoryUtils() { + throw new AssertionError(); + } + + public static void fromXml(@Nullable String xml, @NotNull List historyItems) { + if (xml != null) { + final Serializer serializer = new Persister(); + try { + final History history = serializer.read(History.class, xml); + for (CalculatorHistoryState historyItem : history.getHistoryItems()) { + historyItems.add(historyItem); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + @NotNull + public static String toXml(@NotNull List historyItems) { + final History history = new History(); + for (CalculatorHistoryState historyState : historyItems) { + if (historyState.isSaved()) { + history.getHistoryItems().add(historyState); + } + } + + final StringWriter xml = new StringWriter(); + final Serializer serializer = new Persister(); + try { + serializer.write(history, xml); + } catch (Exception e) { + throw new RuntimeException(e); + } + return xml.toString(); + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/jscl/FromJsclNumericTextProcessor.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/jscl/FromJsclNumericTextProcessor.java similarity index 81% rename from calculatorpp/src/main/java/org/solovyev/android/calculator/jscl/FromJsclNumericTextProcessor.java rename to calculatorpp-core/src/main/java/org/solovyev/android/calculator/jscl/FromJsclNumericTextProcessor.java index 6d8a1340..2251e5b8 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/jscl/FromJsclNumericTextProcessor.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/jscl/FromJsclNumericTextProcessor.java @@ -1,28 +1,28 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - * or visit http://se.solovyev.org - */ - -package org.solovyev.android.calculator.jscl; - -import jscl.math.Generic; -import org.jetbrains.annotations.NotNull; -import org.solovyev.android.calculator.model.CalculatorParseException; -import org.solovyev.android.calculator.model.TextProcessor; - -/** - * User: serso - * Date: 10/6/11 - * Time: 9:48 PM - */ -class FromJsclNumericTextProcessor implements TextProcessor { - - public static final FromJsclNumericTextProcessor instance = new FromJsclNumericTextProcessor(); - - @NotNull - @Override - public String process(@NotNull Generic numeric) throws CalculatorParseException { - return numeric.toString().replace("*", ""); - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + * or visit http://se.solovyev.org + */ + +package org.solovyev.android.calculator.jscl; + +import jscl.math.Generic; +import org.jetbrains.annotations.NotNull; +import org.solovyev.android.calculator.CalculatorParseException; +import org.solovyev.android.calculator.text.TextProcessor; + +/** + * User: serso + * Date: 10/6/11 + * Time: 9:48 PM + */ +class FromJsclNumericTextProcessor implements TextProcessor { + + public static final FromJsclNumericTextProcessor instance = new FromJsclNumericTextProcessor(); + + @NotNull + @Override + public String process(@NotNull Generic numeric) throws CalculatorParseException { + return numeric.toString().replace("*", ""); + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/jscl/JsclOperation.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/jscl/JsclOperation.java similarity index 55% rename from calculatorpp/src/main/java/org/solovyev/android/calculator/jscl/JsclOperation.java rename to calculatorpp-core/src/main/java/org/solovyev/android/calculator/jscl/JsclOperation.java index 65b72281..978964b7 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/jscl/JsclOperation.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/jscl/JsclOperation.java @@ -1,70 +1,70 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - */ - -package org.solovyev.android.calculator.jscl; - - -import jscl.math.Generic; -import jscl.text.ParseException; -import org.jetbrains.annotations.NotNull; -import org.solovyev.android.calculator.model.CalculatorEngine; -import org.solovyev.android.calculator.model.DummyTextProcessor; -import org.solovyev.android.calculator.model.FromJsclSimplifyTextProcessor; -import org.solovyev.android.calculator.model.TextProcessor; - -public enum JsclOperation { - - simplify, - elementary, - numeric; - - JsclOperation() { - } - - - @NotNull - public TextProcessor getFromProcessor() { - switch (this) { - case simplify: - return FromJsclSimplifyTextProcessor.instance; - case elementary: - return DummyTextProcessor.instance; - case numeric: - return FromJsclNumericTextProcessor.instance; - default: - throw new UnsupportedOperationException(); - } - } - - @NotNull - public final String evaluate(@NotNull String expression) throws ParseException { - switch (this) { - case simplify: - return CalculatorEngine.instance.getEngine().simplify(expression); - case elementary: - return CalculatorEngine.instance.getEngine().elementary(expression); - case numeric: - return CalculatorEngine.instance.getEngine().evaluate(expression); - default: - throw new UnsupportedOperationException(); - } - } - - @NotNull - public final Generic evaluateGeneric(@NotNull String expression) throws ParseException { - switch (this) { - case simplify: - return CalculatorEngine.instance.getEngine().simplifyGeneric(expression); - case elementary: - return CalculatorEngine.instance.getEngine().elementaryGeneric(expression); - case numeric: - return CalculatorEngine.instance.getEngine().evaluateGeneric(expression); - default: - throw new UnsupportedOperationException(); - } - } - - -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + */ + +package org.solovyev.android.calculator.jscl; + + +import jscl.math.Generic; +import jscl.text.ParseException; +import org.jetbrains.annotations.NotNull; +import org.solovyev.android.calculator.CalculatorLocatorImpl; +import org.solovyev.android.calculator.text.DummyTextProcessor; +import org.solovyev.android.calculator.text.FromJsclSimplifyTextProcessor; +import org.solovyev.android.calculator.text.TextProcessor; + +public enum JsclOperation { + + simplify, + elementary, + numeric; + + JsclOperation() { + } + + + @NotNull + public TextProcessor getFromProcessor() { + switch (this) { + case simplify: + return FromJsclSimplifyTextProcessor.instance; + case elementary: + return DummyTextProcessor.instance; + case numeric: + return FromJsclNumericTextProcessor.instance; + default: + throw new UnsupportedOperationException(); + } + } + + @NotNull + public final String evaluate(@NotNull String expression) throws ParseException { + switch (this) { + case simplify: + return CalculatorLocatorImpl.getInstance().getCalculatorEngine().getEngine().simplify(expression); + case elementary: + return CalculatorLocatorImpl.getInstance().getCalculatorEngine().getEngine().elementary(expression); + case numeric: + return CalculatorLocatorImpl.getInstance().getCalculatorEngine().getEngine().evaluate(expression); + default: + throw new UnsupportedOperationException(); + } + } + + @NotNull + public final Generic evaluateGeneric(@NotNull String expression) throws ParseException { + switch (this) { + case simplify: + return CalculatorLocatorImpl.getInstance().getCalculatorEngine().getEngine().simplifyGeneric(expression); + case elementary: + return CalculatorLocatorImpl.getInstance().getCalculatorEngine().getEngine().elementaryGeneric(expression); + case numeric: + return CalculatorLocatorImpl.getInstance().getCalculatorEngine().getEngine().evaluateGeneric(expression); + default: + throw new UnsupportedOperationException(); + } + } + + +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/math/MathType.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/math/MathType.java similarity index 91% rename from calculatorpp/src/main/java/org/solovyev/android/calculator/math/MathType.java rename to calculatorpp-core/src/main/java/org/solovyev/android/calculator/math/MathType.java index 91e8d451..549ae9fd 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/math/MathType.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/math/MathType.java @@ -1,448 +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.math; - -import jscl.JsclMathEngine; -import jscl.NumeralBase; -import jscl.math.function.Constants; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.solovyev.common.JPredicate; -import org.solovyev.common.StartsWithFinder; -import org.solovyev.android.calculator.model.CalculatorEngine; -import org.solovyev.android.calculator.model.CalculatorParseException; -import org.solovyev.common.collections.CollectionsUtils; - -import java.util.*; - - -public enum MathType { - - numeral_base(50, true, false, MathGroupType.number) { - - private final List tokens = new ArrayList(10); - { - for (NumeralBase numeralBase : NumeralBase.values()) { - final String jsclPrefix = numeralBase.getJsclPrefix(); - if (jsclPrefix != null) { - tokens.add(jsclPrefix); - } - } - } - - @NotNull - @Override - public List getTokens() { - return tokens; - } - }, - - dot(200, true, true, MathGroupType.number, ".") { - @Override - public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) { - return super.isNeedMultiplicationSignBefore(mathTypeBefore) && mathTypeBefore != digit; - } - }, - - grouping_separator(250, false, false, MathGroupType.number, "'", " "){ - @Override - public int processToJscl(@NotNull StringBuilder result, int i, @NotNull String match) throws CalculatorParseException { - return i; - } - }, - - power_10(300, false, false, MathGroupType.number, "E"), - - postfix_function(400, false, true, MathGroupType.function) { - @NotNull - @Override - public List getTokens() { - return CalculatorEngine.instance.getPostfixFunctionsRegistry().getNames(); - } - }, - - unary_operation(500, false, false, MathGroupType.operation, "-", "="), - binary_operation(600, false, false, MathGroupType.operation, "-", "+", "*", "×", "∙", "/", "^") { - @Override - protected String getSubstituteToJscl(@NotNull String match) { - if (match.equals("×") || match.equals("∙")) { - return "*"; - } else { - return null; - } - } - }, - - open_group_symbol(800, true, false, MathGroupType.other, "[", "(", "{") { - @Override - public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) { - return super.isNeedMultiplicationSignBefore(mathTypeBefore) && mathTypeBefore != function && mathTypeBefore != operator; - } - - @Override - protected String getSubstituteToJscl(@NotNull String match) { - return "("; - } - }, - - close_group_symbol(900, false, true, MathGroupType.other, "]", ")", "}") { - @Override - public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) { - return false; - } - - @Override - protected String getSubstituteToJscl(@NotNull String match) { - return ")"; - } - }, - - function(1000, true, true, MathGroupType.function) { - @NotNull - @Override - public List getTokens() { - return CalculatorEngine.instance.getFunctionsRegistry().getNames(); - } - }, - - operator(1050, true, true, MathGroupType.function) { - @NotNull - @Override - public List getTokens() { - return CalculatorEngine.instance.getOperatorsRegistry().getNames(); - } - }, - - constant(1100, true, true, MathGroupType.other) { - @NotNull - @Override - public List getTokens() { - return CalculatorEngine.instance.getVarsRegistry().getNames(); - } - - @Override - protected String getSubstituteFromJscl(@NotNull String match) { - return Constants.INF_2.getName().equals(match) ? MathType.INFINITY : super.getSubstituteFromJscl(match); - } - }, - - digit(1125, true, true, MathGroupType.number) { - - private final List tokens = new ArrayList(16); - { - for (Character character : NumeralBase.hex.getAcceptableCharacters()) { - tokens.add(character.toString()); - } - } - @Override - public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) { - return super.isNeedMultiplicationSignBefore(mathTypeBefore) && mathTypeBefore != digit && mathTypeBefore != dot /*&& mathTypeBefore != numeral_base*/; - } - - @NotNull - @Override - public List getTokens() { - return tokens; - } - }, - - comma(1150, false, false, MathGroupType.other, ","), - - text(1200, false, false, MathGroupType.other) { - @Override - public int processToJscl(@NotNull StringBuilder result, int i, @NotNull String match) { - if (match.length() > 0) { - result.append(match.charAt(0)); - } - return i; - } - - @Override - public int processFromJscl(@NotNull StringBuilder result, int i, @NotNull String match) { - if (match.length() > 0) { - result.append(match.charAt(0)); - } - return i; - } - }; - - public static enum MathGroupType { - function, - number, - operation, - other - } - - @NotNull - private final List tokens; - - @NotNull - private final Integer priority; - - private final boolean needMultiplicationSignBefore; - - private final boolean needMultiplicationSignAfter; - - @NotNull - private final MathGroupType groupType; - - MathType(@NotNull Integer priority, - boolean needMultiplicationSignBefore, - boolean needMultiplicationSignAfter, - @NotNull MathGroupType groupType, - @NotNull String... tokens) { - this(priority, needMultiplicationSignBefore, needMultiplicationSignAfter, groupType, CollectionsUtils.asList(tokens)); - } - - MathType(@NotNull Integer priority, - boolean needMultiplicationSignBefore, - boolean needMultiplicationSignAfter, - @NotNull MathGroupType groupType, - @NotNull List tokens) { - this.priority = priority; - this.needMultiplicationSignBefore = needMultiplicationSignBefore; - this.needMultiplicationSignAfter = needMultiplicationSignAfter; - this.groupType = groupType; - this.tokens = Collections.unmodifiableList(tokens); - } - - @NotNull - public MathGroupType getGroupType() { - return groupType; - } - - /* public static int getPostfixFunctionStart(@NotNull CharSequence s, int position) throws ParseException { - assert s.length() > position; - - int numberOfOpenGroups = 0; - int result = position; - for (; result >= 0; result--) { - - final MathType mathType = getType(s.toString(), result).getMathType(); - - if (CollectionsUtils.contains(mathType, digit, dot, grouping_separator, power_10)) { - // continue - } else if (mathType == close_group_symbol) { - numberOfOpenGroups++; - } else if (mathType == open_group_symbol) { - if (numberOfOpenGroups > 0) { - numberOfOpenGroups--; - } else { - break; - } - } else { - if (stop(s, numberOfOpenGroups, result)) break; - } - } - - if (numberOfOpenGroups != 0){ - throw new ParseException("Could not find start of prefix function!"); - } - - return result; - } - - public static boolean stop(CharSequence s, int numberOfOpenGroups, int i) { - if (numberOfOpenGroups == 0) { - if (i > 0) { - final EndsWithFinder endsWithFinder = new EndsWithFinder(s); - endsWithFinder.setI(i + 1); - if (!CollectionsUtils.contains(function.getTokens(), FilterType.included, endsWithFinder)) { - MathType type = getType(s.toString(), i).getMathType(); - if (type != constant) { - return true; - } - } - } else { - return true; - } - } - - return false; - }*/ - - @NotNull - public List getTokens() { - return tokens; - } - - private boolean isNeedMultiplicationSignBefore() { - return needMultiplicationSignBefore; - } - - private boolean isNeedMultiplicationSignAfter() { - return needMultiplicationSignAfter; - } - - public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) { - return needMultiplicationSignBefore && mathTypeBefore.isNeedMultiplicationSignAfter(); - } - - public int processToJscl(@NotNull StringBuilder result, int i, @NotNull String match) throws CalculatorParseException { - final String substitute = getSubstituteToJscl(match); - result.append(substitute == null ? match : substitute); - return returnI(i, match); - } - - protected int returnI(int i, @NotNull String match) { - if (match.length() > 1) { - return i + match.length() - 1; - } else { - return i; - } - } - - public int processFromJscl(@NotNull StringBuilder result, int i, @NotNull String match) { - final String substitute = getSubstituteFromJscl(match); - result.append(substitute == null ? match : substitute); - return returnI(i, match); - } - - @Nullable - protected String getSubstituteFromJscl(@NotNull String match) { - return null; - } - - @Nullable - protected String getSubstituteToJscl(@NotNull String match) { - return null; - } - - public static final List openGroupSymbols = Arrays.asList("[]", "()", "{}"); - - public final static Character POWER_10 = 'E'; - - public static final String IMAGINARY_NUMBER = "i"; - public static final String IMAGINARY_NUMBER_JSCL = "√(-1)"; - - public static final String PI = "π"; - public static final String E = "e"; - public static final String C = "c"; - public static final Double C_VALUE = 299792458d; - public static final String G = "G"; - public static final Double G_VALUE = 6.6738480E-11; - public static final String H_REDUCED = "h"; - public static final Double H_REDUCED_VALUE = 6.6260695729E-34 / ( 2 * Math.PI ); - public final static String NAN = "NaN"; - - public final static String INFINITY = "∞"; - public final static String INFINITY_JSCL = "Infinity"; - - - /** - * Method determines mathematical entity type for text substring starting from ith index - * - * - * @param text analyzed text - * @param i index which points to start of substring - * @param hexMode - * @return math entity type of substring starting from ith index of specified text - */ - @NotNull - public static Result getType(@NotNull String text, int i, boolean hexMode) { - if (i < 0) { - throw new IllegalArgumentException("I must be more or equals to 0."); - } else if (i >= text.length() && i != 0) { - throw new IllegalArgumentException("I must be less than size of text."); - } else if (i == 0 && text.length() == 0) { - return new Result(MathType.text, text); - } - - final StartsWithFinder startsWithFinder = new StartsWithFinder(text, i); - - for (MathType mathType : getMathTypesByPriority()) { - final String s = CollectionsUtils.find(mathType.getTokens(), startsWithFinder); - if (s != null) { - if ( s.length() == 1 ) { - if (hexMode || JsclMathEngine.instance.getNumeralBase() == NumeralBase.hex) { - final Character ch = s.charAt(0); - if ( NumeralBase.hex.getAcceptableCharacters().contains(ch) ) { - return new Result(MathType.digit, s); - } - } - } - return new Result(mathType, s); - } - } - - return new Result(MathType.text, text.substring(i)); - } - - - private static List mathTypesByPriority; - - @NotNull - private static List getMathTypesByPriority() { - if (mathTypesByPriority == null) { - final List result = CollectionsUtils.asList(MathType.values()); - - Collections.sort(result, new Comparator() { - @Override - public int compare(MathType l, MathType r) { - return l.priority.compareTo(r.priority); - } - }); - - mathTypesByPriority = result; - } - - return mathTypesByPriority; - } - - public static class Result { - - @NotNull - private final MathType mathType; - - @NotNull - private final String match; - - public Result(@NotNull MathType mathType, @NotNull String match) { - this.mathType = mathType; - - this.match = match; - } - - public int processToJscl(@NotNull StringBuilder result, int i) throws CalculatorParseException { - return mathType.processToJscl(result, i, match); - } - - public int processFromJscl(@NotNull StringBuilder result, int i) { - return mathType.processFromJscl(result, i, match); - } - - @NotNull - public String getMatch() { - return match; - } - - @NotNull - public MathType getMathType() { - return mathType; - } - } - - private static class EndsWithFinder implements JPredicate { - - private int i; - - @NotNull - private final CharSequence targetString; - - private EndsWithFinder(@NotNull CharSequence targetString) { - this.targetString = targetString; - } - - @Override - public boolean apply(@Nullable String s) { - return targetString.subSequence(0, i).toString().endsWith(s); - } - - public void setI(int i) { - this.i = i; - } - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + */ + +package org.solovyev.android.calculator.math; + +import jscl.JsclMathEngine; +import jscl.NumeralBase; +import jscl.math.function.Constants; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.calculator.CalculatorLocatorImpl; +import org.solovyev.common.JPredicate; +import org.solovyev.common.StartsWithFinder; +import org.solovyev.android.calculator.CalculatorParseException; +import org.solovyev.common.collections.CollectionsUtils; + +import java.util.*; + + +public enum MathType { + + numeral_base(50, true, false, MathGroupType.number) { + + private final List tokens = new ArrayList(10); + { + for (NumeralBase numeralBase : NumeralBase.values()) { + tokens.add(numeralBase.getJsclPrefix()); + } + } + + @NotNull + @Override + public List getTokens() { + return tokens; + } + }, + + dot(200, true, true, MathGroupType.number, ".") { + @Override + public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) { + return super.isNeedMultiplicationSignBefore(mathTypeBefore) && mathTypeBefore != digit; + } + }, + + grouping_separator(250, false, false, MathGroupType.number, "'", " "){ + @Override + public int processToJscl(@NotNull StringBuilder result, int i, @NotNull String match) throws CalculatorParseException { + return i; + } + }, + + power_10(300, false, false, MathGroupType.number, "E"), + + postfix_function(400, false, true, MathGroupType.function) { + @NotNull + @Override + public List getTokens() { + return CalculatorLocatorImpl.getInstance().getCalculatorEngine().getPostfixFunctionsRegistry().getNames(); + } + }, + + unary_operation(500, false, false, MathGroupType.operation, "-", "="), + binary_operation(600, false, false, MathGroupType.operation, "-", "+", "*", "×", "∙", "/", "^") { + @Override + protected String getSubstituteToJscl(@NotNull String match) { + if (match.equals("×") || match.equals("∙")) { + return "*"; + } else { + return null; + } + } + }, + + open_group_symbol(800, true, false, MathGroupType.other, "[", "(", "{") { + @Override + public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) { + return super.isNeedMultiplicationSignBefore(mathTypeBefore) && mathTypeBefore != function && mathTypeBefore != operator; + } + + @Override + protected String getSubstituteToJscl(@NotNull String match) { + return "("; + } + }, + + close_group_symbol(900, false, true, MathGroupType.other, "]", ")", "}") { + @Override + public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) { + return false; + } + + @Override + protected String getSubstituteToJscl(@NotNull String match) { + return ")"; + } + }, + + function(1000, true, true, MathGroupType.function) { + @NotNull + @Override + public List getTokens() { + return CalculatorLocatorImpl.getInstance().getCalculatorEngine().getFunctionsRegistry().getNames(); + } + }, + + operator(1050, true, true, MathGroupType.function) { + @NotNull + @Override + public List getTokens() { + return CalculatorLocatorImpl.getInstance().getCalculatorEngine().getOperatorsRegistry().getNames(); + } + }, + + constant(1100, true, true, MathGroupType.other) { + @NotNull + @Override + public List getTokens() { + return CalculatorLocatorImpl.getInstance().getCalculatorEngine().getVarsRegistry().getNames(); + } + + @Override + protected String getSubstituteFromJscl(@NotNull String match) { + return Constants.INF_2.getName().equals(match) ? MathType.INFINITY : super.getSubstituteFromJscl(match); + } + }, + + digit(1125, true, true, MathGroupType.number) { + + private final List tokens = new ArrayList(16); + { + for (Character character : NumeralBase.hex.getAcceptableCharacters()) { + tokens.add(character.toString()); + } + } + @Override + public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) { + return super.isNeedMultiplicationSignBefore(mathTypeBefore) && mathTypeBefore != digit && mathTypeBefore != dot /*&& mathTypeBefore != numeral_base*/; + } + + @NotNull + @Override + public List getTokens() { + return tokens; + } + }, + + comma(1150, false, false, MathGroupType.other, ","), + + text(1200, false, false, MathGroupType.other) { + @Override + public int processToJscl(@NotNull StringBuilder result, int i, @NotNull String match) { + if (match.length() > 0) { + result.append(match.charAt(0)); + } + return i; + } + + @Override + public int processFromJscl(@NotNull StringBuilder result, int i, @NotNull String match) { + if (match.length() > 0) { + result.append(match.charAt(0)); + } + return i; + } + }; + + public static enum MathGroupType { + function, + number, + operation, + other + } + + @NotNull + private final List tokens; + + @NotNull + private final Integer priority; + + private final boolean needMultiplicationSignBefore; + + private final boolean needMultiplicationSignAfter; + + @NotNull + private final MathGroupType groupType; + + MathType(@NotNull Integer priority, + boolean needMultiplicationSignBefore, + boolean needMultiplicationSignAfter, + @NotNull MathGroupType groupType, + @NotNull String... tokens) { + this(priority, needMultiplicationSignBefore, needMultiplicationSignAfter, groupType, CollectionsUtils.asList(tokens)); + } + + MathType(@NotNull Integer priority, + boolean needMultiplicationSignBefore, + boolean needMultiplicationSignAfter, + @NotNull MathGroupType groupType, + @NotNull List tokens) { + this.priority = priority; + this.needMultiplicationSignBefore = needMultiplicationSignBefore; + this.needMultiplicationSignAfter = needMultiplicationSignAfter; + this.groupType = groupType; + this.tokens = Collections.unmodifiableList(tokens); + } + + @NotNull + public MathGroupType getGroupType() { + return groupType; + } + + /* public static int getPostfixFunctionStart(@NotNull CharSequence s, int position) throws ParseException { + assert s.length() > position; + + int numberOfOpenGroups = 0; + int result = position; + for (; result >= 0; result--) { + + final MathType mathType = getType(s.toString(), result).getMathType(); + + if (CollectionsUtils.contains(mathType, digit, dot, grouping_separator, power_10)) { + // continue + } else if (mathType == close_group_symbol) { + numberOfOpenGroups++; + } else if (mathType == open_group_symbol) { + if (numberOfOpenGroups > 0) { + numberOfOpenGroups--; + } else { + break; + } + } else { + if (stop(s, numberOfOpenGroups, result)) break; + } + } + + if (numberOfOpenGroups != 0){ + throw new ParseException("Could not find start of prefix function!"); + } + + return result; + } + + public static boolean stop(CharSequence s, int numberOfOpenGroups, int i) { + if (numberOfOpenGroups == 0) { + if (i > 0) { + final EndsWithFinder endsWithFinder = new EndsWithFinder(s); + endsWithFinder.setI(i + 1); + if (!CollectionsUtils.contains(function.getTokens(), FilterType.included, endsWithFinder)) { + MathType type = getType(s.toString(), i).getMathType(); + if (type != constant) { + return true; + } + } + } else { + return true; + } + } + + return false; + }*/ + + @NotNull + public List getTokens() { + return tokens; + } + + private boolean isNeedMultiplicationSignBefore() { + return needMultiplicationSignBefore; + } + + private boolean isNeedMultiplicationSignAfter() { + return needMultiplicationSignAfter; + } + + public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) { + return needMultiplicationSignBefore && mathTypeBefore.isNeedMultiplicationSignAfter(); + } + + public int processToJscl(@NotNull StringBuilder result, int i, @NotNull String match) throws CalculatorParseException { + final String substitute = getSubstituteToJscl(match); + result.append(substitute == null ? match : substitute); + return returnI(i, match); + } + + protected int returnI(int i, @NotNull String match) { + if (match.length() > 1) { + return i + match.length() - 1; + } else { + return i; + } + } + + public int processFromJscl(@NotNull StringBuilder result, int i, @NotNull String match) { + final String substitute = getSubstituteFromJscl(match); + result.append(substitute == null ? match : substitute); + return returnI(i, match); + } + + @Nullable + protected String getSubstituteFromJscl(@NotNull String match) { + return null; + } + + @Nullable + protected String getSubstituteToJscl(@NotNull String match) { + return null; + } + + public static final List openGroupSymbols = Arrays.asList("[]", "()", "{}"); + + public final static Character POWER_10 = 'E'; + + public static final String IMAGINARY_NUMBER = "i"; + public static final String IMAGINARY_NUMBER_JSCL = "√(-1)"; + + public static final String PI = "π"; + public static final String E = "e"; + public static final String C = "c"; + public static final Double C_VALUE = 299792458d; + public static final String G = "G"; + public static final Double G_VALUE = 6.6738480E-11; + public static final String H_REDUCED = "h"; + public static final Double H_REDUCED_VALUE = 6.6260695729E-34 / ( 2 * Math.PI ); + public final static String NAN = "NaN"; + + public final static String INFINITY = "∞"; + public final static String INFINITY_JSCL = "Infinity"; + + + /** + * Method determines mathematical entity type for text substring starting from ith index + * + * + * @param text analyzed text + * @param i index which points to start of substring + * @param hexMode + * @return math entity type of substring starting from ith index of specified text + */ + @NotNull + public static Result getType(@NotNull String text, int i, boolean hexMode) { + if (i < 0) { + throw new IllegalArgumentException("I must be more or equals to 0."); + } else if (i >= text.length() && i != 0) { + throw new IllegalArgumentException("I must be less than size of text."); + } else if (i == 0 && text.length() == 0) { + return new Result(MathType.text, text); + } + + final StartsWithFinder startsWithFinder = new StartsWithFinder(text, i); + + for (MathType mathType : getMathTypesByPriority()) { + final String s = CollectionsUtils.find(mathType.getTokens(), startsWithFinder); + if (s != null) { + if ( s.length() == 1 ) { + if (hexMode || JsclMathEngine.instance.getNumeralBase() == NumeralBase.hex) { + final Character ch = s.charAt(0); + if ( NumeralBase.hex.getAcceptableCharacters().contains(ch) ) { + return new Result(MathType.digit, s); + } + } + } + return new Result(mathType, s); + } + } + + return new Result(MathType.text, text.substring(i)); + } + + + private static List mathTypesByPriority; + + @NotNull + private static List getMathTypesByPriority() { + if (mathTypesByPriority == null) { + final List result = CollectionsUtils.asList(MathType.values()); + + Collections.sort(result, new Comparator() { + @Override + public int compare(MathType l, MathType r) { + return l.priority.compareTo(r.priority); + } + }); + + mathTypesByPriority = result; + } + + return mathTypesByPriority; + } + + public static class Result { + + @NotNull + private final MathType mathType; + + @NotNull + private final String match; + + public Result(@NotNull MathType mathType, @NotNull String match) { + this.mathType = mathType; + + this.match = match; + } + + public int processToJscl(@NotNull StringBuilder result, int i) throws CalculatorParseException { + return mathType.processToJscl(result, i, match); + } + + public int processFromJscl(@NotNull StringBuilder result, int i) { + return mathType.processFromJscl(result, i, match); + } + + @NotNull + public String getMatch() { + return match; + } + + @NotNull + public MathType getMathType() { + return mathType; + } + } + + private static class EndsWithFinder implements JPredicate { + + private int i; + + @NotNull + private final CharSequence targetString; + + private EndsWithFinder(@NotNull CharSequence targetString) { + this.targetString = targetString; + } + + @Override + public boolean apply(@Nullable String s) { + return targetString.subSequence(0, i).toString().endsWith(s); + } + + public void setI(int i) { + this.i = i; + } + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/DummyTextProcessor.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/text/DummyTextProcessor.java similarity index 78% rename from calculatorpp/src/main/java/org/solovyev/android/calculator/model/DummyTextProcessor.java rename to calculatorpp-core/src/main/java/org/solovyev/android/calculator/text/DummyTextProcessor.java index 19cbc293..22a969c5 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/DummyTextProcessor.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/text/DummyTextProcessor.java @@ -1,26 +1,27 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - * or visit http://se.solovyev.org - */ - -package org.solovyev.android.calculator.model; - -import jscl.math.Generic; -import org.jetbrains.annotations.NotNull; - -/** - * User: serso - * Date: 10/18/11 - * Time: 10:39 PM - */ -public enum DummyTextProcessor implements TextProcessor { - - instance; - - @NotNull - @Override - public String process(@NotNull Generic s) throws CalculatorParseException { - return s.toString(); - } -} +/* + * 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.text; + +import jscl.math.Generic; +import org.jetbrains.annotations.NotNull; +import org.solovyev.android.calculator.CalculatorParseException; + +/** + * User: serso + * Date: 10/18/11 + * Time: 10:39 PM + */ +public enum DummyTextProcessor implements TextProcessor { + + instance; + + @NotNull + @Override + public String process(@NotNull Generic s) throws CalculatorParseException { + return s.toString(); + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/FromJsclSimplifyTextProcessor.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/text/FromJsclSimplifyTextProcessor.java similarity index 86% rename from calculatorpp/src/main/java/org/solovyev/android/calculator/model/FromJsclSimplifyTextProcessor.java rename to calculatorpp-core/src/main/java/org/solovyev/android/calculator/text/FromJsclSimplifyTextProcessor.java index d2fb4860..3fc4da64 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/FromJsclSimplifyTextProcessor.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/text/FromJsclSimplifyTextProcessor.java @@ -1,92 +1,94 @@ -package org.solovyev.android.calculator.model; - -import jscl.math.Generic; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.solovyev.android.calculator.math.MathType; - -import java.util.Arrays; -import java.util.List; - -/** - * User: serso - * Date: 10/20/11 - * Time: 2:59 PM - */ -public class FromJsclSimplifyTextProcessor implements TextProcessor { - - public static final FromJsclSimplifyTextProcessor instance = new FromJsclSimplifyTextProcessor(); - - public FromJsclSimplifyTextProcessor() { - } - - @NotNull - @Override - public String process(@NotNull Generic from) throws CalculatorParseException { - return removeMultiplicationSigns(from.toString()); - } - - public String process(@NotNull String s) { - return removeMultiplicationSigns(s); - } - - @NotNull - private String removeMultiplicationSigns(String s) { - final StringBuilder sb = new StringBuilder(); - - MathType.Result mathTypeBefore; - MathType.Result mathType = null; - MathType.Result mathTypeAfter = null; - - for (int i = 0; i < s.length(); i++) { - mathTypeBefore = mathType; - if (mathTypeAfter == null) { - mathType = MathType.getType(s, i, false); - } else { - mathType = mathTypeAfter; - } - - char ch = s.charAt(i); - if (ch == '*') { - if (i + 1 < s.length()) { - mathTypeAfter = MathType.getType(s, i + 1, false); - } else { - mathTypeAfter = null; - } - - if (needMultiplicationSign(mathTypeBefore == null ? null : mathTypeBefore.getMathType(), mathTypeAfter == null ? null : mathTypeAfter.getMathType())) { - sb.append(CalculatorEngine.instance.getMultiplicationSign()); - } - - } else { - if (mathType.getMathType() == MathType.constant || mathType.getMathType() == MathType.function || mathType.getMathType() == MathType.operator) { - sb.append(mathType.getMatch()); - i += mathType.getMatch().length() - 1; - } else { - sb.append(ch); - } - mathTypeAfter = null; - } - - } - - return sb.toString(); - } - - private final List mathTypes = Arrays.asList(MathType.function, MathType.constant); - - private boolean needMultiplicationSign(@Nullable MathType mathTypeBefore, @Nullable MathType mathTypeAfter) { - if (mathTypeBefore == null || mathTypeAfter == null) { - return true; - } else if (mathTypes.contains(mathTypeBefore) || mathTypes.contains(mathTypeAfter)) { - return false; - } else if ( mathTypeBefore == MathType.close_group_symbol ) { - return false; - } else if ( mathTypeAfter == MathType.open_group_symbol ) { - return false; - } - - return true; - } - -} +package org.solovyev.android.calculator.text; + +import jscl.math.Generic; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.calculator.CalculatorLocatorImpl; +import org.solovyev.android.calculator.math.MathType; +import org.solovyev.android.calculator.CalculatorParseException; + +import java.util.Arrays; +import java.util.List; + +/** + * User: serso + * Date: 10/20/11 + * Time: 2:59 PM + */ +public class FromJsclSimplifyTextProcessor implements TextProcessor { + + public static final FromJsclSimplifyTextProcessor instance = new FromJsclSimplifyTextProcessor(); + + public FromJsclSimplifyTextProcessor() { + } + + @NotNull + @Override + public String process(@NotNull Generic from) throws CalculatorParseException { + return removeMultiplicationSigns(from.toString()); + } + + public String process(@NotNull String s) { + return removeMultiplicationSigns(s); + } + + @NotNull + private String removeMultiplicationSigns(String s) { + final StringBuilder sb = new StringBuilder(); + + MathType.Result mathTypeBefore; + MathType.Result mathType = null; + MathType.Result mathTypeAfter = null; + + for (int i = 0; i < s.length(); i++) { + mathTypeBefore = mathType; + if (mathTypeAfter == null) { + mathType = MathType.getType(s, i, false); + } else { + mathType = mathTypeAfter; + } + + char ch = s.charAt(i); + if (ch == '*') { + if (i + 1 < s.length()) { + mathTypeAfter = MathType.getType(s, i + 1, false); + } else { + mathTypeAfter = null; + } + + if (needMultiplicationSign(mathTypeBefore == null ? null : mathTypeBefore.getMathType(), mathTypeAfter == null ? null : mathTypeAfter.getMathType())) { + sb.append(CalculatorLocatorImpl.getInstance().getCalculatorEngine().getMultiplicationSign()); + } + + } else { + if (mathType.getMathType() == MathType.constant || mathType.getMathType() == MathType.function || mathType.getMathType() == MathType.operator) { + sb.append(mathType.getMatch()); + i += mathType.getMatch().length() - 1; + } else { + sb.append(ch); + } + mathTypeAfter = null; + } + + } + + return sb.toString(); + } + + private final List mathTypes = Arrays.asList(MathType.function, MathType.constant); + + private boolean needMultiplicationSign(@Nullable MathType mathTypeBefore, @Nullable MathType mathTypeAfter) { + if (mathTypeBefore == null || mathTypeAfter == null) { + return true; + } else if (mathTypes.contains(mathTypeBefore) || mathTypes.contains(mathTypeAfter)) { + return false; + } else if ( mathTypeBefore == MathType.close_group_symbol ) { + return false; + } else if ( mathTypeAfter == MathType.open_group_symbol ) { + return false; + } + + return true; + } + +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/TextProcessor.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/text/TextProcessor.java similarity index 65% rename from calculatorpp/src/main/java/org/solovyev/android/calculator/model/TextProcessor.java rename to calculatorpp-core/src/main/java/org/solovyev/android/calculator/text/TextProcessor.java index 48b3b1a3..b9e96935 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/TextProcessor.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/text/TextProcessor.java @@ -1,14 +1,15 @@ -package org.solovyev.android.calculator.model; - -import org.jetbrains.annotations.NotNull; - -/** - * User: serso - * Date: 9/26/11 - * Time: 12:12 PM - */ -public interface TextProcessor { - - @NotNull - TO process(@NotNull FROM from) throws CalculatorParseException; -} +package org.solovyev.android.calculator.text; + +import org.jetbrains.annotations.NotNull; +import org.solovyev.android.calculator.CalculatorParseException; + +/** + * User: serso + * Date: 9/26/11 + * Time: 12:12 PM + */ +public interface TextProcessor { + + @NotNull + TO process(@NotNull FROM from) throws CalculatorParseException; +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/common/utils/ListListenersContainer.java b/calculatorpp-core/src/main/java/org/solovyev/common/utils/ListListenersContainer.java new file mode 100644 index 00000000..fa286f42 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/common/utils/ListListenersContainer.java @@ -0,0 +1,77 @@ +package org.solovyev.common.utils; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 16:43 + */ +// todo serso: move to common +public class ListListenersContainer { + + @NotNull + private final List> listeners = new ArrayList>(); + + public void addListener(@NotNull final T listener) { + synchronized (listeners) { + boolean contains = Iterables.any(listeners, new WeakReferencePredicate(listener)); + + if (!contains) { + listeners.add(new WeakReference(listener)); + } + } + } + + public void removeListener(@NotNull T listener) { + synchronized (listeners) { + Iterables.removeIf(listeners, new WeakReferencePredicate(listener)); + } + } + + @NotNull + public List getListeners() { + final List localListeners; + + synchronized (listeners) { + localListeners = new ArrayList(listeners.size()); + + // copy listeners and remove garbage collected references + for ( Iterator> it = listeners.iterator(); it.hasNext(); ) { + final WeakReference r = it.next(); + final T t = r.get(); + if ( t == null ) { + it.remove(); + } else { + localListeners.add(t); + } + } + } + + return localListeners; + } + + private static class WeakReferencePredicate implements Predicate> { + + @NotNull + private final T t; + + public WeakReferencePredicate(T t) { + this.t = t; + } + + @Override + public boolean apply(@Nullable WeakReference r) { + final T t = r != null ? r.get() : null; + return this.t.equals(t); + } + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/Unit.java b/calculatorpp-core/src/main/java/org/solovyev/math/units/Unit.java similarity index 80% rename from calculatorpp/src/main/java/org/solovyev/android/Unit.java rename to calculatorpp-core/src/main/java/org/solovyev/math/units/Unit.java index c3a2d8a5..23921ea8 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/Unit.java +++ b/calculatorpp-core/src/main/java/org/solovyev/math/units/Unit.java @@ -1,17 +1,17 @@ -package org.solovyev.android; - -import org.jetbrains.annotations.NotNull; - -/** - * User: serso - * Date: 4/21/12 - * Time: 7:54 PM - */ -public interface Unit { - - @NotNull - V getValue(); - - @NotNull - UnitType getUnitType(); -} +package org.solovyev.math.units; + +import org.jetbrains.annotations.NotNull; + +/** + * User: serso + * Date: 4/21/12 + * Time: 7:54 PM + */ +public interface Unit { + + @NotNull + V getValue(); + + @NotNull + UnitType getUnitType(); +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/UnitConverter.java b/calculatorpp-core/src/main/java/org/solovyev/math/units/UnitConverter.java similarity index 92% rename from calculatorpp/src/main/java/org/solovyev/android/UnitConverter.java rename to calculatorpp-core/src/main/java/org/solovyev/math/units/UnitConverter.java index 51785cc7..2d56bebd 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/UnitConverter.java +++ b/calculatorpp-core/src/main/java/org/solovyev/math/units/UnitConverter.java @@ -1,42 +1,42 @@ -package org.solovyev.android; - -import org.jetbrains.annotations.NotNull; - -/** - * User: serso - * Date: 4/21/12 - * Time: 7:53 PM - */ -public interface UnitConverter { - - boolean isSupported(@NotNull UnitType from, @NotNull UnitType to); - - @NotNull - Unit convert(@NotNull Unit from, @NotNull UnitType toType); - - public static class Dummy implements UnitConverter { - - @NotNull - private static final Dummy instance = new Dummy(); - - @NotNull - public static UnitConverter getInstance() { - return (UnitConverter)instance; - } - - private Dummy() { - } - - @Override - public boolean isSupported(@NotNull UnitType from, @NotNull UnitType to) { - return false; - } - - @NotNull - @Override - public Unit convert(@NotNull Unit from, @NotNull UnitType toType) { - throw new IllegalArgumentException(); - } - } - -} +package org.solovyev.math.units; + +import org.jetbrains.annotations.NotNull; + +/** + * User: serso + * Date: 4/21/12 + * Time: 7:53 PM + */ +public interface UnitConverter { + + boolean isSupported(@NotNull UnitType from, @NotNull UnitType to); + + @NotNull + Unit convert(@NotNull Unit from, @NotNull UnitType toType); + + public static class Dummy implements UnitConverter { + + @NotNull + private static final Dummy instance = new Dummy(); + + @NotNull + public static UnitConverter getInstance() { + return (UnitConverter)instance; + } + + private Dummy() { + } + + @Override + public boolean isSupported(@NotNull UnitType from, @NotNull UnitType to) { + return false; + } + + @NotNull + @Override + public Unit convert(@NotNull Unit from, @NotNull UnitType toType) { + throw new IllegalArgumentException(); + } + } + +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/UnitImpl.java b/calculatorpp-core/src/main/java/org/solovyev/math/units/UnitImpl.java similarity index 90% rename from calculatorpp/src/main/java/org/solovyev/android/UnitImpl.java rename to calculatorpp-core/src/main/java/org/solovyev/math/units/UnitImpl.java index b1c08ef0..647e0c6f 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/UnitImpl.java +++ b/calculatorpp-core/src/main/java/org/solovyev/math/units/UnitImpl.java @@ -1,42 +1,42 @@ -package org.solovyev.android; - -import org.jetbrains.annotations.NotNull; - -/** - * User: serso - * Date: 4/21/12 - * Time: 8:01 PM - */ -public class UnitImpl implements Unit { - - @NotNull - private V value; - - @NotNull - private UnitType unitType; - - private UnitImpl() { - } - - @NotNull - public static Unit newInstance(@NotNull V value, @NotNull UnitType unitType) { - final UnitImpl result = new UnitImpl(); - - result.value = value; - result.unitType = unitType; - - return result; - } - - @NotNull - @Override - public V getValue() { - return this.value; - } - - @NotNull - @Override - public UnitType getUnitType() { - return unitType; - } -} +package org.solovyev.math.units; + +import org.jetbrains.annotations.NotNull; + +/** + * User: serso + * Date: 4/21/12 + * Time: 8:01 PM + */ +public class UnitImpl implements Unit { + + @NotNull + private V value; + + @NotNull + private UnitType unitType; + + private UnitImpl() { + } + + @NotNull + public static Unit newInstance(@NotNull V value, @NotNull UnitType unitType) { + final UnitImpl result = new UnitImpl(); + + result.value = value; + result.unitType = unitType; + + return result; + } + + @NotNull + @Override + public V getValue() { + return this.value; + } + + @NotNull + @Override + public UnitType getUnitType() { + return unitType; + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/UnitType.java b/calculatorpp-core/src/main/java/org/solovyev/math/units/UnitType.java similarity index 81% rename from calculatorpp/src/main/java/org/solovyev/android/UnitType.java rename to calculatorpp-core/src/main/java/org/solovyev/math/units/UnitType.java index 82175bdf..457442c2 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/UnitType.java +++ b/calculatorpp-core/src/main/java/org/solovyev/math/units/UnitType.java @@ -1,16 +1,16 @@ -package org.solovyev.android; - -import org.jetbrains.annotations.NotNull; - -/** - * User: serso - * Date: 4/21/12 - * Time: 7:55 PM - */ -public interface UnitType { - - @NotNull - Class getUnitValueClass(); - - boolean equals(@NotNull Object o); -} +package org.solovyev.math.units; + +import org.jetbrains.annotations.NotNull; + +/** + * User: serso + * Date: 4/21/12 + * Time: 7:55 PM + */ +public interface UnitType { + + @NotNull + Class getUnitValueClass(); + + boolean equals(@NotNull Object o); +} 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-service/pom.xml b/calculatorpp-service/pom.xml index 236f1951..220ae027 100644 --- a/calculatorpp-service/pom.xml +++ b/calculatorpp-service/pom.xml @@ -1,36 +1,35 @@ - - - - - org.solovyev.android - calculatorpp-parent - 1.3.1 - - - 4.0.0 - - org.solovyev.android - calculatorpp-service - 0.1 - apklib - Calculator++ Service - - - - - com.intellij - annotations - 7.0.3 - - - - com.google.android - android - provided - - - - + + + + + org.solovyev.android + calculatorpp-parent + 1.3.1 + + + 4.0.0 + + org.solovyev.android + calculatorpp-service + 0.1 + apklib + Calculator++ Service + + + + + com.intellij + annotations + + + + com.google.android + android + provided + + + + \ No newline at end of file diff --git a/calculatorpp/pom.xml b/calculatorpp/pom.xml index e83dc844..82523499 100644 --- a/calculatorpp/pom.xml +++ b/calculatorpp/pom.xml @@ -1,353 +1,340 @@ - - - - - org.solovyev.android - calculatorpp-parent - 1.3.1 - - - 4.0.0 - - org.solovyev.android - calculatorpp - apk - Calculator++ Application - - - - - - org.solovyev - common-core - - - - org.solovyev - common-text - - - - org.solovyev.android - android-common-core - apklib - - - - org.solovyev.android - android-common-ads - apklib - - - - org.solovyev.android - android-common-view - apklib - - - - org.solovyev.android - android-common-preferences - apklib - - - - org.solovyev.android - android-common-other - apklib - - - - org.solovyev.android - android-common-menu - apklib - - - - org.solovyev.android - calculatorpp-service - 0.1 - apklib - - - - org.solovyev - jscl - 0.0.2 - - - xercesImpl - xerces - - - - - - - - com.google.android - android - provided - - - - junit - junit - 4.8.2 - test - - - - net.sf.opencsv - opencsv - 2.0 - test - - - - org.simpleframework - simple-xml - 2.6.1 - - - stax-api - stax - - - xpp3 - xpp3 - - - - - - achartengine - achartengine - 0.7.0 - - - - admob - admob - 6.1.0 - - - - org.solovyev.android - billing - 0.1 - - - - - com.google.guava - guava - 11.0.2 - - - - com.intellij - annotations - 7.0.3 - - - - - - - - - - - com.jayway.maven.plugins.android.generation2 - android-maven-plugin - true - - - true - - - - - manifestUpdate - process-resources - - manifest-update - - - - alignApk - package - - zipalign - - - - - - - - - - - - - release - - - - performRelease - true - - - - - - - - com.jayway.maven.plugins.android.generation2 - android-maven-plugin - - - - alignApk - package - - zipalign - - - - - - - - org.codehaus.mojo - properties-maven-plugin - 1.0-alpha-2 - - - initialize - - read-project-properties - - - - ${project.basedir}/misc/env/jarsigner.properties - - - - - - - - org.apache.maven.plugins - maven-jarsigner-plugin - - - signing - - sign - verify - - package - true - - true - - - ${project.build.directory}/${project.artifactId}-${project.version}.apk - - ${sign.keystore} - ${sign.alias} - ${sign.storepass} - ${sign.keypass} - false - - - - - - - - com.jayway.maven.plugins.android.generation2 - android-maven-plugin - true - - - - false - - - - false - ${project.build.directory}/${project.artifactId}-${project.version}.apk - ${project.build.directory}/${project.artifactId}-${project.version}-signed-aligned.apk - - - - false - true - - - - true - - - - - - manifestUpdate - process-resources - - manifest-update - - - - alignApk - package - - zipalign - - - - - - - org.codehaus.mojo - build-helper-maven-plugin - - - - ${project.build.directory}/${project.artifactId}-${project.version}-signed-aligned.apk - apk - signed-aligned - - - ${project.build.directory}/proguard/mapping.txt - map - release - - - - - - attach-signed-aligned - package - - attach-artifact - - - - - - - - - - - + + + + + org.solovyev.android + calculatorpp-parent + 1.3.1 + + + 4.0.0 + + org.solovyev.android + calculatorpp + apk + Calculator++ Application + + + + + + + org.solovyev.android + calculatorpp-core + 1.3.1 + + + + org.solovyev + common-core + + + + org.solovyev + common-text + + + + org.solovyev.android + android-common-core + apklib + + + + org.solovyev.android + android-common-ads + apklib + + + + org.solovyev.android + android-common-view + apklib + + + + org.solovyev.android + android-common-preferences + apklib + + + + org.solovyev.android + android-common-other + apklib + + + + org.solovyev.android + android-common-menu + apklib + + + + org.solovyev.android + calculatorpp-service + 0.1 + apklib + + + + org.solovyev + jscl + + + + + + com.google.android + android + provided + + + + net.sf.opencsv + opencsv + 2.0 + test + + + + org.simpleframework + simple-xml + + + + achartengine + achartengine + 0.7.0 + + + + admob + admob + 6.1.0 + + + + org.solovyev.android + billing + 0.1 + + + + + com.google.guava + guava + 11.0.2 + + + + junit + junit + test + + + + com.intellij + annotations + + + + + + + + + + + com.jayway.maven.plugins.android.generation2 + android-maven-plugin + true + + + true + + + + + manifestUpdate + process-resources + + manifest-update + + + + alignApk + package + + zipalign + + + + + + + + + + + + + release + + + + performRelease + true + + + + + + + + com.jayway.maven.plugins.android.generation2 + android-maven-plugin + + + + alignApk + package + + zipalign + + + + + + + + org.codehaus.mojo + properties-maven-plugin + 1.0-alpha-2 + + + initialize + + read-project-properties + + + + ${project.basedir}/misc/env/jarsigner.properties + + + + + + + + org.apache.maven.plugins + maven-jarsigner-plugin + + + signing + + sign + verify + + package + true + + true + + + ${project.build.directory}/${project.artifactId}-${project.version}.apk + + ${sign.keystore} + ${sign.alias} + ${sign.storepass} + ${sign.keypass} + false + + + + + + + + com.jayway.maven.plugins.android.generation2 + android-maven-plugin + true + + + + false + + + + false + ${project.build.directory}/${project.artifactId}-${project.version}.apk + ${project.build.directory}/${project.artifactId}-${project.version}-signed-aligned.apk + + + + false + true + + + + true + + + + + + manifestUpdate + process-resources + + manifest-update + + + + alignApk + package + + zipalign + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + + ${project.build.directory}/${project.artifactId}-${project.version}-signed-aligned.apk + apk + signed-aligned + + + ${project.build.directory}/proguard/mapping.txt + map + release + + + + + + attach-signed-aligned + package + + attach-artifact + + + + + + + + + + + \ No newline at end of file diff --git a/calculatorpp/project.properties b/calculatorpp/project.properties index 0b0ebc4d..1b733b09 100644 --- a/calculatorpp/project.properties +++ b/calculatorpp/project.properties @@ -1,20 +1,19 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system use, -# "ant.properties", and override values to adapt the script to your -# project structure. - -# Project target. -target=android-15 -android.library.reference.1=../calculatorpp-service -android.library.reference.2=gen-external-apklibs/org.solovyev.android_android-common-core_1.0.0 -android.library.reference.3=gen-external-apklibs/org.solovyev.android_android-common-ads_1.0.0 -android.library.reference.4=gen-external-apklibs/org.solovyev.android_android-common-view_1.0.0 -android.library.reference.5=gen-external-apklibs/org.solovyev.android_android-common-preferences_1.0.0 -android.library.reference.6=gen-external-apklibs/org.solovyev.android_android-common-other_1.0.0 -android.library.reference.7=gen-external-apklibs/org.solovyev.android_android-common-menu_1.0.0 - - +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "ant.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-15 +android.library.reference.1=gen-external-apklibs/org.solovyev.android_android-common-core_1.0.0 +android.library.reference.2=gen-external-apklibs/org.solovyev.android_android-common-ads_1.0.0 +android.library.reference.3=gen-external-apklibs/org.solovyev.android_android-common-view_1.0.0 +android.library.reference.4=gen-external-apklibs/org.solovyev.android_android-common-preferences_1.0.0 +android.library.reference.5=gen-external-apklibs/org.solovyev.android_android-common-other_1.0.0 +android.library.reference.6=gen-external-apklibs/org.solovyev.android_android-common-menu_1.0.0 + + 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 --> - { + + copy(R.string.c_copy) { + @Override + public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) { + CalculatorModel.copyResult(context, data); + } + }, + + convert_to_bin(R.string.convert_to_bin) { + @Override + public void onClick(@NotNull CalculatorDisplayViewState 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 CalculatorDisplayViewState 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 CalculatorDisplayViewState 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 CalculatorDisplayViewState data, @NotNull Context context) { + final Generic result = data.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 CalculatorDisplayViewState data, @NotNull Context context) { + final Generic generic = data.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/AndroidNumeralBase.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/AndroidNumeralBase.java index 7e9262fe..4abf638d 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/AndroidNumeralBase.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/AndroidNumeralBase.java @@ -1,158 +1,158 @@ -package org.solovyev.android.calculator; - -import android.app.Activity; -import jscl.NumeralBase; -import org.jetbrains.annotations.NotNull; -import org.solovyev.android.Unit; -import org.solovyev.android.UnitConverter; -import org.solovyev.android.UnitImpl; -import org.solovyev.android.UnitType; -import org.solovyev.android.view.drag.DirectionDragButton; -import org.solovyev.android.view.drag.DragDirection; - -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * User: serso - * Date: 4/21/12 - * Time: 8:00 PM - */ -public enum AndroidNumeralBase implements UnitType { - - bin(NumeralBase.bin) { - @NotNull - @Override - public List getButtonIds() { - return Arrays.asList(R.id.zeroDigitButton, R.id.oneDigitButton); - } - }, - - oct(NumeralBase.oct) { - @NotNull - @Override - public List getButtonIds() { - final List result = new ArrayList(bin.getButtonIds()); - result.addAll(Arrays.asList(R.id.twoDigitButton, R.id.threeDigitButton, R.id.fourDigitButton, R.id.fiveDigitButton, R.id.sixDigitButton, R.id.sevenDigitButton)); - return result; - } - }, - - dec(NumeralBase.dec) { - @NotNull - @Override - public List getButtonIds() { - final List result = new ArrayList(oct.getButtonIds()); - result.addAll(Arrays.asList(R.id.eightDigitButton, R.id.nineDigitButton)); - return result; - } - }, - - hex(NumeralBase.hex) { - - @NotNull - private List specialHexButtonIds = Arrays.asList(R.id.oneDigitButton, R.id.twoDigitButton, R.id.threeDigitButton, R.id.fourDigitButton, R.id.fiveDigitButton, R.id.sixDigitButton); - - @NotNull - @Override - public List getButtonIds() { - return dec.getButtonIds(); - } - - @Override - protected void toggleButton(boolean show, @NotNull DirectionDragButton button) { - super.toggleButton(show, button); - if (specialHexButtonIds.contains(button.getId())) { - button.showDirectionText(show, DragDirection.left); - button.invalidate(); - } - } - }; - - @NotNull - private final NumeralBase numeralBase; - - private AndroidNumeralBase(@NotNull NumeralBase numeralBase) { - this.numeralBase = numeralBase; - } - - @NotNull - public Unit createUnit(@NotNull String value) { - return UnitImpl.newInstance(value, this); - } - - @NotNull - public abstract List getButtonIds(); - - public void toggleButtons(boolean show, @NotNull Activity activity) { - for (Integer buttonId : getButtonIds()) { - final DirectionDragButton button = (DirectionDragButton) activity.findViewById(buttonId); - if (button != null) { - toggleButton(show, button); - } - } - } - - protected void toggleButton(boolean show, @NotNull DirectionDragButton button) { - button.setShowText(show); - } - - @NotNull - public NumeralBase getNumeralBase() { - return numeralBase; - } - - @NotNull - @Override - public Class getUnitValueClass() { - return String.class; - } - - @NotNull - private static final Converter converter = new Converter(); - - @NotNull - public static Converter getConverter() { - return converter; - } - - public static class Converter implements UnitConverter { - - private Converter() { - } - - @Override - public boolean isSupported(@NotNull UnitType from, @NotNull UnitType to) { - return AndroidNumeralBase.class.isAssignableFrom(from.getClass()) && AndroidNumeralBase.class.isAssignableFrom(to.getClass()); - } - - @NotNull - @Override - public Unit convert(@NotNull Unit from, @NotNull UnitType toType) { - if (!isSupported(from.getUnitType(), toType)) { - throw new IllegalArgumentException("Types are not supported!"); - } - - final AndroidNumeralBase fromTypeAndroid = (AndroidNumeralBase) from.getUnitType(); - final NumeralBase fromNumeralBase = fromTypeAndroid.numeralBase; - final NumeralBase toNumeralBase = ((AndroidNumeralBase) toType).numeralBase; - final String fromValue = (String) from.getValue(); - - final BigInteger decBigInteger = fromNumeralBase.toBigInteger(fromValue); - return UnitImpl.newInstance(toNumeralBase.toString(decBigInteger), (AndroidNumeralBase) toType); - } - } - - @NotNull - public static AndroidNumeralBase valueOf(@NotNull NumeralBase nb) { - for (AndroidNumeralBase androidNumeralBase : values()) { - if (androidNumeralBase.numeralBase == nb) { - return androidNumeralBase; - } - } - - throw new IllegalArgumentException(nb + " is not supported numeral base!"); - } -} +package org.solovyev.android.calculator; + +import android.app.Activity; +import jscl.NumeralBase; +import org.jetbrains.annotations.NotNull; +import org.solovyev.math.units.Unit; +import org.solovyev.math.units.UnitConverter; +import org.solovyev.math.units.UnitImpl; +import org.solovyev.math.units.UnitType; +import org.solovyev.android.view.drag.DirectionDragButton; +import org.solovyev.android.view.drag.DragDirection; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * User: serso + * Date: 4/21/12 + * Time: 8:00 PM + */ +public enum AndroidNumeralBase implements UnitType { + + bin(NumeralBase.bin) { + @NotNull + @Override + public List getButtonIds() { + return Arrays.asList(R.id.zeroDigitButton, R.id.oneDigitButton); + } + }, + + oct(NumeralBase.oct) { + @NotNull + @Override + public List getButtonIds() { + final List result = new ArrayList(bin.getButtonIds()); + result.addAll(Arrays.asList(R.id.twoDigitButton, R.id.threeDigitButton, R.id.fourDigitButton, R.id.fiveDigitButton, R.id.sixDigitButton, R.id.sevenDigitButton)); + return result; + } + }, + + dec(NumeralBase.dec) { + @NotNull + @Override + public List getButtonIds() { + final List result = new ArrayList(oct.getButtonIds()); + result.addAll(Arrays.asList(R.id.eightDigitButton, R.id.nineDigitButton)); + return result; + } + }, + + hex(NumeralBase.hex) { + + @NotNull + private List specialHexButtonIds = Arrays.asList(R.id.oneDigitButton, R.id.twoDigitButton, R.id.threeDigitButton, R.id.fourDigitButton, R.id.fiveDigitButton, R.id.sixDigitButton); + + @NotNull + @Override + public List getButtonIds() { + return dec.getButtonIds(); + } + + @Override + protected void toggleButton(boolean show, @NotNull DirectionDragButton button) { + super.toggleButton(show, button); + if (specialHexButtonIds.contains(button.getId())) { + button.showDirectionText(show, DragDirection.left); + button.invalidate(); + } + } + }; + + @NotNull + private final NumeralBase numeralBase; + + private AndroidNumeralBase(@NotNull NumeralBase numeralBase) { + this.numeralBase = numeralBase; + } + + @NotNull + public Unit createUnit(@NotNull String value) { + return UnitImpl.newInstance(value, this); + } + + @NotNull + public abstract List getButtonIds(); + + public void toggleButtons(boolean show, @NotNull Activity activity) { + for (Integer buttonId : getButtonIds()) { + final DirectionDragButton button = (DirectionDragButton) activity.findViewById(buttonId); + if (button != null) { + toggleButton(show, button); + } + } + } + + protected void toggleButton(boolean show, @NotNull DirectionDragButton button) { + button.setShowText(show); + } + + @NotNull + public NumeralBase getNumeralBase() { + return numeralBase; + } + + @NotNull + @Override + public Class getUnitValueClass() { + return String.class; + } + + @NotNull + private static final Converter converter = new Converter(); + + @NotNull + public static Converter getConverter() { + return converter; + } + + public static class Converter implements UnitConverter { + + private Converter() { + } + + @Override + public boolean isSupported(@NotNull UnitType from, @NotNull UnitType to) { + return AndroidNumeralBase.class.isAssignableFrom(from.getClass()) && AndroidNumeralBase.class.isAssignableFrom(to.getClass()); + } + + @NotNull + @Override + public Unit convert(@NotNull Unit from, @NotNull UnitType toType) { + if (!isSupported(from.getUnitType(), toType)) { + throw new IllegalArgumentException("Types are not supported!"); + } + + final AndroidNumeralBase fromTypeAndroid = (AndroidNumeralBase) from.getUnitType(); + final NumeralBase fromNumeralBase = fromTypeAndroid.numeralBase; + final NumeralBase toNumeralBase = ((AndroidNumeralBase) toType).numeralBase; + final String fromValue = (String) from.getValue(); + + final BigInteger decBigInteger = fromNumeralBase.toBigInteger(fromValue); + return UnitImpl.newInstance(toNumeralBase.toString(decBigInteger), (AndroidNumeralBase) toType); + } + } + + @NotNull + public static AndroidNumeralBase valueOf(@NotNull NumeralBase nb) { + for (AndroidNumeralBase androidNumeralBase : values()) { + if (androidNumeralBase.numeralBase == nb) { + return androidNumeralBase; + } + } + + throw new IllegalArgumentException(nb + " is not supported numeral base!"); + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorActivity.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorActivity.java index 608a3aa4..d7a3c0c3 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorActivity.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorActivity.java @@ -32,7 +32,7 @@ import org.solovyev.android.AndroidUtils; import org.solovyev.android.FontSizeAdjuster; import org.solovyev.android.LocalBinder; import org.solovyev.android.calculator.about.CalculatorReleaseNotesActivity; -import org.solovyev.android.calculator.history.CalculatorHistory; +import org.solovyev.android.calculator.history.AndroidCalculatorHistoryImpl; import org.solovyev.android.calculator.history.CalculatorHistoryState; import org.solovyev.android.calculator.model.CalculatorEngine; import org.solovyev.android.calculator.view.AngleUnitsButton; @@ -136,7 +136,7 @@ public class CalculatorActivity extends Activity implements FontSizeAdjuster, Sh vibrator = (Vibrator) this.getSystemService(VIBRATOR_SERVICE); - CalculatorHistory.instance.load(this, preferences); + AndroidCalculatorHistoryImpl.instance.load(this, preferences); calculatorModel = CalculatorModel.instance.init(this, preferences, CalculatorEngine.instance); dpclRegister.clear(); diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorApplication.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorApplication.java index 10e5bc61..73a5cca1 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorApplication.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorApplication.java @@ -1,92 +1,94 @@ -package org.solovyev.android.calculator; - -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.net.Uri; -import android.preference.PreferenceManager; -import android.text.method.LinkMovementMethod; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.TextView; -import net.robotmedia.billing.BillingController; -import org.jetbrains.annotations.NotNull; -import org.solovyev.android.ads.AdsController; -import org.solovyev.android.calculator.model.CalculatorEngine; - -/** - * User: serso - * Date: 12/1/11 - * Time: 1:21 PM - */ -public class CalculatorApplication extends android.app.Application { - - private static final String paypalDonateUrl = "https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=se%2esolovyev%40gmail%2ecom&lc=RU&item_name=Android%20Calculator¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted"; - - public static final String AD_FREE_PRODUCT_ID = "ad_free"; - public static final String AD_FREE_P_KEY = "org.solovyev.android.calculator_ad_free"; - - public static final String ADMOB_USER_ID = "a14f02cf9c80cbc"; - public static final String REMOTE_STACK_TRACE_URL = "http://calculatorpp.com/crash_reports/upload.php"; - - @NotNull - private static CalculatorApplication instance; - - public CalculatorApplication() { - instance = this; - } - - @NotNull - public static CalculatorApplication getInstance() { - return instance; - } - - @Override - public void onCreate() { - super.onCreate(); - - AdsController.getInstance().init(ADMOB_USER_ID, AD_FREE_PRODUCT_ID, new BillingController.IConfiguration() { - - @Override - public byte[] getObfuscationSalt() { - return new byte[]{81, -114, 32, -127, -32, -104, -40, -15, -47, 57, -13, -41, -33, 67, -114, 7, -11, 53, 126, 82}; - } - - @Override - public String getPublicKey() { - return CalculatorSecurity.getPK(); - } - }); - - CalculatorEngine.instance.init(this, PreferenceManager.getDefaultSharedPreferences(this)); - - } - - public static void showDonationDialog(@NotNull final Context context) { - final LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(LAYOUT_INFLATER_SERVICE); - final View view = layoutInflater.inflate(R.layout.donate, null); - - final TextView donate = (TextView) view.findViewById(R.id.donateText); - donate.setMovementMethod(LinkMovementMethod.getInstance()); - - final AlertDialog.Builder builder = new AlertDialog.Builder(context) - .setCancelable(true) - .setNegativeButton(R.string.c_cancel, null) - .setPositiveButton(R.string.c_donate, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - final Intent i = new Intent(Intent.ACTION_VIEW); - i.setData(Uri.parse(paypalDonateUrl)); - context.startActivity(i); - } - }) - .setView(view); - - builder.create().show(); - } - - public static void registerOnRemoteStackTrace() { - //Thread.setDefaultUncaughtExceptionHandler(new CustomExceptionHandler(null, REMOTE_STACK_TRACE_URL)); - } -} +package org.solovyev.android.calculator; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.net.Uri; +import android.preference.PreferenceManager; +import android.text.method.LinkMovementMethod; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.TextView; +import net.robotmedia.billing.BillingController; +import org.jetbrains.annotations.NotNull; +import org.solovyev.android.ads.AdsController; +import org.solovyev.android.calculator.model.CalculatorEngine; + +/** + * User: serso + * Date: 12/1/11 + * Time: 1:21 PM + */ +public class CalculatorApplication extends android.app.Application { + + private static final String paypalDonateUrl = "https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=se%2esolovyev%40gmail%2ecom&lc=RU&item_name=Android%20Calculator¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted"; + + public static final String AD_FREE_PRODUCT_ID = "ad_free"; + public static final String AD_FREE_P_KEY = "org.solovyev.android.calculator_ad_free"; + + public static final String ADMOB_USER_ID = "a14f02cf9c80cbc"; + public static final String REMOTE_STACK_TRACE_URL = "http://calculatorpp.com/crash_reports/upload.php"; + + @NotNull + private static CalculatorApplication instance; + + public CalculatorApplication() { + instance = this; + } + + @NotNull + public static CalculatorApplication getInstance() { + return instance; + } + + @Override + public void onCreate() { + super.onCreate(); + + CalculatorLocatorImpl.getInstance().setCalculatorEngine(CalculatorEngine.instance); + + AdsController.getInstance().init(ADMOB_USER_ID, AD_FREE_PRODUCT_ID, new BillingController.IConfiguration() { + + @Override + public byte[] getObfuscationSalt() { + return new byte[]{81, -114, 32, -127, -32, -104, -40, -15, -47, 57, -13, -41, -33, 67, -114, 7, -11, 53, 126, 82}; + } + + @Override + public String getPublicKey() { + return CalculatorSecurity.getPK(); + } + }); + + CalculatorEngine.instance.init(this, PreferenceManager.getDefaultSharedPreferences(this)); + + } + + public static void showDonationDialog(@NotNull final Context context) { + final LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(LAYOUT_INFLATER_SERVICE); + final View view = layoutInflater.inflate(R.layout.donate, null); + + final TextView donate = (TextView) view.findViewById(R.id.donateText); + donate.setMovementMethod(LinkMovementMethod.getInstance()); + + final AlertDialog.Builder builder = new AlertDialog.Builder(context) + .setCancelable(true) + .setNegativeButton(R.string.c_cancel, null) + .setPositiveButton(R.string.c_donate, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + final Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse(paypalDonateUrl)); + context.startActivity(i); + } + }) + .setView(view); + + builder.create().show(); + } + + public static void registerOnRemoteStackTrace() { + //Thread.setDefaultUncaughtExceptionHandler(new CustomExceptionHandler(null, REMOTE_STACK_TRACE_URL)); + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorDisplay.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorDisplay.java deleted file mode 100644 index 77b21101..00000000 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorDisplay.java +++ /dev/null @@ -1,348 +0,0 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - */ - -package org.solovyev.android.calculator; - -import android.content.Context; -import android.graphics.Color; -import android.text.Html; -import android.util.AttributeSet; -import android.util.Log; -import jscl.NumeralBase; -import jscl.math.Generic; -import jscl.math.function.Constant; -import jscl.math.function.IConstant; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.solovyev.android.calculator.jscl.JsclOperation; -import org.solovyev.android.calculator.model.CalculatorEngine; -import org.solovyev.android.calculator.model.CalculatorParseException; -import org.solovyev.android.calculator.model.TextProcessor; -import org.solovyev.android.calculator.model.ToJsclTextProcessor; -import org.solovyev.android.calculator.view.NumeralBaseConverterDialog; -import org.solovyev.android.calculator.view.TextHighlighter; -import org.solovyev.android.calculator.view.UnitConverterViewBuilder; -import org.solovyev.android.menu.AMenuItem; -import org.solovyev.android.menu.LabeledMenuItem; -import org.solovyev.android.view.AutoResizeTextView; -import org.solovyev.common.collections.CollectionsUtils; -import org.solovyev.common.text.StringUtils; - -import java.util.HashSet; -import java.util.Set; - -/** - * User: serso - * Date: 9/17/11 - * Time: 10:58 PM - */ -public class CalculatorDisplay extends AutoResizeTextView implements ICalculatorDisplay{ - - 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 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 - } -} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorEditor.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorEditor.java index 013b5a09..3f9b20c9 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorEditor.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorEditor.java @@ -1,166 +1,165 @@ -/* - * 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.content.SharedPreferences; -import android.graphics.Color; -import android.os.Build; -import android.text.Html; -import android.util.AttributeSet; -import android.util.Log; -import android.view.ContextMenu; -import android.widget.EditText; -import org.jetbrains.annotations.NotNull; -import org.solovyev.android.calculator.model.CalculatorEngine; -import org.solovyev.android.calculator.model.CalculatorParseException; -import org.solovyev.android.calculator.model.TextProcessor; -import org.solovyev.android.calculator.view.TextHighlighter; -import org.solovyev.common.collections.CollectionsUtils; - -/** - * User: serso - * Date: 9/17/11 - * Time: 12:25 AM - */ -public class CalculatorEditor extends EditText implements SharedPreferences.OnSharedPreferenceChangeListener { - - private static final String CALC_COLOR_DISPLAY_KEY = "org.solovyev.android.calculator.CalculatorModel_color_display"; - private static final boolean CALC_COLOR_DISPLAY_DEFAULT = true; - - private boolean highlightText = true; - - @NotNull - private final static TextProcessor textHighlighter = new TextHighlighter(Color.WHITE, true, CalculatorEngine.instance.getEngine()); - - public CalculatorEditor(Context context) { - super(context); - init(); - } - - public CalculatorEditor(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - private void init() { - // NOTE: in this solution cursor is missing - - /*this.setOnTouchListener(new OnTouchListener() { - @Override - public boolean onTouch(View v, MotionEvent event) { - final TextView textView = (TextView)v; - // backup the input type - int inputType = textView.getInputType(); - - // disable soft input - textView.setInputType(InputType.TYPE_NULL); - - // call native handler - textView.onTouchEvent(event); - - // restore input type - textView.setInputType(inputType); - - // consume touch even - return true; - } - });*/ - } - - - @Override - public boolean onCheckIsTextEditor() { - // NOTE: code below can be used carefully and should not be copied without special intention - // The main purpose of code is to disable soft input (virtual keyboard) but leave all the TextEdit functionality, like cursor, scrolling, copy/paste menu etc - - if ( Build.VERSION.SDK_INT >= 11 ) { - // fix for missing cursor in android 3 and higher - try { - // IDEA: return false always except if method was called from TextView.isCursorVisible() method - for (StackTraceElement stackTraceElement : CollectionsUtils.asList(Thread.currentThread().getStackTrace())) { - if ( "isCursorVisible".equals(stackTraceElement.getMethodName()) ) { - return true; - } - } - } catch (RuntimeException e) { - // just in case... - } - - return false; - } else { - return false; - } - } - - public CalculatorEditor(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - init(); - } - - @Override - protected void onCreateContextMenu(ContextMenu menu) { - super.onCreateContextMenu(menu); - - menu.removeItem(android.R.id.selectAll); - } - - @Override - public void setText(CharSequence text, BufferType type) { - super.setText(text, type); - } - - public synchronized void redraw() { - String text = getText().toString(); - - int selectionStart = getSelectionStart(); - int selectionEnd = getSelectionEnd(); - - if (highlightText) { - - Log.d(this.getClass().getName(), text); - - try { - final TextHighlighter.Result result = textHighlighter.process(text); - selectionStart += result.getOffset(); - selectionEnd += result.getOffset(); - 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); - } else { - super.setText(text, BufferType.EDITABLE); - } - - Log.d(this.getClass().getName(), getText().toString()); - - int length = getText().length(); - setSelection(Math.max(Math.min(length, selectionStart), 0), Math.max(Math.min(length, selectionEnd), 0)); - } - - public boolean isHighlightText() { - return highlightText; - } - - public void setHighlightText(boolean highlightText) { - this.highlightText = highlightText; - redraw(); - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences preferences, String key) { - if (CALC_COLOR_DISPLAY_KEY.equals(key)) { - this.setHighlightText(preferences.getBoolean(CALC_COLOR_DISPLAY_KEY, CALC_COLOR_DISPLAY_DEFAULT)); - } - } - - public void init(@NotNull SharedPreferences preferences) { - onSharedPreferenceChanged(preferences, CALC_COLOR_DISPLAY_KEY); - } -} +/* + * 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.content.SharedPreferences; +import android.graphics.Color; +import android.os.Build; +import android.text.Html; +import android.util.AttributeSet; +import android.util.Log; +import android.view.ContextMenu; +import android.widget.EditText; +import org.jetbrains.annotations.NotNull; +import org.solovyev.android.calculator.model.CalculatorEngine; +import org.solovyev.android.calculator.text.TextProcessor; +import org.solovyev.android.calculator.view.TextHighlighter; +import org.solovyev.common.collections.CollectionsUtils; + +/** + * User: serso + * Date: 9/17/11 + * Time: 12:25 AM + */ +public class CalculatorEditor extends EditText implements SharedPreferences.OnSharedPreferenceChangeListener { + + private static final String CALC_COLOR_DISPLAY_KEY = "org.solovyev.android.calculator.CalculatorModel_color_display"; + private static final boolean CALC_COLOR_DISPLAY_DEFAULT = true; + + private boolean highlightText = true; + + @NotNull + private final static TextProcessor textHighlighter = new TextHighlighter(Color.WHITE, true, CalculatorEngine.instance.getEngine()); + + public CalculatorEditor(Context context) { + super(context); + init(); + } + + public CalculatorEditor(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + private void init() { + // NOTE: in this solution cursor is missing + + /*this.setOnTouchListener(new OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + final TextView textView = (TextView)v; + // backup the input type + int inputType = textView.getInputType(); + + // disable soft input + textView.setInputType(InputType.TYPE_NULL); + + // call native handler + textView.onTouchEvent(event); + + // restore input type + textView.setInputType(inputType); + + // consume touch even + return true; + } + });*/ + } + + + @Override + public boolean onCheckIsTextEditor() { + // NOTE: code below can be used carefully and should not be copied without special intention + // The main purpose of code is to disable soft input (virtual keyboard) but leave all the TextEdit functionality, like cursor, scrolling, copy/paste menu etc + + if ( Build.VERSION.SDK_INT >= 11 ) { + // fix for missing cursor in android 3 and higher + try { + // IDEA: return false always except if method was called from TextView.isCursorVisible() method + for (StackTraceElement stackTraceElement : CollectionsUtils.asList(Thread.currentThread().getStackTrace())) { + if ( "isCursorVisible".equals(stackTraceElement.getMethodName()) ) { + return true; + } + } + } catch (RuntimeException e) { + // just in case... + } + + return false; + } else { + return false; + } + } + + public CalculatorEditor(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + @Override + protected void onCreateContextMenu(ContextMenu menu) { + super.onCreateContextMenu(menu); + + menu.removeItem(android.R.id.selectAll); + } + + @Override + public void setText(CharSequence text, BufferType type) { + super.setText(text, type); + } + + public synchronized void redraw() { + String text = getText().toString(); + + int selectionStart = getSelectionStart(); + int selectionEnd = getSelectionEnd(); + + if (highlightText) { + + Log.d(this.getClass().getName(), text); + + try { + final TextHighlighter.Result result = textHighlighter.process(text); + selectionStart += result.getOffset(); + selectionEnd += result.getOffset(); + 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); + } else { + super.setText(text, BufferType.EDITABLE); + } + + Log.d(this.getClass().getName(), getText().toString()); + + int length = getText().length(); + setSelection(Math.max(Math.min(length, selectionStart), 0), Math.max(Math.min(length, selectionEnd), 0)); + } + + public boolean isHighlightText() { + return highlightText; + } + + public void setHighlightText(boolean highlightText) { + this.highlightText = highlightText; + redraw(); + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences preferences, String key) { + if (CALC_COLOR_DISPLAY_KEY.equals(key)) { + this.setHighlightText(preferences.getBoolean(CALC_COLOR_DISPLAY_KEY, CALC_COLOR_DISPLAY_DEFAULT)); + } + } + + public void init(@NotNull SharedPreferences preferences) { + onSharedPreferenceChanged(preferences, CALC_COLOR_DISPLAY_KEY); + } +} 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 bc78f737..e43e9131 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorModel.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorModel.java @@ -20,14 +20,12 @@ 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.CalculatorHistory; +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.calculator.model.CalculatorEvalException; -import org.solovyev.android.calculator.model.CalculatorParseException; import org.solovyev.android.history.HistoryControl; import org.solovyev.android.menu.AMenuBuilder; import org.solovyev.android.menu.MenuImpl; @@ -55,7 +53,7 @@ public enum CalculatorModel implements CursorControl, HistoryControl 0) { - display.setText(result.getResult()); + display.setText(result.getStringResult()); } else { display.setText(""); } - display.setJsclOperation(result.getUserOperation()); - display.setGenericResult(result.getGenericResult()); + display.setJsclOperation(result.getOperation()); + display.setGenericResult(result.getResult()); } catch (CalculatorParseException e) { handleEvaluationException(expression, display, operation, e); } catch (CalculatorEvalException e) { @@ -257,7 +255,7 @@ public enum CalculatorModel implements CursorControl, HistoryControl filteredMenuItems = new ArrayList(CalculatorDisplay.MenuItem.values().length); - for (CalculatorDisplay.MenuItem menuItem : CalculatorDisplay.MenuItem.values()) { - if (menuItem.isItemVisible(cd)) { + 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); } } @@ -400,7 +400,7 @@ public enum CalculatorModel implements CursorControl, HistoryControl { + + convert_to_bin(NumeralBase.bin), + convert_to_dec(NumeralBase.dec), + convert_to_hex(NumeralBase.hex); + + @NotNull + private final NumeralBase toNumeralBase; + + 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 (CalculatorImpl.ConversionException e) { + // conversion is not possible => return false + } + } + } + + return result; + } + + @Override + public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) { + final NumeralBase fromNumeralBase = CalculatorEngine.instance.getEngine().getNumeralBase(); + + final Generic lastResult = data.getResult(); + + if (lastResult != null) { + convert(lastResult); + } + } + + private void convert(@NotNull Generic generic) { + CalculatorLocatorImpl.getInstance().getCalculator().convert(generic, this.toNumeralBase); + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/ICalculatorDisplay.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/ICalculatorDisplay.java deleted file mode 100644 index 1c0b892f..00000000 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/ICalculatorDisplay.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.history.Editor; -import org.solovyev.android.calculator.jscl.JsclOperation; - -/** - * User: serso - * Date: 12/17/11 - * Time: 9:45 PM - */ -public interface ICalculatorDisplay extends Editor{ - - boolean isValid(); - - void setValid(boolean valid); - - @Nullable - String getErrorMessage(); - - void setErrorMessage(@Nullable String errorMessage); - - void setJsclOperation(@NotNull JsclOperation jsclOperation); - - @NotNull - JsclOperation getJsclOperation(); - - void setGenericResult(@Nullable Generic genericResult); - - @Nullable - Generic getGenericResult(); -} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/AbstractHistoryActivity.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/history/AbstractHistoryActivity.java index 3069aafd..0f608392 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/AbstractHistoryActivity.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/history/AbstractHistoryActivity.java @@ -1,263 +1,263 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - * or visit http://se.solovyev.org - */ - -package org.solovyev.android.calculator.history; - -import android.app.ListActivity; -import android.content.Context; -import android.os.Bundle; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.EditText; -import android.widget.ListView; -import com.google.ads.AdView; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.solovyev.android.ads.AdsController; -import org.solovyev.android.calculator.CalculatorModel; -import org.solovyev.android.calculator.R; -import org.solovyev.android.calculator.jscl.JsclOperation; -import org.solovyev.android.menu.AMenuBuilder; -import org.solovyev.android.menu.MenuImpl; -import org.solovyev.common.collections.CollectionsUtils; -import org.solovyev.common.equals.Equalizer; -import org.solovyev.common.filter.Filter; -import org.solovyev.common.filter.FilterRule; -import org.solovyev.common.filter.FilterRulesChain; -import org.solovyev.common.text.StringUtils; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -/** - * User: serso - * Date: 10/15/11 - * Time: 1:13 PM - */ -public abstract class AbstractHistoryActivity extends ListActivity { - - public static final Comparator COMPARATOR = new Comparator() { - @Override - public int compare(CalculatorHistoryState state1, CalculatorHistoryState state2) { - if (state1.isSaved() == state2.isSaved()) { - long l = state2.getTime() - state1.getTime(); - return l > 0l ? 1 : (l < 0l ? -1 : 0); - } else if (state1.isSaved()) { - return -1; - } else if (state2.isSaved()) { - return 1; - } - return 0; - } - }; - - - @NotNull - private ArrayAdapter adapter; - - @Nullable - private AdView adView; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.history_activity); - - adView = AdsController.getInstance().inflateAd(this); - - adapter = new HistoryArrayAdapter(this, getLayoutId(), R.id.history_item, new ArrayList()); - setListAdapter(adapter); - - final ListView lv = getListView(); - lv.setTextFilterEnabled(true); - - lv.setOnItemClickListener(new AdapterView.OnItemClickListener() { - public void onItemClick(final AdapterView parent, - final View view, - final int position, - final long id) { - - useHistoryItem((CalculatorHistoryState) parent.getItemAtPosition(position), AbstractHistoryActivity.this); - } - }); - - lv.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { - @Override - public boolean onItemLongClick(AdapterView parent, View view, final int position, long id) { - final CalculatorHistoryState historyState = (CalculatorHistoryState) parent.getItemAtPosition(position); - - final Context context = AbstractHistoryActivity.this; - - final HistoryItemMenuData data = new HistoryItemMenuData(historyState, adapter); - - final List menuItems = CollectionsUtils.asList(HistoryItemMenuItem.values()); - - if (historyState.isSaved()) { - menuItems.remove(HistoryItemMenuItem.save); - } else { - if (isAlreadySaved(historyState)) { - menuItems.remove(HistoryItemMenuItem.save); - } - menuItems.remove(HistoryItemMenuItem.remove); - menuItems.remove(HistoryItemMenuItem.edit); - } - - if (historyState.getDisplayState().isValid() && StringUtils.isEmpty(historyState.getDisplayState().getEditorState().getText())) { - menuItems.remove(HistoryItemMenuItem.copy_result); - } - - final AMenuBuilder menuBuilder = AMenuBuilder.newInstance(context, MenuImpl.newInstance(menuItems)); - menuBuilder.create(data).show(); - - return true; - } - }); - } - - @Override - protected void onDestroy() { - if ( this.adView != null ) { - this.adView.destroy(); - } - super.onDestroy(); - } - - protected abstract int getLayoutId(); - - @Override - protected void onResume() { - super.onResume(); - - final List historyList = getHistoryList(); - try { - this.adapter.setNotifyOnChange(false); - this.adapter.clear(); - for (CalculatorHistoryState historyState : historyList) { - this.adapter.add(historyState); - } - } finally { - this.adapter.setNotifyOnChange(true); - } - - this.adapter.notifyDataSetChanged(); - } - - public static boolean isAlreadySaved(@NotNull CalculatorHistoryState historyState) { - assert !historyState.isSaved(); - - boolean result = false; - try { - historyState.setSaved(true); - if ( CollectionsUtils.contains(historyState, CalculatorHistory.instance.getSavedHistory(), new Equalizer() { - @Override - public boolean equals(@Nullable CalculatorHistoryState first, @Nullable CalculatorHistoryState second) { - return first != null && second != null && - first.getTime() == second.getTime() && - first.getDisplayState().equals(second.getDisplayState()) && - first.getEditorState().equals(second.getEditorState()); - } - }) ) { - result = true; - } - } finally { - historyState.setSaved(false); - } - return result; - } - - public static void useHistoryItem(@NotNull final CalculatorHistoryState historyState, @NotNull AbstractHistoryActivity activity) { - - // before evaluating history item - clear display (in order to get Error message in display if evaluation fail) - CalculatorModel.instance.getDisplay().setText(""); - CalculatorModel.instance.doTextOperation(new CalculatorModel.TextOperation() { - @Override - public void doOperation(@NotNull EditText editor) { - final EditorHistoryState editorState = historyState.getEditorState(); - editor.setText(editorState.getText()); - editor.setSelection(editorState.getCursorPosition()); - } - }, false, historyState.getDisplayState().getJsclOperation(), true); - - CalculatorModel.instance.setCursorOnEnd(); - - activity.finish(); - } - - @NotNull - private List getHistoryList() { - final List calculatorHistoryStates = getHistoryItems(); - - Collections.sort(calculatorHistoryStates, COMPARATOR); - - final FilterRulesChain filterRulesChain = new FilterRulesChain(); - filterRulesChain.addFilterRule(new FilterRule() { - @Override - public boolean isFiltered(CalculatorHistoryState object) { - return object == null || StringUtils.isEmpty(object.getEditorState().getText()); - } - }); - - new Filter(filterRulesChain).filter(calculatorHistoryStates.iterator()); - - return calculatorHistoryStates; - } - - @NotNull - protected abstract List getHistoryItems(); - - @NotNull - public static String getHistoryText(@NotNull CalculatorHistoryState state) { - final StringBuilder result = new StringBuilder(); - result.append(state.getEditorState().getText()); - result.append(getIdentitySign(state.getDisplayState().getJsclOperation())); - final String expressionResult = state.getDisplayState().getEditorState().getText(); - if (expressionResult != null) { - result.append(expressionResult); - } - return result.toString(); - } - - @NotNull - private static String getIdentitySign(@NotNull JsclOperation jsclOperation) { - return jsclOperation == JsclOperation.simplify ? "≡" : "="; - } - - @Override - public boolean onCreateOptionsMenu(android.view.Menu menu) { - final MenuInflater menuInflater = getMenuInflater(); - menuInflater.inflate(R.menu.history_menu, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - boolean result; - - switch (item.getItemId()) { - case R.id.history_menu_clear_history: - clearHistory(); - result = true; - break; - default: - result = super.onOptionsItemSelected(item); - } - - return result; - } - - protected abstract void clearHistory(); - - @NotNull - protected ArrayAdapter getAdapter() { - return adapter; - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + * or visit http://se.solovyev.org + */ + +package org.solovyev.android.calculator.history; + +import android.app.ListActivity; +import android.content.Context; +import android.os.Bundle; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.EditText; +import android.widget.ListView; +import com.google.ads.AdView; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.ads.AdsController; +import org.solovyev.android.calculator.CalculatorModel; +import org.solovyev.android.calculator.R; +import org.solovyev.android.calculator.jscl.JsclOperation; +import org.solovyev.android.menu.AMenuBuilder; +import org.solovyev.android.menu.MenuImpl; +import org.solovyev.common.collections.CollectionsUtils; +import org.solovyev.common.equals.Equalizer; +import org.solovyev.common.filter.Filter; +import org.solovyev.common.filter.FilterRule; +import org.solovyev.common.filter.FilterRulesChain; +import org.solovyev.common.text.StringUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * User: serso + * Date: 10/15/11 + * Time: 1:13 PM + */ +public abstract class AbstractHistoryActivity extends ListActivity { + + public static final Comparator COMPARATOR = new Comparator() { + @Override + public int compare(CalculatorHistoryState state1, CalculatorHistoryState state2) { + if (state1.isSaved() == state2.isSaved()) { + long l = state2.getTime() - state1.getTime(); + return l > 0l ? 1 : (l < 0l ? -1 : 0); + } else if (state1.isSaved()) { + return -1; + } else if (state2.isSaved()) { + return 1; + } + return 0; + } + }; + + + @NotNull + private ArrayAdapter adapter; + + @Nullable + private AdView adView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.history_activity); + + adView = AdsController.getInstance().inflateAd(this); + + adapter = new HistoryArrayAdapter(this, getLayoutId(), R.id.history_item, new ArrayList()); + setListAdapter(adapter); + + final ListView lv = getListView(); + lv.setTextFilterEnabled(true); + + lv.setOnItemClickListener(new AdapterView.OnItemClickListener() { + public void onItemClick(final AdapterView parent, + final View view, + final int position, + final long id) { + + useHistoryItem((CalculatorHistoryState) parent.getItemAtPosition(position), AbstractHistoryActivity.this); + } + }); + + lv.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { + @Override + public boolean onItemLongClick(AdapterView parent, View view, final int position, long id) { + final CalculatorHistoryState historyState = (CalculatorHistoryState) parent.getItemAtPosition(position); + + final Context context = AbstractHistoryActivity.this; + + final HistoryItemMenuData data = new HistoryItemMenuData(historyState, adapter); + + final List menuItems = CollectionsUtils.asList(HistoryItemMenuItem.values()); + + if (historyState.isSaved()) { + menuItems.remove(HistoryItemMenuItem.save); + } else { + if (isAlreadySaved(historyState)) { + menuItems.remove(HistoryItemMenuItem.save); + } + menuItems.remove(HistoryItemMenuItem.remove); + menuItems.remove(HistoryItemMenuItem.edit); + } + + if (historyState.getDisplayState().isValid() && StringUtils.isEmpty(historyState.getDisplayState().getEditorState().getText())) { + menuItems.remove(HistoryItemMenuItem.copy_result); + } + + final AMenuBuilder menuBuilder = AMenuBuilder.newInstance(context, MenuImpl.newInstance(menuItems)); + menuBuilder.create(data).show(); + + return true; + } + }); + } + + @Override + protected void onDestroy() { + if ( this.adView != null ) { + this.adView.destroy(); + } + super.onDestroy(); + } + + protected abstract int getLayoutId(); + + @Override + protected void onResume() { + super.onResume(); + + final List historyList = getHistoryList(); + try { + this.adapter.setNotifyOnChange(false); + this.adapter.clear(); + for (CalculatorHistoryState historyState : historyList) { + this.adapter.add(historyState); + } + } finally { + this.adapter.setNotifyOnChange(true); + } + + this.adapter.notifyDataSetChanged(); + } + + public static boolean isAlreadySaved(@NotNull CalculatorHistoryState historyState) { + assert !historyState.isSaved(); + + boolean result = false; + try { + historyState.setSaved(true); + if ( CollectionsUtils.contains(historyState, AndroidCalculatorHistoryImpl.instance.getSavedHistory(), new Equalizer() { + @Override + public boolean equals(@Nullable CalculatorHistoryState first, @Nullable CalculatorHistoryState second) { + return first != null && second != null && + first.getTime() == second.getTime() && + first.getDisplayState().equals(second.getDisplayState()) && + first.getEditorState().equals(second.getEditorState()); + } + }) ) { + result = true; + } + } finally { + historyState.setSaved(false); + } + return result; + } + + public static void useHistoryItem(@NotNull final CalculatorHistoryState historyState, @NotNull AbstractHistoryActivity activity) { + + // before evaluating history item - clear display (in order to get Error message in display if evaluation fail) + CalculatorModel.instance.getDisplay().setText(""); + CalculatorModel.instance.doTextOperation(new CalculatorModel.TextOperation() { + @Override + public void doOperation(@NotNull EditText editor) { + final EditorHistoryState editorState = historyState.getEditorState(); + editor.setText(editorState.getText()); + editor.setSelection(editorState.getCursorPosition()); + } + }, false, historyState.getDisplayState().getJsclOperation(), true); + + CalculatorModel.instance.setCursorOnEnd(); + + activity.finish(); + } + + @NotNull + private List getHistoryList() { + final List calculatorHistoryStates = getHistoryItems(); + + Collections.sort(calculatorHistoryStates, COMPARATOR); + + final FilterRulesChain filterRulesChain = new FilterRulesChain(); + filterRulesChain.addFilterRule(new FilterRule() { + @Override + public boolean isFiltered(CalculatorHistoryState object) { + return object == null || StringUtils.isEmpty(object.getEditorState().getText()); + } + }); + + new Filter(filterRulesChain).filter(calculatorHistoryStates.iterator()); + + return calculatorHistoryStates; + } + + @NotNull + protected abstract List getHistoryItems(); + + @NotNull + public static String getHistoryText(@NotNull CalculatorHistoryState state) { + final StringBuilder result = new StringBuilder(); + result.append(state.getEditorState().getText()); + result.append(getIdentitySign(state.getDisplayState().getJsclOperation())); + final String expressionResult = state.getDisplayState().getEditorState().getText(); + if (expressionResult != null) { + result.append(expressionResult); + } + return result.toString(); + } + + @NotNull + private static String getIdentitySign(@NotNull JsclOperation jsclOperation) { + return jsclOperation == JsclOperation.simplify ? "≡" : "="; + } + + @Override + public boolean onCreateOptionsMenu(android.view.Menu menu) { + final MenuInflater menuInflater = getMenuInflater(); + menuInflater.inflate(R.menu.history_menu, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + boolean result; + + switch (item.getItemId()) { + case R.id.history_menu_clear_history: + clearHistory(); + result = true; + break; + default: + result = super.onOptionsItemSelected(item); + } + + return result; + } + + protected abstract void clearHistory(); + + @NotNull + protected ArrayAdapter getAdapter() { + return adapter; + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/AndroidCalculatorHistory.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/history/AndroidCalculatorHistory.java new file mode 100644 index 00000000..2f4dc4b4 --- /dev/null +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/history/AndroidCalculatorHistory.java @@ -0,0 +1,18 @@ +package org.solovyev.android.calculator.history; + +import android.content.Context; +import android.content.SharedPreferences; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 16:07 + */ +public interface AndroidCalculatorHistory extends CalculatorHistory { + + void load(@Nullable Context context, @Nullable SharedPreferences preferences); + + void save(@NotNull Context context); +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/AndroidCalculatorHistoryImpl.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/history/AndroidCalculatorHistoryImpl.java new file mode 100644 index 00000000..b5cba95f --- /dev/null +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/history/AndroidCalculatorHistoryImpl.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + * or visit http://se.solovyev.org + */ + +package org.solovyev.android.calculator.history; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.calculator.R; +import org.solovyev.common.history.HistoryAction; + +import java.util.List; + +/** + * User: serso + * Date: 10/9/11 + * Time: 6:35 PM + */ +public enum AndroidCalculatorHistoryImpl implements AndroidCalculatorHistory { + + instance; + + @NotNull + private final CalculatorHistoryImpl calculatorHistory = new CalculatorHistoryImpl(); + + @Override + public void load(@Nullable Context context, @Nullable SharedPreferences preferences) { + if (context != null && preferences != null) { + final String value = preferences.getString(context.getString(R.string.p_calc_history), null); + calculatorHistory.fromXml(value); + } + } + + @Override + public void save(@NotNull Context context) { + final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context); + final SharedPreferences.Editor editor = settings.edit(); + + editor.putString(context.getString(R.string.p_calc_history), calculatorHistory.toXml()); + + editor.commit(); + } + + public void clearSavedHistory(@NotNull Context context) { + calculatorHistory.clearSavedHistory(); + save(context); + } + + public void removeSavedHistory(@NotNull CalculatorHistoryState historyState, @NotNull Context context) { + historyState.setSaved(false); + calculatorHistory.removeSavedHistory(historyState); + save(context); + } + + @Override + public boolean isEmpty() { + return calculatorHistory.isEmpty(); + } + + @Override + public CalculatorHistoryState getLastHistoryState() { + return calculatorHistory.getLastHistoryState(); + } + + @Override + public boolean isUndoAvailable() { + return calculatorHistory.isUndoAvailable(); + } + + @Override + public CalculatorHistoryState undo(@Nullable CalculatorHistoryState currentState) { + return calculatorHistory.undo(currentState); + } + + @Override + public boolean isRedoAvailable() { + return calculatorHistory.isRedoAvailable(); + } + + @Override + public CalculatorHistoryState redo(@Nullable CalculatorHistoryState currentState) { + return calculatorHistory.redo(currentState); + } + + @Override + public boolean isActionAvailable(@NotNull HistoryAction historyAction) { + return calculatorHistory.isActionAvailable(historyAction); + } + + @Override + public CalculatorHistoryState doAction(@NotNull HistoryAction historyAction, @Nullable CalculatorHistoryState currentState) { + return calculatorHistory.doAction(historyAction, currentState); + } + + @Override + public void addState(@Nullable CalculatorHistoryState currentState) { + calculatorHistory.addState(currentState); + } + + @NotNull + @Override + public List getStates() { + return calculatorHistory.getStates(); + } + + @Override + public void clear() { + calculatorHistory.clear(); + } + + @NotNull + public List getSavedHistory() { + return calculatorHistory.getSavedHistory(); + } + + @NotNull + public CalculatorHistoryState addSavedState(@NotNull CalculatorHistoryState historyState) { + return calculatorHistory.addSavedState(historyState); + } + + @Override + public void fromXml(@NotNull String xml) { + calculatorHistory.fromXml(xml); + } + + @Override + public String toXml() { + return calculatorHistory.toXml(); + } + + @Override + public void clearSavedHistory() { + calculatorHistory.clearSavedHistory(); + } + + @Override + public void removeSavedHistory(@NotNull CalculatorHistoryState historyState) { + calculatorHistory.removeSavedHistory(historyState); + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/CalculatorHistory.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/history/CalculatorHistory.java deleted file mode 100644 index c4967038..00000000 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/CalculatorHistory.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - * or visit http://se.solovyev.org - */ - -package org.solovyev.android.calculator.history; - -import android.content.Context; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.solovyev.android.calculator.R; -import org.solovyev.common.history.HistoryAction; -import org.solovyev.common.history.HistoryHelper; -import org.solovyev.common.history.SimpleHistoryHelper; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * User: serso - * Date: 10/9/11 - * Time: 6:35 PM - */ -public enum CalculatorHistory implements HistoryHelper { - - instance; - - // todo serso: not synchronized - private int counter = 0; - - @NotNull - private final HistoryHelper history = new SimpleHistoryHelper(); - - @NotNull - private final List savedHistory = new ArrayList (); - - @Override - public boolean isEmpty() { - return this.history.isEmpty(); - } - - @Override - public CalculatorHistoryState getLastHistoryState() { - return this.history.getLastHistoryState(); - } - - @Override - public boolean isUndoAvailable() { - return history.isUndoAvailable(); - } - - @Override - public CalculatorHistoryState undo(@Nullable CalculatorHistoryState currentState) { - return history.undo(currentState); - } - - @Override - public boolean isRedoAvailable() { - return history.isRedoAvailable(); - } - - @Override - public CalculatorHistoryState redo(@Nullable CalculatorHistoryState currentState) { - return history.redo(currentState); - } - - @Override - public boolean isActionAvailable(@NotNull HistoryAction historyAction) { - return history.isActionAvailable(historyAction); - } - - @Override - public CalculatorHistoryState doAction(@NotNull HistoryAction historyAction, @Nullable CalculatorHistoryState currentState) { - return history.doAction(historyAction, currentState); - } - - @Override - public void addState(@Nullable CalculatorHistoryState currentState) { - history.addState(currentState); - } - - @NotNull - @Override - public List getStates() { - return history.getStates(); - } - - @Override - public void clear() { - this.history.clear(); - } - - public void load(@Nullable Context context, @Nullable SharedPreferences preferences) { - if (context != null && preferences != null) { - final String value = preferences.getString(context.getString(R.string.p_calc_history), null); - this.savedHistory.clear(); - HistoryUtils.fromXml(value, this.savedHistory); - for (CalculatorHistoryState historyState : savedHistory) { - historyState.setSaved(true); - historyState.setId(counter++); - } - } - } - - public void save(@NotNull Context context) { - final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context); - final SharedPreferences.Editor editor = settings.edit(); - - editor.putString(context.getString(R.string.p_calc_history), HistoryUtils.toXml(this.savedHistory)); - - editor.commit(); - } - - @NotNull - public List getSavedHistory() { - return Collections.unmodifiableList(savedHistory); - } - - @NotNull - public CalculatorHistoryState addSavedState(@NotNull CalculatorHistoryState historyState) { - if (historyState.isSaved()) { - return historyState; - } else { - final CalculatorHistoryState savedState = historyState.clone(); - - savedState.setId(counter++); - savedState.setSaved(true); - - savedHistory.add(savedState); - - return savedState; - } - } - - public void clearSavedHistory(@NotNull Context context) { - this.savedHistory.clear(); - save(context); - } - - public void removeSavedHistory(@NotNull CalculatorHistoryState historyState, @NotNull Context context) { - historyState.setSaved(false); - this.savedHistory.remove(historyState); - save(context); - } -} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/HistoryActivityTab.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/history/HistoryActivityTab.java index 56dacc14..73f67340 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/HistoryActivityTab.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/history/HistoryActivityTab.java @@ -1,37 +1,37 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - * or visit http://se.solovyev.org - */ - -package org.solovyev.android.calculator.history; - -import org.jetbrains.annotations.NotNull; -import org.solovyev.android.calculator.R; - -import java.util.ArrayList; -import java.util.List; - -/** - * User: serso - * Date: 12/18/11 - * Time: 7:39 PM - */ -public class HistoryActivityTab extends AbstractHistoryActivity { - @Override - protected int getLayoutId() { - return R.layout.history; - } - - @NotNull - @Override - protected List getHistoryItems() { - return new ArrayList(CalculatorHistory.instance.getStates()); - } - - @Override - protected void clearHistory() { - CalculatorHistory.instance.clear(); - getAdapter().clear(); - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + * or visit http://se.solovyev.org + */ + +package org.solovyev.android.calculator.history; + +import org.jetbrains.annotations.NotNull; +import org.solovyev.android.calculator.R; + +import java.util.ArrayList; +import java.util.List; + +/** + * User: serso + * Date: 12/18/11 + * Time: 7:39 PM + */ +public class HistoryActivityTab extends AbstractHistoryActivity { + @Override + protected int getLayoutId() { + return R.layout.history; + } + + @NotNull + @Override + protected List getHistoryItems() { + return new ArrayList(AndroidCalculatorHistoryImpl.instance.getStates()); + } + + @Override + protected void clearHistory() { + AndroidCalculatorHistoryImpl.instance.clear(); + getAdapter().clear(); + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/HistoryItemMenuItem.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/history/HistoryItemMenuItem.java index 3811c198..7cb1c859 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/HistoryItemMenuItem.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/history/HistoryItemMenuItem.java @@ -1,154 +1,154 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - * or visit http://se.solovyev.org - */ - -package org.solovyev.android.calculator.history; - -import android.app.Activity; -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; -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.solovyev.android.calculator.R; -import org.solovyev.android.menu.LabeledMenuItem; -import org.solovyev.common.text.StringUtils; - -/** -* User: serso -* Date: 12/18/11 -* Time: 3:09 PM -*/ -public enum HistoryItemMenuItem implements LabeledMenuItem { - - use(R.string.c_use) { - @Override - public void onClick(@NotNull HistoryItemMenuData data, @NotNull Context context) { - if (context instanceof AbstractHistoryActivity) { - AbstractHistoryActivity.useHistoryItem(data.getHistoryState(), (AbstractHistoryActivity) context); - } else { - Log.e(HistoryItemMenuItem.class.getName(), AbstractHistoryActivity.class + " must be passed as context!"); - } - } - }, - - copy_expression(R.string.c_copy_expression) { - @Override - public void onClick(@NotNull HistoryItemMenuData data, @NotNull Context context) { - final CalculatorHistoryState calculatorHistoryState = data.getHistoryState(); - final String text = calculatorHistoryState.getEditorState().getText(); - if (!StringUtils.isEmpty(text)) { - final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE); - clipboard.setText(text); - Toast.makeText(context, context.getText(R.string.c_expression_copied), Toast.LENGTH_SHORT).show(); - } - } - }, - - copy_result(R.string.c_copy_result) { - @Override - public void onClick(@NotNull HistoryItemMenuData data, @NotNull Context context) { - final CalculatorHistoryState calculatorHistoryState = data.getHistoryState(); - final String text = calculatorHistoryState.getDisplayState().getEditorState().getText(); - if (!StringUtils.isEmpty(text)) { - final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE); - clipboard.setText(text); - Toast.makeText(context, context.getText(R.string.c_result_copied), Toast.LENGTH_SHORT).show(); - } - } - }, - - save(R.string.c_save) { - @Override - public void onClick(@NotNull final HistoryItemMenuData data, @NotNull final Context context) { - final CalculatorHistoryState historyState = data.getHistoryState(); - if (!historyState.isSaved()) { - createEditHistoryDialog(data, context, true); - } else { - Toast.makeText(context, context.getText(R.string.c_history_already_saved), Toast.LENGTH_LONG).show(); - } - } - }, - - edit(R.string.c_edit) { - @Override - public void onClick(@NotNull final HistoryItemMenuData data, @NotNull final Context context) { - final CalculatorHistoryState historyState = data.getHistoryState(); - if (historyState.isSaved()) { - createEditHistoryDialog(data, context, false); - } else { - Toast.makeText(context, context.getText(R.string.c_history_must_be_saved), Toast.LENGTH_LONG).show(); - } - } - }, - - remove(R.string.c_remove) { - @Override - public void onClick(@NotNull HistoryItemMenuData data, @NotNull Context context) { - final CalculatorHistoryState historyState = data.getHistoryState(); - if (historyState.isSaved()) { - data.getAdapter().remove(historyState); - CalculatorHistory.instance.removeSavedHistory(historyState, context); - Toast.makeText(context, context.getText(R.string.c_history_was_removed), Toast.LENGTH_LONG).show(); - data.getAdapter().notifyDataSetChanged(); - } - } - }; - - private static void createEditHistoryDialog(@NotNull final HistoryItemMenuData data, @NotNull final Context context, final boolean save) { - final CalculatorHistoryState historyState = data.getHistoryState(); - - final LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - final View editView = layoutInflater.inflate(R.layout.history_edit, null); - final TextView historyExpression = (TextView)editView.findViewById(R.id.history_edit_expression); - historyExpression.setText(AbstractHistoryActivity.getHistoryText(historyState)); - - final EditText comment = (EditText)editView.findViewById(R.id.history_edit_comment); - comment.setText(historyState.getComment()); - - final AlertDialog.Builder builder = new AlertDialog.Builder(context) - .setTitle(save ? R.string.c_save_history : R.string.c_edit_history) - .setCancelable(true) - .setNegativeButton(R.string.c_cancel, null) - .setPositiveButton(R.string.c_save, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (save) { - final CalculatorHistoryState savedHistoryItem = CalculatorHistory.instance.addSavedState(historyState); - savedHistoryItem.setComment(comment.getText().toString()); - CalculatorHistory.instance.save(context); - // we don't need to add element to the adapter as adapter of another activity must be updated and not this - //data.getAdapter().add(savedHistoryItem); - } else { - historyState.setComment(comment.getText().toString()); - CalculatorHistory.instance.save(context); - } - data.getAdapter().notifyDataSetChanged(); - Toast.makeText(context, context.getText(R.string.c_history_saved), Toast.LENGTH_LONG).show(); - } - }) - .setView(editView); - - builder.create().show(); - } - - private final int captionId; - - private HistoryItemMenuItem(int captionId) { - this.captionId = captionId; - } - - @NotNull - @Override - public String getCaption(@NotNull Context context) { - return context.getString(captionId); - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + * or visit http://se.solovyev.org + */ + +package org.solovyev.android.calculator.history; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +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.solovyev.android.calculator.R; +import org.solovyev.android.menu.LabeledMenuItem; +import org.solovyev.common.text.StringUtils; + +/** +* User: serso +* Date: 12/18/11 +* Time: 3:09 PM +*/ +public enum HistoryItemMenuItem implements LabeledMenuItem { + + use(R.string.c_use) { + @Override + public void onClick(@NotNull HistoryItemMenuData data, @NotNull Context context) { + if (context instanceof AbstractHistoryActivity) { + AbstractHistoryActivity.useHistoryItem(data.getHistoryState(), (AbstractHistoryActivity) context); + } else { + Log.e(HistoryItemMenuItem.class.getName(), AbstractHistoryActivity.class + " must be passed as context!"); + } + } + }, + + copy_expression(R.string.c_copy_expression) { + @Override + public void onClick(@NotNull HistoryItemMenuData data, @NotNull Context context) { + final CalculatorHistoryState calculatorHistoryState = data.getHistoryState(); + final String text = calculatorHistoryState.getEditorState().getText(); + if (!StringUtils.isEmpty(text)) { + final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE); + clipboard.setText(text); + Toast.makeText(context, context.getText(R.string.c_expression_copied), Toast.LENGTH_SHORT).show(); + } + } + }, + + copy_result(R.string.c_copy_result) { + @Override + public void onClick(@NotNull HistoryItemMenuData data, @NotNull Context context) { + final CalculatorHistoryState calculatorHistoryState = data.getHistoryState(); + final String text = calculatorHistoryState.getDisplayState().getEditorState().getText(); + if (!StringUtils.isEmpty(text)) { + final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE); + clipboard.setText(text); + Toast.makeText(context, context.getText(R.string.c_result_copied), Toast.LENGTH_SHORT).show(); + } + } + }, + + save(R.string.c_save) { + @Override + public void onClick(@NotNull final HistoryItemMenuData data, @NotNull final Context context) { + final CalculatorHistoryState historyState = data.getHistoryState(); + if (!historyState.isSaved()) { + createEditHistoryDialog(data, context, true); + } else { + Toast.makeText(context, context.getText(R.string.c_history_already_saved), Toast.LENGTH_LONG).show(); + } + } + }, + + edit(R.string.c_edit) { + @Override + public void onClick(@NotNull final HistoryItemMenuData data, @NotNull final Context context) { + final CalculatorHistoryState historyState = data.getHistoryState(); + if (historyState.isSaved()) { + createEditHistoryDialog(data, context, false); + } else { + Toast.makeText(context, context.getText(R.string.c_history_must_be_saved), Toast.LENGTH_LONG).show(); + } + } + }, + + remove(R.string.c_remove) { + @Override + public void onClick(@NotNull HistoryItemMenuData data, @NotNull Context context) { + final CalculatorHistoryState historyState = data.getHistoryState(); + if (historyState.isSaved()) { + data.getAdapter().remove(historyState); + AndroidCalculatorHistoryImpl.instance.removeSavedHistory(historyState, context); + Toast.makeText(context, context.getText(R.string.c_history_was_removed), Toast.LENGTH_LONG).show(); + data.getAdapter().notifyDataSetChanged(); + } + } + }; + + private static void createEditHistoryDialog(@NotNull final HistoryItemMenuData data, @NotNull final Context context, final boolean save) { + final CalculatorHistoryState historyState = data.getHistoryState(); + + final LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + final View editView = layoutInflater.inflate(R.layout.history_edit, null); + final TextView historyExpression = (TextView)editView.findViewById(R.id.history_edit_expression); + historyExpression.setText(AbstractHistoryActivity.getHistoryText(historyState)); + + final EditText comment = (EditText)editView.findViewById(R.id.history_edit_comment); + comment.setText(historyState.getComment()); + + final AlertDialog.Builder builder = new AlertDialog.Builder(context) + .setTitle(save ? R.string.c_save_history : R.string.c_edit_history) + .setCancelable(true) + .setNegativeButton(R.string.c_cancel, null) + .setPositiveButton(R.string.c_save, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (save) { + final CalculatorHistoryState savedHistoryItem = AndroidCalculatorHistoryImpl.instance.addSavedState(historyState); + savedHistoryItem.setComment(comment.getText().toString()); + AndroidCalculatorHistoryImpl.instance.save(context); + // we don't need to add element to the adapter as adapter of another activity must be updated and not this + //data.getAdapter().add(savedHistoryItem); + } else { + historyState.setComment(comment.getText().toString()); + AndroidCalculatorHistoryImpl.instance.save(context); + } + data.getAdapter().notifyDataSetChanged(); + Toast.makeText(context, context.getText(R.string.c_history_saved), Toast.LENGTH_LONG).show(); + } + }) + .setView(editView); + + builder.create().show(); + } + + private final int captionId; + + private HistoryItemMenuItem(int captionId) { + this.captionId = captionId; + } + + @NotNull + @Override + public String getCaption(@NotNull Context context) { + return context.getString(captionId); + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/SavedHistoryActivityTab.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/history/SavedHistoryActivityTab.java index 373f2af9..6113c89d 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/SavedHistoryActivityTab.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/history/SavedHistoryActivityTab.java @@ -1,37 +1,37 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - * or visit http://se.solovyev.org - */ - -package org.solovyev.android.calculator.history; - -import org.jetbrains.annotations.NotNull; -import org.solovyev.android.calculator.R; - -import java.util.ArrayList; -import java.util.List; - -/** - * User: serso - * Date: 12/18/11 - * Time: 7:40 PM - */ -public class SavedHistoryActivityTab extends AbstractHistoryActivity { - @Override - protected int getLayoutId() { - return R.layout.saved_history; - } - - @NotNull - @Override - protected List getHistoryItems() { - return new ArrayList(CalculatorHistory.instance.getSavedHistory()); - } - - @Override - protected void clearHistory() { - CalculatorHistory.instance.clearSavedHistory(this); - getAdapter().clear(); - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + * or visit http://se.solovyev.org + */ + +package org.solovyev.android.calculator.history; + +import org.jetbrains.annotations.NotNull; +import org.solovyev.android.calculator.R; + +import java.util.ArrayList; +import java.util.List; + +/** + * User: serso + * Date: 12/18/11 + * Time: 7:40 PM + */ +public class SavedHistoryActivityTab extends AbstractHistoryActivity { + @Override + protected int getLayoutId() { + return R.layout.saved_history; + } + + @NotNull + @Override + protected List getHistoryItems() { + return new ArrayList(AndroidCalculatorHistoryImpl.instance.getSavedHistory()); + } + + @Override + protected void clearHistory() { + AndroidCalculatorHistoryImpl.instance.clearSavedHistory(this); + getAdapter().clear(); + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/TextViewEditorAdapter.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/history/TextViewEditorAdapter.java index b494edd2..35207b07 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/TextViewEditorAdapter.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/history/TextViewEditorAdapter.java @@ -1,49 +1,50 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - * or visit http://se.solovyev.org - */ - -package org.solovyev.android.calculator.history; - -import android.widget.EditText; -import android.widget.TextView; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * User: serso - * Date: 12/17/11 - * Time: 9:39 PM - */ -public class TextViewEditorAdapter implements Editor { - - @NotNull - private final TextView textView; - - public TextViewEditorAdapter(@NotNull TextView textView) { - this.textView = textView; - } - - @Override - public CharSequence getText() { - return textView.getText().toString(); - } - - @Override - public void setText(@Nullable CharSequence text) { - textView.setText(text); - } - - @Override - public int getSelection() { - return textView.getSelectionStart(); - } - - @Override - public void setSelection(int selection) { - if ( textView instanceof EditText ) { - ((EditText) textView).setSelection(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 android.widget.EditText; +import android.widget.TextView; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.calculator.Editor; + +/** + * User: serso + * Date: 12/17/11 + * Time: 9:39 PM + */ +public class TextViewEditorAdapter implements Editor { + + @NotNull + private final TextView textView; + + public TextViewEditorAdapter(@NotNull TextView textView) { + this.textView = textView; + } + + @Override + public CharSequence getText() { + return textView.getText().toString(); + } + + @Override + public void setText(@Nullable CharSequence text) { + textView.setText(text); + } + + @Override + public int getSelection() { + return textView.getSelectionStart(); + } + + @Override + public void setSelection(int selection) { + if ( textView instanceof EditText ) { + ((EditText) textView).setSelection(selection); + } + } +} 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 68bcc9b8..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,425 +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.CalculatorApplication; -import org.solovyev.android.calculator.R; -import org.solovyev.android.calculator.jscl.JsclOperation; -import org.solovyev.android.prefs.BooleanPreference; -import org.solovyev.android.prefs.Preference; -import org.solovyev.android.prefs.StringPreference; -import org.solovyev.common.MutableObject; -import org.solovyev.common.msg.MessageRegistry; -import org.solovyev.common.text.EnumMapper; -import org.solovyev.common.text.NumberMapper; -import org.solovyev.common.text.StringUtils; - -import java.text.DecimalFormatSymbols; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -/** - * User: serso - * Date: 9/12/11 - * Time: 11:38 PM - */ - -public enum CalculatorEngine { - - instance; - - private static final String GROUPING_SEPARATOR_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_calc_grouping_separator"; - - private static final String MULTIPLICATION_SIGN_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_calc_multiplication_sign"; - private static final String MULTIPLICATION_SIGN_DEFAULT = "×"; - - private static final String MAX_CALCULATION_TIME_P_KEY = "calculation.max_calculation_time"; - private static final String MAX_CALCULATION_TIME_DEFAULT = "5"; - - private static final String SCIENCE_NOTATION_P_KEY = "calculation.output.science_notation"; - private static final boolean SCIENCE_NOTATION_DEFAULT = false; - - private static final String ROUND_RESULT_P_KEY = "org.solovyev.android.calculator.CalculatorModel_round_result"; - private static final boolean ROUND_RESULT_DEFAULT = true; - - private static final String RESULT_PRECISION_P_KEY = "org.solovyev.android.calculator.CalculatorModel_result_precision"; - private static final String RESULT_PRECISION_DEFAULT = "5"; - - private static final String NUMERAL_BASES_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_numeral_bases"; - private static final String NUMERAL_BASES_DEFAULT = "dec"; - - private static final String ANGLE_UNITS_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_angle_units"; - private static final String ANGLE_UNITS_DEFAULT = "deg"; - - public static class Preferences { - public static final Preference 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); - } - - @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()); - parseException.setObject(new CalculatorParseException(R.string.msg_1, CalculatorApplication.getInstance(), jsclExpression, e.getMessage())); - } catch (StackOverflowError e) { - //System.out.println(StringUtils.fromStackTrace(e.getStackTrace())); - parseException.setObject(new CalculatorParseException(R.string.msg_2, CalculatorApplication.getInstance(), jsclExpression)); - } catch (jscl.text.ParseException e) { - //System.out.println(e.getMessage()); - parseException.setObject(new CalculatorParseException(e)); - } catch (ParseInterruptedException e) { - //System.out.println(e.getMessage()); - // do nothing - we ourselves interrupt the calculations - } finally { - //Log.d(CalculatorEngine.class.getName(), "Calculation thread ended work: " + thread.getName()); - calculationThread.setObject(null); - latch.countDown(); - } - } - }).start(); - - try { - //Log.d(CalculatorEngine.class.getName(), "Main thread is waiting: " + Thread.currentThread().getName()); - latch.await(timeout, TimeUnit.SECONDS); - //Log.d(CalculatorEngine.class.getName(), "Main thread got up: " + Thread.currentThread().getName()); - - final CalculatorParseException parseExceptionObject = parseException.getObject(); - final CalculatorEvalException evalExceptionObject = evalException.getObject(); - final Object calculationResultLocal = calculationResult.getObject(); - final Thread calculationThreadLocal = calculationThread.getObject(); - - if (calculationThreadLocal != null) { - if (threadKiller != null) { - threadKiller.killThread(calculationThreadLocal); - } - //calculationThreadLocal.stop(); - } - - if (parseExceptionObject != null || evalExceptionObject != null) { - if (operation == JsclOperation.numeric && - (preparedExpression.isExistsUndefinedVar() || (evalExceptionObject != null && evalExceptionObject.getCause() instanceof NumeralBaseException))) { - return evaluate(JsclOperation.simplify, expression, mr); - } - - if (parseExceptionObject != null) { - throw parseExceptionObject; - } else { - throw evalExceptionObject; - } - } - - if (calculationResultLocal == null) { - throw new CalculatorParseException(R.string.msg_3, CalculatorApplication.getInstance(), jsclExpression); - } - - } catch (InterruptedException e) { - throw new CalculatorParseException(R.string.msg_4, CalculatorApplication.getInstance(), jsclExpression); - } - - final Generic genericResult = calculationResult.getObject(); - - return new Result(operation.getFromProcessor().process(genericResult), operation, genericResult); - } - } - - public void setPrecision(int precision) { - this.getEngine().setPrecision(precision); - } - - public void setRoundResult(boolean roundResult) { - this.getEngine().setRoundResult(roundResult); - } - - public void init(@Nullable Context context, @Nullable SharedPreferences preferences) { - synchronized (lock) { - reset(context, preferences); - } - } - - public void reset(@Nullable Context context, @Nullable SharedPreferences preferences) { - synchronized (lock) { - softReset(context, preferences); - - varsRegistry.load(context, preferences); - functionsRegistry.load(context, preferences); - operatorsRegistry.load(context, preferences); - postfixFunctionsRegistry.load(context, preferences); - } - } - - public void softReset(@Nullable Context context, @Nullable SharedPreferences preferences) { - synchronized (lock) { - if (preferences != null) { - this.setPrecision(Preferences.precision.getPreference(preferences)); - this.setRoundResult(Preferences.roundResult.getPreference(preferences)); - this.setAngleUnits(getAngleUnitsFromPrefs(preferences)); - this.setNumeralBase(getNumeralBaseFromPrefs(preferences)); - this.setMultiplicationSign(Preferences.multiplicationSign.getPreference(preferences)); - this.setScienceNotation(Preferences.scienceNotation.getPreference(preferences)); - this.setTimeout(Preferences.maxCalculationTime.getPreference(preferences)); - - final String groupingSeparator = Preferences.groupingSeparator.getPreference(preferences); - if (StringUtils.isEmpty(groupingSeparator)) { - this.getEngine().setUseGroupingSeparator(false); - } else { - this.getEngine().setUseGroupingSeparator(true); - this.getEngine().setGroupingSeparator(groupingSeparator.charAt(0)); - } - } - } - } - - - @NotNull - public NumeralBase getNumeralBaseFromPrefs(@NotNull SharedPreferences preferences) { - return Preferences.numeralBase.getPreference(preferences); - } - - @NotNull - public AngleUnit getAngleUnitsFromPrefs(@NotNull SharedPreferences preferences) { - return Preferences.angleUnit.getPreference(preferences); - } - - //for tests only - void setDecimalGroupSymbols(@NotNull DecimalFormatSymbols decimalGroupSymbols) { - synchronized (lock) { - this.getEngine().setDecimalGroupSymbols(decimalGroupSymbols); - } - } - - @NotNull - public AndroidMathRegistry getVarsRegistry() { - return varsRegistry; - } - - @NotNull - public AndroidMathRegistry getFunctionsRegistry() { - return functionsRegistry; - } - - @NotNull - public AndroidMathRegistry getOperatorsRegistry() { - return operatorsRegistry; - } - - @NotNull - public AndroidMathRegistry getPostfixFunctionsRegistry() { - return postfixFunctionsRegistry; - } - - @NotNull - public MathEngine getEngine() { - return engine; - } - - // package protected for tests - void setTimeout(int timeout) { - this.timeout = timeout; - } - - public void setAngleUnits(@NotNull AngleUnit angleUnits) { - getEngine().setAngleUnits(angleUnits); - } - - public void setScienceNotation(boolean scienceNotation) { - getEngine().setScienceNotation(scienceNotation); - } - - public void setNumeralBase(@NotNull NumeralBase numeralBase) { - getEngine().setNumeralBase(numeralBase); - } - - // for tests only - void setThreadKiller(@Nullable ThreadKiller threadKiller) { - this.threadKiller = threadKiller; - } - - private static interface ThreadKiller { - void killThread(@NotNull Thread thread); - } - - private static class AndroidThreadKiller implements ThreadKiller { - @Override - public void killThread(@NotNull Thread thread) { - thread.setPriority(Thread.MIN_PRIORITY); - thread.interrupt(); - } - } - - public static class ThreadKillerImpl implements ThreadKiller { - @Override - public void killThread(@NotNull Thread thread) { - thread.setPriority(Thread.MIN_PRIORITY); - thread.stop(); - } - } -} +/* + * 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/main/java/org/solovyev/android/calculator/plot/CalculatorPlotActivity.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/plot/CalculatorPlotActivity.java index 33411a3c..c38714bc 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/plot/CalculatorPlotActivity.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/plot/CalculatorPlotActivity.java @@ -1,360 +1,360 @@ -/* - * 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.plot; - -import android.app.Activity; -import android.graphics.Color; -import android.os.Bundle; -import android.os.Handler; -import android.util.Log; -import android.view.View; -import android.view.ViewGroup; -import android.view.Window; -import android.widget.Toast; -import jscl.math.Expression; -import jscl.math.Generic; -import jscl.math.function.Constant; -import jscl.text.ParseException; -import org.achartengine.ChartFactory; -import org.achartengine.GraphicalView; -import org.achartengine.chart.CubicLineChart; -import org.achartengine.chart.PointStyle; -import org.achartengine.chart.XYChart; -import org.achartengine.model.XYMultipleSeriesDataset; -import org.achartengine.model.XYSeries; -import org.achartengine.renderer.BasicStroke; -import org.achartengine.renderer.XYMultipleSeriesRenderer; -import org.achartengine.renderer.XYSeriesRenderer; -import org.achartengine.tools.PanListener; -import org.achartengine.tools.ZoomEvent; -import org.achartengine.tools.ZoomListener; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.solovyev.android.calculator.R; -import org.solovyev.android.calculator.model.CalculatorParseException; -import org.solovyev.android.calculator.model.PreparedExpression; -import org.solovyev.android.calculator.model.ToJsclTextProcessor; -import org.solovyev.common.MutableObject; - -import java.io.Serializable; - -/** - * User: serso - * Date: 12/1/11 - * Time: 12:40 AM - */ -public class CalculatorPlotActivity extends Activity { - - private static final String TAG = CalculatorPlotActivity.class.getSimpleName(); - - private static final int DEFAULT_NUMBER_OF_STEPS = 100; - - private static final int DEFAULT_MIN_NUMBER = -10; - - private static final int DEFAULT_MAX_NUMBER = 10; - - public static final String INPUT = "org.solovyev.android.calculator.CalculatorPlotActivity_input"; - - public static final long EVAL_DELAY_MILLIS = 200; - - private XYChart chart; - - /** - * The encapsulated graphical view. - */ - private GraphicalView graphicalView; - - @NotNull - private Generic expression; - - @NotNull - private Constant variable; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - Bundle extras = getIntent().getExtras(); - - final Input input = (Input) extras.getSerializable(INPUT); - - try { - final PreparedExpression preparedExpression = ToJsclTextProcessor.getInstance().process(input.getExpression()); - this.expression = Expression.valueOf(preparedExpression.getExpression()); - this.variable = new Constant(input.getVariableName()); - - String title = extras.getString(ChartFactory.TITLE); - if (title == null) { - requestWindowFeature(Window.FEATURE_NO_TITLE); - } else if (title.length() > 0) { - setTitle(title); - } - - setContentView(R.layout.calc_plot_view); - - final Object lastNonConfigurationInstance = getLastNonConfigurationInstance(); - setGraphicalView(lastNonConfigurationInstance instanceof PlotBoundaries ? (PlotBoundaries)lastNonConfigurationInstance : null); - - } catch (ParseException e) { - Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_LONG).show(); - finish(); - } catch (ArithmeticException e) { - Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_LONG).show(); - finish(); - } catch (CalculatorParseException e) { - Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_LONG).show(); - finish(); - } - } - - private void setGraphicalView(@Nullable PlotBoundaries plotBoundaries) { - double minValue = plotBoundaries == null ? DEFAULT_MIN_NUMBER : plotBoundaries.xMin; - double maxValue = plotBoundaries == null ? DEFAULT_MAX_NUMBER : plotBoundaries.xMax; - - final ViewGroup graphContainer = (ViewGroup) findViewById(R.id.plot_view_container); - - if (graphicalView != null) { - graphContainer.removeView(graphicalView); - } - - chart = prepareChart(minValue, maxValue, expression, variable); - - // reverting boundaries (as in prepareChart() we add some cached values ) - double minX = Double.MAX_VALUE; - double minY = Double.MAX_VALUE; - - double maxX = Double.MIN_VALUE; - double maxY = Double.MIN_VALUE; - - for (XYSeries series : chart.getDataset().getSeries()) { - minX = Math.min(minX, series.getMinX()); - minY = Math.min(minY, series.getMinY()); - maxX = Math.max(maxX, series.getMaxX()); - maxY = Math.max(maxY, series.getMaxY()); - } - - Log.d(CalculatorPlotActivity.class.getName(), "min x: " + minX + ", min y: " + minY + ", max x: " + maxX + ", max y: " + maxY); - Log.d(CalculatorPlotActivity.class.getName(), "Plot boundaries are " + plotBoundaries); - - - if (plotBoundaries == null) { - chart.getRenderer().setXAxisMin(Math.max(minX, minValue)); - chart.getRenderer().setYAxisMin(Math.max(minY, minValue)); - chart.getRenderer().setXAxisMax(Math.min(maxX, maxValue)); - chart.getRenderer().setYAxisMax(Math.min(maxY, maxValue)); - } else { - chart.getRenderer().setXAxisMin(plotBoundaries.xMin); - chart.getRenderer().setYAxisMin(plotBoundaries.yMin); - chart.getRenderer().setXAxisMax(plotBoundaries.xMax); - chart.getRenderer().setYAxisMax(plotBoundaries.yMax); - } - - graphicalView = new GraphicalView(this, chart); - - graphicalView.addZoomListener(new ZoomListener() { - @Override - public void zoomApplied(ZoomEvent e) { - updateDataSets(chart); - } - - @Override - public void zoomReset() { - updateDataSets(chart); - } - }, true, true); - - graphicalView.addPanListener(new PanListener() { - @Override - public void panApplied() { - Log.d(TAG, "org.achartengine.tools.PanListener.panApplied"); - updateDataSets(chart); - } - - }); - graphContainer.addView(graphicalView); - - updateDataSets(chart, 50); - } - - - private void updateDataSets(@NotNull final XYChart chart) { - updateDataSets(chart, EVAL_DELAY_MILLIS); - } - - private void updateDataSets(@NotNull final XYChart chart, long millisToWait) { - 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) { - - Log.d(TAG, "org.solovyev.android.calculator.plot.CalculatorPlotActivity.updateDataSets"); - - final XYMultipleSeriesRenderer dr = chart.getRenderer(); - - //Log.d(CalculatorPlotActivity.class.getName(), "x = [" + dr.getXAxisMin() + ", " + dr.getXAxisMax() + "], y = [" + dr.getYAxisMin() + ", " + dr.getYAxisMax() + "]"); - - final MyXYSeries realSeries = (MyXYSeries)chart.getDataset().getSeriesAt(0); - - final MyXYSeries imagSeries; - if (chart.getDataset().getSeriesCount() > 1) { - imagSeries = (MyXYSeries)chart.getDataset().getSeriesAt(1); - } else { - imagSeries = new MyXYSeries(getImagFunctionName(CalculatorPlotActivity.this.variable), DEFAULT_NUMBER_OF_STEPS * 2); - } - - try { - if (PlotUtils.addXY(dr.getXAxisMin(), dr.getXAxisMax(), expression, variable, realSeries, imagSeries, true, DEFAULT_NUMBER_OF_STEPS)) { - if (chart.getDataset().getSeriesCount() <= 1) { - chart.getDataset().addSeries(imagSeries); - chart.getRenderer().addSeriesRenderer(createImagRenderer()); - } - } - } catch (ArithmeticException e) { - // todo serso: translate - Toast.makeText(CalculatorPlotActivity.this, "Arithmetic error: " + e.getLocalizedMessage(), Toast.LENGTH_LONG).show(); - CalculatorPlotActivity.this.finish(); - } - - if (pendingOperation.getObject() == this) { - graphicalView.repaint(); - } - } - } - } - }); - - - new Handler().postDelayed(pendingOperation.getObject(), millisToWait); - } - - @NotNull - private static String getImagFunctionName(@NotNull Constant variable) { - return "g(" + variable.getName() +")" + " = " + "Im(ƒ(" + variable.getName() +"))"; - } - - @NotNull - private static String getRealFunctionName(@NotNull Generic expression, @NotNull Constant variable) { - return "ƒ(" + variable.getName() +")" + " = " + expression.toString(); - } - - @NotNull - private final static MutableObject pendingOperation = new MutableObject(); - - private static XYChart prepareChart(final double minValue, final double maxValue, @NotNull final Generic expression, @NotNull final Constant variable) { - final MyXYSeries realSeries = new MyXYSeries(getRealFunctionName(expression, variable), DEFAULT_NUMBER_OF_STEPS * 2); - final MyXYSeries imagSeries = new MyXYSeries(getImagFunctionName(variable), DEFAULT_NUMBER_OF_STEPS * 2); - - boolean imagExists = PlotUtils.addXY(minValue, maxValue, expression, variable, realSeries, imagSeries, false, DEFAULT_NUMBER_OF_STEPS); - - final XYMultipleSeriesDataset data = new XYMultipleSeriesDataset(); - data.addSeries(realSeries); - if (imagExists) { - data.addSeries(imagSeries); - } - - final XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer(); - renderer.setShowGrid(true); - renderer.setXTitle(variable.getName()); - renderer.setYTitle("f(" + variable.getName() + ")"); - renderer.setChartTitleTextSize(20); - - renderer.setZoomEnabled(true); - renderer.setZoomButtonsVisible(true); - - renderer.addSeriesRenderer(createCommonRenderer()); - if (imagExists) { - renderer.addSeriesRenderer(createImagRenderer()); - } - - return new CubicLineChart(data, renderer, 0.1f); - } - - private static XYSeriesRenderer createImagRenderer() { - final XYSeriesRenderer imagRenderer = createCommonRenderer(); - imagRenderer.setStroke(BasicStroke.DASHED); - imagRenderer.setColor(Color.LTGRAY); - return imagRenderer; - } - - @Override - public Object onRetainNonConfigurationInstance() { - return new PlotBoundaries(chart.getRenderer()); - } - - private static final class PlotBoundaries implements Serializable { - - private final double xMin; - private final double xMax; - private final double yMin; - private final double yMax; - - public PlotBoundaries(@NotNull XYMultipleSeriesRenderer renderer) { - this.xMin = renderer.getXAxisMin(); - this.yMin = renderer.getYAxisMin(); - this.xMax = renderer.getXAxisMax(); - this.yMax = renderer.getYAxisMax(); - } - - @Override - public String toString() { - return "PlotBoundaries{" + - "yMax=" + yMax + - ", yMin=" + yMin + - ", xMax=" + xMax + - ", xMin=" + xMin + - '}'; - } - } - - - @NotNull - private static XYSeriesRenderer createCommonRenderer() { - final XYSeriesRenderer renderer = new XYSeriesRenderer(); - renderer.setFillPoints(true); - renderer.setPointStyle(PointStyle.POINT); - renderer.setLineWidth(3); - renderer.setColor(Color.WHITE); - renderer.setStroke(BasicStroke.SOLID); - return renderer; - } - - public void zoomInClickHandler(@NotNull View v) { - this.graphicalView.zoomIn(); - } - - public void zoomOutClickHandler(@NotNull View v) { - this.graphicalView.zoomOut(); - } - - - - public static class Input implements Serializable { - - @NotNull - private String expression; - - @NotNull - private String variableName; - - public Input(@NotNull String expression, @NotNull String variableName) { - this.expression = expression; - this.variableName = variableName; - } - - @NotNull - public String getExpression() { - return expression; - } - - @NotNull - public String getVariableName() { - return variableName; - } - } -} +/* + * 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.plot; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.os.Handler; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.Toast; +import jscl.math.Expression; +import jscl.math.Generic; +import jscl.math.function.Constant; +import jscl.text.ParseException; +import org.achartengine.ChartFactory; +import org.achartengine.GraphicalView; +import org.achartengine.chart.CubicLineChart; +import org.achartengine.chart.PointStyle; +import org.achartengine.chart.XYChart; +import org.achartengine.model.XYMultipleSeriesDataset; +import org.achartengine.model.XYSeries; +import org.achartengine.renderer.BasicStroke; +import org.achartengine.renderer.XYMultipleSeriesRenderer; +import org.achartengine.renderer.XYSeriesRenderer; +import org.achartengine.tools.PanListener; +import org.achartengine.tools.ZoomEvent; +import org.achartengine.tools.ZoomListener; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.calculator.R; +import org.solovyev.android.calculator.CalculatorParseException; +import org.solovyev.android.calculator.PreparedExpression; +import org.solovyev.android.calculator.ToJsclTextProcessor; +import org.solovyev.common.MutableObject; + +import java.io.Serializable; + +/** + * User: serso + * Date: 12/1/11 + * Time: 12:40 AM + */ +public class CalculatorPlotActivity extends Activity { + + private static final String TAG = CalculatorPlotActivity.class.getSimpleName(); + + private static final int DEFAULT_NUMBER_OF_STEPS = 100; + + private static final int DEFAULT_MIN_NUMBER = -10; + + private static final int DEFAULT_MAX_NUMBER = 10; + + public static final String INPUT = "org.solovyev.android.calculator.CalculatorPlotActivity_input"; + + public static final long EVAL_DELAY_MILLIS = 200; + + private XYChart chart; + + /** + * The encapsulated graphical view. + */ + private GraphicalView graphicalView; + + @NotNull + private Generic expression; + + @NotNull + private Constant variable; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Bundle extras = getIntent().getExtras(); + + final Input input = (Input) extras.getSerializable(INPUT); + + try { + final PreparedExpression preparedExpression = ToJsclTextProcessor.getInstance().process(input.getExpression()); + this.expression = Expression.valueOf(preparedExpression.getExpression()); + this.variable = new Constant(input.getVariableName()); + + String title = extras.getString(ChartFactory.TITLE); + if (title == null) { + requestWindowFeature(Window.FEATURE_NO_TITLE); + } else if (title.length() > 0) { + setTitle(title); + } + + setContentView(R.layout.calc_plot_view); + + final Object lastNonConfigurationInstance = getLastNonConfigurationInstance(); + setGraphicalView(lastNonConfigurationInstance instanceof PlotBoundaries ? (PlotBoundaries)lastNonConfigurationInstance : null); + + } catch (ParseException e) { + Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_LONG).show(); + finish(); + } catch (ArithmeticException e) { + Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_LONG).show(); + finish(); + } catch (CalculatorParseException e) { + Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_LONG).show(); + finish(); + } + } + + private void setGraphicalView(@Nullable PlotBoundaries plotBoundaries) { + double minValue = plotBoundaries == null ? DEFAULT_MIN_NUMBER : plotBoundaries.xMin; + double maxValue = plotBoundaries == null ? DEFAULT_MAX_NUMBER : plotBoundaries.xMax; + + final ViewGroup graphContainer = (ViewGroup) findViewById(R.id.plot_view_container); + + if (graphicalView != null) { + graphContainer.removeView(graphicalView); + } + + chart = prepareChart(minValue, maxValue, expression, variable); + + // reverting boundaries (as in prepareChart() we add some cached values ) + double minX = Double.MAX_VALUE; + double minY = Double.MAX_VALUE; + + double maxX = Double.MIN_VALUE; + double maxY = Double.MIN_VALUE; + + for (XYSeries series : chart.getDataset().getSeries()) { + minX = Math.min(minX, series.getMinX()); + minY = Math.min(minY, series.getMinY()); + maxX = Math.max(maxX, series.getMaxX()); + maxY = Math.max(maxY, series.getMaxY()); + } + + Log.d(CalculatorPlotActivity.class.getName(), "min x: " + minX + ", min y: " + minY + ", max x: " + maxX + ", max y: " + maxY); + Log.d(CalculatorPlotActivity.class.getName(), "Plot boundaries are " + plotBoundaries); + + + if (plotBoundaries == null) { + chart.getRenderer().setXAxisMin(Math.max(minX, minValue)); + chart.getRenderer().setYAxisMin(Math.max(minY, minValue)); + chart.getRenderer().setXAxisMax(Math.min(maxX, maxValue)); + chart.getRenderer().setYAxisMax(Math.min(maxY, maxValue)); + } else { + chart.getRenderer().setXAxisMin(plotBoundaries.xMin); + chart.getRenderer().setYAxisMin(plotBoundaries.yMin); + chart.getRenderer().setXAxisMax(plotBoundaries.xMax); + chart.getRenderer().setYAxisMax(plotBoundaries.yMax); + } + + graphicalView = new GraphicalView(this, chart); + + graphicalView.addZoomListener(new ZoomListener() { + @Override + public void zoomApplied(ZoomEvent e) { + updateDataSets(chart); + } + + @Override + public void zoomReset() { + updateDataSets(chart); + } + }, true, true); + + graphicalView.addPanListener(new PanListener() { + @Override + public void panApplied() { + Log.d(TAG, "org.achartengine.tools.PanListener.panApplied"); + updateDataSets(chart); + } + + }); + graphContainer.addView(graphicalView); + + updateDataSets(chart, 50); + } + + + private void updateDataSets(@NotNull final XYChart chart) { + updateDataSets(chart, EVAL_DELAY_MILLIS); + } + + private void updateDataSets(@NotNull final XYChart chart, long millisToWait) { + 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) { + + Log.d(TAG, "org.solovyev.android.calculator.plot.CalculatorPlotActivity.updateDataSets"); + + final XYMultipleSeriesRenderer dr = chart.getRenderer(); + + //Log.d(CalculatorPlotActivity.class.getName(), "x = [" + dr.getXAxisMin() + ", " + dr.getXAxisMax() + "], y = [" + dr.getYAxisMin() + ", " + dr.getYAxisMax() + "]"); + + final MyXYSeries realSeries = (MyXYSeries)chart.getDataset().getSeriesAt(0); + + final MyXYSeries imagSeries; + if (chart.getDataset().getSeriesCount() > 1) { + imagSeries = (MyXYSeries)chart.getDataset().getSeriesAt(1); + } else { + imagSeries = new MyXYSeries(getImagFunctionName(CalculatorPlotActivity.this.variable), DEFAULT_NUMBER_OF_STEPS * 2); + } + + try { + if (PlotUtils.addXY(dr.getXAxisMin(), dr.getXAxisMax(), expression, variable, realSeries, imagSeries, true, DEFAULT_NUMBER_OF_STEPS)) { + if (chart.getDataset().getSeriesCount() <= 1) { + chart.getDataset().addSeries(imagSeries); + chart.getRenderer().addSeriesRenderer(createImagRenderer()); + } + } + } catch (ArithmeticException e) { + // todo serso: translate + Toast.makeText(CalculatorPlotActivity.this, "Arithmetic error: " + e.getLocalizedMessage(), Toast.LENGTH_LONG).show(); + CalculatorPlotActivity.this.finish(); + } + + if (pendingOperation.getObject() == this) { + graphicalView.repaint(); + } + } + } + } + }); + + + new Handler().postDelayed(pendingOperation.getObject(), millisToWait); + } + + @NotNull + private static String getImagFunctionName(@NotNull Constant variable) { + return "g(" + variable.getName() +")" + " = " + "Im(ƒ(" + variable.getName() +"))"; + } + + @NotNull + private static String getRealFunctionName(@NotNull Generic expression, @NotNull Constant variable) { + return "ƒ(" + variable.getName() +")" + " = " + expression.toString(); + } + + @NotNull + private final static MutableObject pendingOperation = new MutableObject(); + + private static XYChart prepareChart(final double minValue, final double maxValue, @NotNull final Generic expression, @NotNull final Constant variable) { + final MyXYSeries realSeries = new MyXYSeries(getRealFunctionName(expression, variable), DEFAULT_NUMBER_OF_STEPS * 2); + final MyXYSeries imagSeries = new MyXYSeries(getImagFunctionName(variable), DEFAULT_NUMBER_OF_STEPS * 2); + + boolean imagExists = PlotUtils.addXY(minValue, maxValue, expression, variable, realSeries, imagSeries, false, DEFAULT_NUMBER_OF_STEPS); + + final XYMultipleSeriesDataset data = new XYMultipleSeriesDataset(); + data.addSeries(realSeries); + if (imagExists) { + data.addSeries(imagSeries); + } + + final XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer(); + renderer.setShowGrid(true); + renderer.setXTitle(variable.getName()); + renderer.setYTitle("f(" + variable.getName() + ")"); + renderer.setChartTitleTextSize(20); + + renderer.setZoomEnabled(true); + renderer.setZoomButtonsVisible(true); + + renderer.addSeriesRenderer(createCommonRenderer()); + if (imagExists) { + renderer.addSeriesRenderer(createImagRenderer()); + } + + return new CubicLineChart(data, renderer, 0.1f); + } + + private static XYSeriesRenderer createImagRenderer() { + final XYSeriesRenderer imagRenderer = createCommonRenderer(); + imagRenderer.setStroke(BasicStroke.DASHED); + imagRenderer.setColor(Color.LTGRAY); + return imagRenderer; + } + + @Override + public Object onRetainNonConfigurationInstance() { + return new PlotBoundaries(chart.getRenderer()); + } + + private static final class PlotBoundaries implements Serializable { + + private final double xMin; + private final double xMax; + private final double yMin; + private final double yMax; + + public PlotBoundaries(@NotNull XYMultipleSeriesRenderer renderer) { + this.xMin = renderer.getXAxisMin(); + this.yMin = renderer.getYAxisMin(); + this.xMax = renderer.getXAxisMax(); + this.yMax = renderer.getYAxisMax(); + } + + @Override + public String toString() { + return "PlotBoundaries{" + + "yMax=" + yMax + + ", yMin=" + yMin + + ", xMax=" + xMax + + ", xMin=" + xMin + + '}'; + } + } + + + @NotNull + private static XYSeriesRenderer createCommonRenderer() { + final XYSeriesRenderer renderer = new XYSeriesRenderer(); + renderer.setFillPoints(true); + renderer.setPointStyle(PointStyle.POINT); + renderer.setLineWidth(3); + renderer.setColor(Color.WHITE); + renderer.setStroke(BasicStroke.SOLID); + return renderer; + } + + public void zoomInClickHandler(@NotNull View v) { + this.graphicalView.zoomIn(); + } + + public void zoomOutClickHandler(@NotNull View v) { + this.graphicalView.zoomOut(); + } + + + + public static class Input implements Serializable { + + @NotNull + private String expression; + + @NotNull + private String variableName; + + public Input(@NotNull String expression, @NotNull String variableName) { + this.expression = expression; + this.variableName = variableName; + } + + @NotNull + public String getExpression() { + return expression; + } + + @NotNull + public String getVariableName() { + return variableName; + } + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/view/NumeralBaseConverterDialog.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/view/NumeralBaseConverterDialog.java index 2dfa459f..78f3a0bd 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/view/NumeralBaseConverterDialog.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/view/NumeralBaseConverterDialog.java @@ -1,99 +1,99 @@ -package org.solovyev.android.calculator.view; - -import android.app.AlertDialog; -import android.content.Context; -import android.view.View; -import android.view.WindowManager; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.solovyev.android.Unit; -import org.solovyev.android.UnitImpl; -import org.solovyev.android.calculator.AndroidNumeralBase; -import org.solovyev.android.calculator.CalculatorModel; -import org.solovyev.android.calculator.R; -import org.solovyev.android.calculator.model.CalculatorEngine; -import org.solovyev.android.calculator.model.CalculatorParseException; -import org.solovyev.android.calculator.model.ToJsclTextProcessor; -import org.solovyev.common.MutableObject; -import org.solovyev.common.text.StringUtils; - -import java.util.Arrays; - -/** - * User: serso - * Date: 4/22/12 - * Time: 12:20 AM - */ -public class NumeralBaseConverterDialog { - - @Nullable - private String initialFromValue; - - public NumeralBaseConverterDialog(String initialFromValue) { - this.initialFromValue = initialFromValue; - } - - public void show(@NotNull Context context) { - final UnitConverterViewBuilder b = new UnitConverterViewBuilder(); - b.setFromUnitTypes(Arrays.asList(AndroidNumeralBase.values())); - b.setToUnitTypes(Arrays.asList(AndroidNumeralBase.values())); - - if (!StringUtils.isEmpty(initialFromValue)) { - String value = initialFromValue; - try { - value = ToJsclTextProcessor.getInstance().process(value).getExpression(); - b.setFromValue(UnitImpl.newInstance(value, AndroidNumeralBase.valueOf(CalculatorEngine.instance.getEngine().getNumeralBase()))); - } catch (CalculatorParseException e) { - b.setFromValue(UnitImpl.newInstance(value, AndroidNumeralBase.valueOf(CalculatorEngine.instance.getEngine().getNumeralBase()))); - } - } else { - b.setFromValue(UnitImpl.newInstance("", AndroidNumeralBase.valueOf(CalculatorEngine.instance.getEngine().getNumeralBase()))); - } - - b.setConverter(AndroidNumeralBase.getConverter()); - - final MutableObject alertDialogHolder = new MutableObject(); - b.setOkButtonOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - final AlertDialog alertDialog = alertDialogHolder.getObject(); - if (alertDialog != null) { - alertDialog.dismiss(); - } - } - }); - - b.setCustomButtonData(new UnitConverterViewBuilder.CustomButtonData(context.getString(R.string.c_use_short), new UnitConverterViewBuilder.CustomButtonOnClickListener() { - @Override - public void onClick(@NotNull Unit fromUnits, @NotNull Unit toUnits) { - String toUnitsValue = toUnits.getValue(); - - if (!toUnits.getUnitType().equals(AndroidNumeralBase.valueOf(CalculatorEngine.instance.getEngine().getNumeralBase()))) { - toUnitsValue = ((AndroidNumeralBase) toUnits.getUnitType()).getNumeralBase().getJsclPrefix() + toUnitsValue; - } - - CalculatorModel.instance.processDigitButtonAction(toUnitsValue, false); - final AlertDialog alertDialog = alertDialogHolder.getObject(); - if (alertDialog != null) { - alertDialog.dismiss(); - } - } - })); - - final AlertDialog.Builder alertBuilder = new AlertDialog.Builder(context); - alertBuilder.setView(b.build(context)); - alertBuilder.setTitle(R.string.c_conversion_tool); - - final AlertDialog alertDialog = alertBuilder.create(); - - final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(); - lp.copyFrom(alertDialog.getWindow().getAttributes()); - - lp.width = WindowManager.LayoutParams.FILL_PARENT; - lp.height = WindowManager.LayoutParams.WRAP_CONTENT; - - alertDialogHolder.setObject(alertDialog); - alertDialog.show(); - alertDialog.getWindow().setAttributes(lp); - } -} +package org.solovyev.android.calculator.view; + +import android.app.AlertDialog; +import android.content.Context; +import android.view.View; +import android.view.WindowManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.math.units.Unit; +import org.solovyev.math.units.UnitImpl; +import org.solovyev.android.calculator.AndroidNumeralBase; +import org.solovyev.android.calculator.CalculatorModel; +import org.solovyev.android.calculator.R; +import org.solovyev.android.calculator.model.CalculatorEngine; +import org.solovyev.android.calculator.CalculatorParseException; +import org.solovyev.android.calculator.ToJsclTextProcessor; +import org.solovyev.common.MutableObject; +import org.solovyev.common.text.StringUtils; + +import java.util.Arrays; + +/** + * User: serso + * Date: 4/22/12 + * Time: 12:20 AM + */ +public class NumeralBaseConverterDialog { + + @Nullable + private String initialFromValue; + + public NumeralBaseConverterDialog(String initialFromValue) { + this.initialFromValue = initialFromValue; + } + + public void show(@NotNull Context context) { + final UnitConverterViewBuilder b = new UnitConverterViewBuilder(); + b.setFromUnitTypes(Arrays.asList(AndroidNumeralBase.values())); + b.setToUnitTypes(Arrays.asList(AndroidNumeralBase.values())); + + if (!StringUtils.isEmpty(initialFromValue)) { + String value = initialFromValue; + try { + value = ToJsclTextProcessor.getInstance().process(value).getExpression(); + b.setFromValue(UnitImpl.newInstance(value, AndroidNumeralBase.valueOf(CalculatorEngine.instance.getEngine().getNumeralBase()))); + } catch (CalculatorParseException e) { + b.setFromValue(UnitImpl.newInstance(value, AndroidNumeralBase.valueOf(CalculatorEngine.instance.getEngine().getNumeralBase()))); + } + } else { + b.setFromValue(UnitImpl.newInstance("", AndroidNumeralBase.valueOf(CalculatorEngine.instance.getEngine().getNumeralBase()))); + } + + b.setConverter(AndroidNumeralBase.getConverter()); + + final MutableObject alertDialogHolder = new MutableObject(); + b.setOkButtonOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + final AlertDialog alertDialog = alertDialogHolder.getObject(); + if (alertDialog != null) { + alertDialog.dismiss(); + } + } + }); + + b.setCustomButtonData(new UnitConverterViewBuilder.CustomButtonData(context.getString(R.string.c_use_short), new UnitConverterViewBuilder.CustomButtonOnClickListener() { + @Override + public void onClick(@NotNull Unit fromUnits, @NotNull Unit toUnits) { + String toUnitsValue = toUnits.getValue(); + + if (!toUnits.getUnitType().equals(AndroidNumeralBase.valueOf(CalculatorEngine.instance.getEngine().getNumeralBase()))) { + toUnitsValue = ((AndroidNumeralBase) toUnits.getUnitType()).getNumeralBase().getJsclPrefix() + toUnitsValue; + } + + CalculatorModel.instance.processDigitButtonAction(toUnitsValue, false); + final AlertDialog alertDialog = alertDialogHolder.getObject(); + if (alertDialog != null) { + alertDialog.dismiss(); + } + } + })); + + final AlertDialog.Builder alertBuilder = new AlertDialog.Builder(context); + alertBuilder.setView(b.build(context)); + alertBuilder.setTitle(R.string.c_conversion_tool); + + final AlertDialog alertDialog = alertBuilder.create(); + + final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(); + lp.copyFrom(alertDialog.getWindow().getAttributes()); + + lp.width = WindowManager.LayoutParams.FILL_PARENT; + lp.height = WindowManager.LayoutParams.WRAP_CONTENT; + + alertDialogHolder.setObject(alertDialog); + alertDialog.show(); + alertDialog.getWindow().setAttributes(lp); + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/view/TextHighlighter.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/view/TextHighlighter.java index 0c658a97..0d616fcc 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/view/TextHighlighter.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/view/TextHighlighter.java @@ -1,238 +1,243 @@ -/* - * 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.view; - -import jscl.MathContext; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.solovyev.android.calculator.math.MathType; -import org.solovyev.android.calculator.model.*; -import org.solovyev.common.MutableObject; - -import java.util.HashMap; -import java.util.Map; - -/** - * User: serso - * Date: 10/12/11 - * Time: 9:47 PM - */ -public class TextHighlighter implements TextProcessor { - - private static final Map nbFontAttributes = new HashMap(); - - static { - nbFontAttributes.put("color", "#008000"); - } - - @NotNull - public final MathContext mathContext; - - public static class Result implements CharSequence { - - @NotNull - private final String string; - - private final int offset; - - public Result(@NotNull String string, int offset) { - this.string = string; - this.offset = offset; - } - - @Override - public int length() { - return string.length(); - } - - @Override - public char charAt(int i) { - return string.charAt(i); - } - - @Override - public CharSequence subSequence(int i, int i1) { - return string.subSequence(i, i1); - } - - @Override - public String toString() { - return string; - } - - public int getOffset() { - return offset; - } - } - - private final int color; - private final int colorRed; - private final int colorGreen; - private final int colorBlue; - private final boolean formatNumber; - - public TextHighlighter(int baseColor, boolean formatNumber, @NotNull MathContext mathContext) { - this.color = baseColor; - this.formatNumber = formatNumber; - this.mathContext = mathContext; - //this.colorRed = Color.red(baseColor); - this.colorRed = (baseColor >> 16) & 0xFF; - //this.colorGreen = Color.green(baseColor); - this.colorGreen = (color >> 8) & 0xFF; - //this.colorBlue = Color.blue(baseColor); - this.colorBlue = color & 0xFF; - } - - @NotNull - @Override - public Result process(@NotNull String text) throws CalculatorParseException { - final String result; - - int maxNumberOfOpenGroupSymbols = 0; - int numberOfOpenGroupSymbols = 0; - - final StringBuilder text1 = new StringBuilder(); - - int resultOffset = 0; - - final AbstractNumberBuilder numberBuilder; - if (!formatNumber) { - numberBuilder = new LiteNumberBuilder(CalculatorEngine.instance.getEngine()); - } else { - numberBuilder = new NumberBuilder(CalculatorEngine.instance.getEngine()); - } - for (int i = 0; i < text.length(); i++) { - MathType.Result mathType = MathType.getType(text, i, numberBuilder.isHexMode()); - - if (numberBuilder instanceof NumberBuilder) { - final MutableObject numberOffset = new MutableObject(0); - ((NumberBuilder) numberBuilder).process(text1, mathType, numberOffset); - resultOffset += numberOffset.getObject(); - } else { - ((LiteNumberBuilder) numberBuilder).process(mathType); - } - - final String match = mathType.getMatch(); - switch (mathType.getMathType()) { - case open_group_symbol: - numberOfOpenGroupSymbols++; - maxNumberOfOpenGroupSymbols = Math.max(maxNumberOfOpenGroupSymbols, numberOfOpenGroupSymbols); - text1.append(text.charAt(i)); - break; - case close_group_symbol: - numberOfOpenGroupSymbols--; - text1.append(text.charAt(i)); - break; - case operator: - text1.append(match); - if (match.length() > 1) { - i += match.length() - 1; - } - break; - case function: - i = processHighlightedText(text1, i, match, "i", null); - break; - case constant: - i = processHighlightedText(text1, i, match, "b", null); - break; - case numeral_base: - i = processHighlightedText(text1, i, match, "b", null); - break; - default: - if (mathType.getMathType() == MathType.text || match.length() <= 1) { - text1.append(text.charAt(i)); - } else { - text1.append(match); - i += match.length() - 1; - } - } - } - - if (numberBuilder instanceof NumberBuilder) { - final MutableObject numberOffset = new MutableObject(0); - ((NumberBuilder) numberBuilder).processNumber(text1, numberOffset); - resultOffset += numberOffset.getObject(); - } - - if (maxNumberOfOpenGroupSymbols > 0) { - - final StringBuilder text2 = new StringBuilder(); - - String s = text1.toString(); - int i = processBracketGroup(text2, s, 0, 0, maxNumberOfOpenGroupSymbols); - for (; i < s.length(); i++) { - text2.append(s.charAt(i)); - } - - //Log.d(CalculatorEditor.class.getName(), text2.toString()); - - result = text2.toString(); - } else { - result = text1.toString(); - } - - return new Result(result, resultOffset); - } - - private int processHighlightedText(@NotNull StringBuilder result, int i, @NotNull String match, @NotNull String tag, @Nullable Map tagAttributes) { - result.append("<").append(tag); - - if (tagAttributes != null) { - for (Map.Entry entry : tagAttributes.entrySet()) { - // attr1="attr1_value" attr2="attr2_value" - result.append(" ").append(entry.getKey()).append("=\"").append(entry.getValue()).append("\""); - } - } - - result.append(">").append(match).append(""); - if (match.length() > 1) { - return i + match.length() - 1; - } else { - return i; - } - } - - private int processBracketGroup(@NotNull StringBuilder result, @NotNull String s, int i, int numberOfOpenings, int maxNumberOfGroups) { - - result.append(""); - - for (; i < s.length(); i++) { - char ch = s.charAt(i); - - if (MathType.open_group_symbol.getTokens().contains(String.valueOf(ch))) { - result.append(ch); - result.append(""); - i = processBracketGroup(result, s, i + 1, numberOfOpenings + 1, maxNumberOfGroups); - result.append(""); - if (i < s.length() && MathType.close_group_symbol.getTokens().contains(String.valueOf(s.charAt(i)))) { - result.append(s.charAt(i)); - } - } else if (MathType.close_group_symbol.getTokens().contains(String.valueOf(ch))) { - break; - } else { - result.append(ch); - } - } - - result.append(""); - - - return i; - } - - private String getColor(int totalNumberOfOpenings, int numberOfOpenings) { - double c = 0.8; - - int offset = ((int) (255 * c)) * numberOfOpenings / (totalNumberOfOpenings + 1); - - // for tests: - // innt result = Color.rgb(BASE_COLOUR_RED_COMPONENT - offset, BASE_COLOUR_GREEN_COMPONENT - offset, BASE_COLOUR_BLUE_COMPONENT - offset); - int result = (0xFF << 24) | ((colorRed - offset) << 16) | ((colorGreen - offset) << 8) | (colorBlue - offset); - - return "#" + Integer.toHexString(result).substring(2); - } -} +/* + * 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.view; + +import jscl.MathContext; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.calculator.AbstractNumberBuilder; +import org.solovyev.android.calculator.CalculatorParseException; +import org.solovyev.android.calculator.LiteNumberBuilder; +import org.solovyev.android.calculator.NumberBuilder; +import org.solovyev.android.calculator.math.MathType; +import org.solovyev.android.calculator.model.*; +import org.solovyev.android.calculator.text.TextProcessor; +import org.solovyev.common.MutableObject; + +import java.util.HashMap; +import java.util.Map; + +/** + * User: serso + * Date: 10/12/11 + * Time: 9:47 PM + */ +public class TextHighlighter implements TextProcessor { + + private static final Map nbFontAttributes = new HashMap(); + + static { + nbFontAttributes.put("color", "#008000"); + } + + @NotNull + public final MathContext mathContext; + + public static class Result implements CharSequence { + + @NotNull + private final String string; + + private final int offset; + + public Result(@NotNull String string, int offset) { + this.string = string; + this.offset = offset; + } + + @Override + public int length() { + return string.length(); + } + + @Override + public char charAt(int i) { + return string.charAt(i); + } + + @Override + public CharSequence subSequence(int i, int i1) { + return string.subSequence(i, i1); + } + + @Override + public String toString() { + return string; + } + + public int getOffset() { + return offset; + } + } + + private final int color; + private final int colorRed; + private final int colorGreen; + private final int colorBlue; + private final boolean formatNumber; + + public TextHighlighter(int baseColor, boolean formatNumber, @NotNull MathContext mathContext) { + this.color = baseColor; + this.formatNumber = formatNumber; + this.mathContext = mathContext; + //this.colorRed = Color.red(baseColor); + this.colorRed = (baseColor >> 16) & 0xFF; + //this.colorGreen = Color.green(baseColor); + this.colorGreen = (color >> 8) & 0xFF; + //this.colorBlue = Color.blue(baseColor); + this.colorBlue = color & 0xFF; + } + + @NotNull + @Override + public Result process(@NotNull String text) throws CalculatorParseException { + final String result; + + int maxNumberOfOpenGroupSymbols = 0; + int numberOfOpenGroupSymbols = 0; + + final StringBuilder text1 = new StringBuilder(); + + int resultOffset = 0; + + final AbstractNumberBuilder numberBuilder; + if (!formatNumber) { + numberBuilder = new LiteNumberBuilder(CalculatorEngine.instance.getEngine()); + } else { + numberBuilder = new NumberBuilder(CalculatorEngine.instance.getEngine()); + } + for (int i = 0; i < text.length(); i++) { + MathType.Result mathType = MathType.getType(text, i, numberBuilder.isHexMode()); + + if (numberBuilder instanceof NumberBuilder) { + final MutableObject numberOffset = new MutableObject(0); + ((NumberBuilder) numberBuilder).process(text1, mathType, numberOffset); + resultOffset += numberOffset.getObject(); + } else { + ((LiteNumberBuilder) numberBuilder).process(mathType); + } + + final String match = mathType.getMatch(); + switch (mathType.getMathType()) { + case open_group_symbol: + numberOfOpenGroupSymbols++; + maxNumberOfOpenGroupSymbols = Math.max(maxNumberOfOpenGroupSymbols, numberOfOpenGroupSymbols); + text1.append(text.charAt(i)); + break; + case close_group_symbol: + numberOfOpenGroupSymbols--; + text1.append(text.charAt(i)); + break; + case operator: + text1.append(match); + if (match.length() > 1) { + i += match.length() - 1; + } + break; + case function: + i = processHighlightedText(text1, i, match, "i", null); + break; + case constant: + i = processHighlightedText(text1, i, match, "b", null); + break; + case numeral_base: + i = processHighlightedText(text1, i, match, "b", null); + break; + default: + if (mathType.getMathType() == MathType.text || match.length() <= 1) { + text1.append(text.charAt(i)); + } else { + text1.append(match); + i += match.length() - 1; + } + } + } + + if (numberBuilder instanceof NumberBuilder) { + final MutableObject numberOffset = new MutableObject(0); + ((NumberBuilder) numberBuilder).processNumber(text1, numberOffset); + resultOffset += numberOffset.getObject(); + } + + if (maxNumberOfOpenGroupSymbols > 0) { + + final StringBuilder text2 = new StringBuilder(); + + String s = text1.toString(); + int i = processBracketGroup(text2, s, 0, 0, maxNumberOfOpenGroupSymbols); + for (; i < s.length(); i++) { + text2.append(s.charAt(i)); + } + + //Log.d(CalculatorEditor.class.getName(), text2.toString()); + + result = text2.toString(); + } else { + result = text1.toString(); + } + + return new Result(result, resultOffset); + } + + private int processHighlightedText(@NotNull StringBuilder result, int i, @NotNull String match, @NotNull String tag, @Nullable Map tagAttributes) { + result.append("<").append(tag); + + if (tagAttributes != null) { + for (Map.Entry entry : tagAttributes.entrySet()) { + // attr1="attr1_value" attr2="attr2_value" + result.append(" ").append(entry.getKey()).append("=\"").append(entry.getValue()).append("\""); + } + } + + result.append(">").append(match).append(""); + if (match.length() > 1) { + return i + match.length() - 1; + } else { + return i; + } + } + + private int processBracketGroup(@NotNull StringBuilder result, @NotNull String s, int i, int numberOfOpenings, int maxNumberOfGroups) { + + result.append(""); + + for (; i < s.length(); i++) { + char ch = s.charAt(i); + + if (MathType.open_group_symbol.getTokens().contains(String.valueOf(ch))) { + result.append(ch); + result.append(""); + i = processBracketGroup(result, s, i + 1, numberOfOpenings + 1, maxNumberOfGroups); + result.append(""); + if (i < s.length() && MathType.close_group_symbol.getTokens().contains(String.valueOf(s.charAt(i)))) { + result.append(s.charAt(i)); + } + } else if (MathType.close_group_symbol.getTokens().contains(String.valueOf(ch))) { + break; + } else { + result.append(ch); + } + } + + result.append(""); + + + return i; + } + + private String getColor(int totalNumberOfOpenings, int numberOfOpenings) { + double c = 0.8; + + int offset = ((int) (255 * c)) * numberOfOpenings / (totalNumberOfOpenings + 1); + + // for tests: + // innt result = Color.rgb(BASE_COLOUR_RED_COMPONENT - offset, BASE_COLOUR_GREEN_COMPONENT - offset, BASE_COLOUR_BLUE_COMPONENT - offset); + int result = (0xFF << 24) | ((colorRed - offset) << 16) | ((colorGreen - offset) << 8) | (colorBlue - offset); + + return "#" + Integer.toHexString(result).substring(2); + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/view/UnitConverterViewBuilder.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/view/UnitConverterViewBuilder.java index 4113d63e..41603705 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/view/UnitConverterViewBuilder.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/view/UnitConverterViewBuilder.java @@ -10,14 +10,14 @@ import android.view.ViewGroup; import android.widget.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.solovyev.android.Unit; -import org.solovyev.android.UnitConverter; -import org.solovyev.android.UnitImpl; -import org.solovyev.android.UnitType; +import org.solovyev.android.calculator.CalculatorImpl; +import org.solovyev.math.units.Unit; +import org.solovyev.math.units.UnitConverter; +import org.solovyev.math.units.UnitImpl; +import org.solovyev.math.units.UnitType; import org.solovyev.android.calculator.R; import org.solovyev.android.view.ViewBuilder; import org.solovyev.android.view.ViewFromLayoutBuilder; -import org.solovyev.common.text.StringUtils; import java.util.Collections; import java.util.List; @@ -173,47 +173,12 @@ public class UnitConverterViewBuilder implements ViewBuilder { final String from = fromEditText.getText().toString(); try { - toEditText.setText(doConversion(converter, from, getFromUnitType(main), getToUnitType(main))); - } catch (ConversionException e) { + toEditText.setText(CalculatorImpl.doConversion(converter, from, getFromUnitType(main), getToUnitType(main))); + } catch (CalculatorImpl.ConversionException e) { toEditText.setText(context.getString(R.string.c_error)); } } - public static final class ConversionException extends Exception { - private ConversionException() { - } - - private ConversionException(Throwable throwable) { - super(throwable); - } - } - - @NotNull - public static String doConversion(@NotNull UnitConverter converter, - @Nullable String from, - @NotNull UnitType fromUnitType, - @NotNull UnitType toUnitType) throws ConversionException{ - final String result; - - if (StringUtils.isEmpty(from)) { - result = ""; - } else { - - String to = null; - try { - if (converter.isSupported(fromUnitType, toUnitType)) { - to = converter.convert(UnitImpl.newInstance(from, fromUnitType), toUnitType).getValue(); - } - } catch (RuntimeException e) { - throw new ConversionException(e); - } - - result = to; - } - - return result; - } - @NotNull private static Unit getToUnit(@NotNull View main) { final EditText toUnits = (EditText) main.findViewById(R.id.units_to); diff --git a/calculatorpp/src/test/java/org/solovyev/android/AndroidNumeralBaseTest.java b/calculatorpp/src/test/java/org/solovyev/android/AndroidNumeralBaseTest.java index fc198068..5ad54751 100644 --- a/calculatorpp/src/test/java/org/solovyev/android/AndroidNumeralBaseTest.java +++ b/calculatorpp/src/test/java/org/solovyev/android/AndroidNumeralBaseTest.java @@ -1,70 +1,72 @@ -package org.solovyev.android; - -import junit.framework.Assert; -import org.jetbrains.annotations.NotNull; -import org.junit.Test; -import org.solovyev.android.calculator.AndroidNumeralBase; - -import java.util.Date; -import java.util.Random; - -/** - * User: serso - * Date: 4/21/12 - * Time: 8:24 PM - */ -public class AndroidNumeralBaseTest { - - @NotNull - private final UnitConverter c = AndroidNumeralBase.getConverter(); - - @Test - public void testIsSupported() throws Exception { - Assert.assertTrue(c.isSupported(AndroidNumeralBase.bin, AndroidNumeralBase.dec)); - } - - @Test - public void testConvertFromDec() throws Exception { - - Assert.assertEquals("101", c.convert(AndroidNumeralBase.dec.createUnit("5"), AndroidNumeralBase.bin).getValue()); - Assert.assertEquals("1", c.convert(AndroidNumeralBase.dec.createUnit("1"), AndroidNumeralBase.bin).getValue()); - Assert.assertEquals("0", c.convert(AndroidNumeralBase.dec.createUnit("0"), AndroidNumeralBase.bin).getValue()); - Assert.assertEquals("1111100111", c.convert(AndroidNumeralBase.dec.createUnit("999"), AndroidNumeralBase.bin).getValue()); - - Assert.assertEquals("A23", c.convert(AndroidNumeralBase.dec.createUnit("2595"), AndroidNumeralBase.hex).getValue()); - Assert.assertEquals("AEE", c.convert(AndroidNumeralBase.dec.createUnit("2798"), AndroidNumeralBase.hex).getValue()); - Assert.assertEquals("15", c.convert(AndroidNumeralBase.dec.createUnit("21"), AndroidNumeralBase.hex).getValue()); - Assert.assertEquals("0", c.convert(AndroidNumeralBase.dec.createUnit("0"), AndroidNumeralBase.hex).getValue()); - Assert.assertEquals("3E7", c.convert(AndroidNumeralBase.dec.createUnit("999"), AndroidNumeralBase.hex).getValue()); - - Assert.assertEquals("76", c.convert(AndroidNumeralBase.dec.createUnit("62"), AndroidNumeralBase.oct).getValue()); - Assert.assertEquals("12", c.convert(AndroidNumeralBase.dec.createUnit("10"), AndroidNumeralBase.oct).getValue()); - Assert.assertEquals("15", c.convert(AndroidNumeralBase.dec.createUnit("13"), AndroidNumeralBase.oct).getValue()); - Assert.assertEquals("0", c.convert(AndroidNumeralBase.dec.createUnit("0"), AndroidNumeralBase.oct).getValue()); - Assert.assertEquals("10445", c.convert(AndroidNumeralBase.dec.createUnit("4389"), AndroidNumeralBase.oct).getValue()); - } - - @Test - public void testRandomConvert() throws Exception { - final Random random = new Random(new Date().getTime()); - for (int i = 0; i < 100000; i++) { - final String value = String.valueOf(random.nextInt()); - Assert.assertEquals(value, convertChain(value, AndroidNumeralBase.dec, AndroidNumeralBase.oct, AndroidNumeralBase.oct, AndroidNumeralBase.bin, AndroidNumeralBase.dec)); - Assert.assertEquals(value, convertChain(value, AndroidNumeralBase.dec, AndroidNumeralBase.bin, AndroidNumeralBase.hex, AndroidNumeralBase.dec, AndroidNumeralBase.dec)); - Assert.assertEquals(value, convertChain(value, AndroidNumeralBase.dec, AndroidNumeralBase.dec, AndroidNumeralBase.hex, AndroidNumeralBase.oct, AndroidNumeralBase.dec)); - Assert.assertEquals(value, convertChain(value, AndroidNumeralBase.dec, AndroidNumeralBase.hex, AndroidNumeralBase.bin, AndroidNumeralBase.oct, AndroidNumeralBase.dec)); - - } - } - - @NotNull - private String convertChain(@NotNull String value, @NotNull AndroidNumeralBase baseAndroid, @NotNull AndroidNumeralBase... typeAndroids) { - Unit unit = baseAndroid.createUnit(value); - - for (AndroidNumeralBase typeAndroid : typeAndroids) { - unit = AndroidNumeralBase.getConverter().convert(unit, typeAndroid); - } - - return unit.getValue(); - } -} +package org.solovyev.android; + +import junit.framework.Assert; +import org.jetbrains.annotations.NotNull; +import org.junit.Test; +import org.solovyev.android.calculator.AndroidNumeralBase; +import org.solovyev.math.units.Unit; +import org.solovyev.math.units.UnitConverter; + +import java.util.Date; +import java.util.Random; + +/** + * User: serso + * Date: 4/21/12 + * Time: 8:24 PM + */ +public class AndroidNumeralBaseTest { + + @NotNull + private final UnitConverter c = AndroidNumeralBase.getConverter(); + + @Test + public void testIsSupported() throws Exception { + Assert.assertTrue(c.isSupported(AndroidNumeralBase.bin, AndroidNumeralBase.dec)); + } + + @Test + public void testConvertFromDec() throws Exception { + + Assert.assertEquals("101", c.convert(AndroidNumeralBase.dec.createUnit("5"), AndroidNumeralBase.bin).getValue()); + Assert.assertEquals("1", c.convert(AndroidNumeralBase.dec.createUnit("1"), AndroidNumeralBase.bin).getValue()); + Assert.assertEquals("0", c.convert(AndroidNumeralBase.dec.createUnit("0"), AndroidNumeralBase.bin).getValue()); + Assert.assertEquals("1111100111", c.convert(AndroidNumeralBase.dec.createUnit("999"), AndroidNumeralBase.bin).getValue()); + + Assert.assertEquals("A23", c.convert(AndroidNumeralBase.dec.createUnit("2595"), AndroidNumeralBase.hex).getValue()); + Assert.assertEquals("AEE", c.convert(AndroidNumeralBase.dec.createUnit("2798"), AndroidNumeralBase.hex).getValue()); + Assert.assertEquals("15", c.convert(AndroidNumeralBase.dec.createUnit("21"), AndroidNumeralBase.hex).getValue()); + Assert.assertEquals("0", c.convert(AndroidNumeralBase.dec.createUnit("0"), AndroidNumeralBase.hex).getValue()); + Assert.assertEquals("3E7", c.convert(AndroidNumeralBase.dec.createUnit("999"), AndroidNumeralBase.hex).getValue()); + + Assert.assertEquals("76", c.convert(AndroidNumeralBase.dec.createUnit("62"), AndroidNumeralBase.oct).getValue()); + Assert.assertEquals("12", c.convert(AndroidNumeralBase.dec.createUnit("10"), AndroidNumeralBase.oct).getValue()); + Assert.assertEquals("15", c.convert(AndroidNumeralBase.dec.createUnit("13"), AndroidNumeralBase.oct).getValue()); + Assert.assertEquals("0", c.convert(AndroidNumeralBase.dec.createUnit("0"), AndroidNumeralBase.oct).getValue()); + Assert.assertEquals("10445", c.convert(AndroidNumeralBase.dec.createUnit("4389"), AndroidNumeralBase.oct).getValue()); + } + + @Test + public void testRandomConvert() throws Exception { + final Random random = new Random(new Date().getTime()); + for (int i = 0; i < 100000; i++) { + final String value = String.valueOf(random.nextInt()); + Assert.assertEquals(value, convertChain(value, AndroidNumeralBase.dec, AndroidNumeralBase.oct, AndroidNumeralBase.oct, AndroidNumeralBase.bin, AndroidNumeralBase.dec)); + Assert.assertEquals(value, convertChain(value, AndroidNumeralBase.dec, AndroidNumeralBase.bin, AndroidNumeralBase.hex, AndroidNumeralBase.dec, AndroidNumeralBase.dec)); + Assert.assertEquals(value, convertChain(value, AndroidNumeralBase.dec, AndroidNumeralBase.dec, AndroidNumeralBase.hex, AndroidNumeralBase.oct, AndroidNumeralBase.dec)); + Assert.assertEquals(value, convertChain(value, AndroidNumeralBase.dec, AndroidNumeralBase.hex, AndroidNumeralBase.bin, AndroidNumeralBase.oct, AndroidNumeralBase.dec)); + + } + } + + @NotNull + private String convertChain(@NotNull String value, @NotNull AndroidNumeralBase baseAndroid, @NotNull AndroidNumeralBase... typeAndroids) { + Unit unit = baseAndroid.createUnit(value); + + for (AndroidNumeralBase typeAndroid : typeAndroids) { + unit = AndroidNumeralBase.getConverter().convert(unit, typeAndroid); + } + + return unit.getValue(); + } +} diff --git a/calculatorpp/src/test/java/org/solovyev/android/calculator/TextHighlighterTest.java b/calculatorpp/src/test/java/org/solovyev/android/calculator/TextHighlighterTest.java index bc8950a9..af984177 100644 --- a/calculatorpp/src/test/java/org/solovyev/android/calculator/TextHighlighterTest.java +++ b/calculatorpp/src/test/java/org/solovyev/android/calculator/TextHighlighterTest.java @@ -1,137 +1,137 @@ -/* - * 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.JsclMathEngine; -import jscl.MathEngine; -import jscl.NumeralBase; -import junit.framework.Assert; -import org.junit.Test; -import org.solovyev.android.calculator.model.CalculatorEngine; -import org.solovyev.android.calculator.model.TextProcessor; -import org.solovyev.android.calculator.view.TextHighlighter; - -import java.util.Date; -import java.util.Random; - -/** - * User: serso - * Date: 10/12/11 - * Time: 10:07 PM - */ -public class TextHighlighterTest { - - @Test - public void testProcess() throws Exception { - TextProcessor textHighlighter = new TextHighlighter(0, false, JsclMathEngine.instance); - - final Random random = new Random(new Date().getTime()); - for (int i = 0; i < 1000; i++) { - final StringBuilder sb = new StringBuilder(); - for (int j = 0; j < 1000; j++) { - sb.append(random.nextBoolean() ? "(" : ")"); - } - try { - textHighlighter.process(sb.toString()); - } catch (Exception e) { - System.out.println(sb.toString()); - throw e; - } - } - - Assert.assertEquals(")(((())())", textHighlighter.process(")(((())())").toString()); - Assert.assertEquals(")", textHighlighter.process(")").toString()); - Assert.assertEquals(")()(", textHighlighter.process(")()(").toString()); - - textHighlighter = new TextHighlighter(0, true, JsclMathEngine.instance); - Assert.assertEquals("1 000 000", textHighlighter.process("1000000").toString()); - Assert.assertEquals("1 000 000", textHighlighter.process("1000000").toString()); - Assert.assertEquals("0.1E3", textHighlighter.process("0.1E3").toString()); - Assert.assertEquals("1E3", textHighlighter.process("1E3").toString()); - Assert.assertEquals("20x:", textHighlighter.process("20x:").toString()); - Assert.assertEquals("20g", textHighlighter.process("20g").toString()); - Assert.assertEquals("22g", textHighlighter.process("22g").toString()); - Assert.assertEquals("20ю", textHighlighter.process("20ю").toString()); - Assert.assertEquals("20ъ", textHighlighter.process("20ъ").toString()); - Assert.assertEquals("3!!", textHighlighter.process("3!!").toString()); - Assert.assertEquals("2", textHighlighter.process("2").toString()); - Assert.assertEquals("21", textHighlighter.process("21").toString()); - Assert.assertEquals("214", textHighlighter.process("214").toString()); - Assert.assertEquals("2 145", textHighlighter.process("2 145").toString()); - Assert.assertEquals("1 000 000E3", textHighlighter.process("1000000E3").toString()); - Assert.assertEquals("-1 000 000E3", textHighlighter.process("-1000000E3").toString()); - Assert.assertEquals("-1 000 000E-3", textHighlighter.process("-1000000E-3").toString()); - Assert.assertEquals("-1 000 000E-30000", textHighlighter.process("-1000000E-30000").toString()); - textHighlighter = new TextHighlighter(0, false, JsclMathEngine.instance); - - textHighlighter.process("cannot calculate 3^10^10 !!!\n" + - " unable to enter 0. FIXED\n" + - " empty display in Xperia Rayo\n" + - " check привиденная FIXED\n" + - " set display result only if text in editor was not changed FIXED\n" + - " shift M text to the left\n" + - " do not show SYNTAX ERROR always (may be show send clock?q) FIXED\n" + - " ln(8)*log(8) => ln(8)*og(8) FIXED\n" + - " copy/paste ln(8)*log(8)\n" + - " 6!^2 ERROR"); - - Assert.assertEquals("sin(2)", textHighlighter.process("sin(2)").toString()); - Assert.assertEquals("atanh(2)", textHighlighter.process("atanh(2)").toString()); - - - Assert.assertEquals("0x:E", textHighlighter.process("0x:E").toString()); - Assert.assertEquals("0x:6F", textHighlighter.process("0x:6F").toString()); - Assert.assertEquals("0x:6F.", textHighlighter.process("0x:6F.").toString()); - Assert.assertEquals("0x:6F.2", textHighlighter.process("0x:6F.2").toString()); - Assert.assertEquals("0x:6F.B", textHighlighter.process("0x:6F.B").toString()); - Assert.assertEquals("0x:006F.B", textHighlighter.process("0x:006F.B").toString()); - Assert.assertEquals("0x:0", textHighlighter.process("0x:0").toString()); - Assert.assertEquals("0x:FF33233FFE", textHighlighter.process("0x:FF33233FFE").toString()); - Assert.assertEquals("0x:FF33 233 FFE", textHighlighter.process("0x:FF33 233 FFE").toString()); - - final MathEngine me = CalculatorEngine.instance.getEngine(); - try { - me.setNumeralBase(NumeralBase.hex); - Assert.assertEquals("E", textHighlighter.process("E").toString()); - Assert.assertEquals(".E", textHighlighter.process(".E").toString()); - Assert.assertEquals("E+", textHighlighter.process("E+").toString()); - Assert.assertEquals("E.", textHighlighter.process("E.").toString()); - Assert.assertEquals(".E.", textHighlighter.process(".E.").toString()); - Assert.assertEquals("6F", textHighlighter.process("6F").toString()); - Assert.assertEquals("6F", textHighlighter.process("6F").toString()); - Assert.assertEquals("6F.", textHighlighter.process("6F.").toString()); - Assert.assertEquals("6F.2", textHighlighter.process("6F.2").toString()); - Assert.assertEquals("6F.B", textHighlighter.process("6F.B").toString()); - Assert.assertEquals("006F.B", textHighlighter.process("006F.B").toString()); - } finally { - me.setNumeralBase(NumeralBase.dec); - } - - Assert.assertEquals("0b:110101", textHighlighter.process("0b:110101").toString()); - Assert.assertEquals("0b:110101.", textHighlighter.process("0b:110101.").toString()); - Assert.assertEquals("0b:110101.101", textHighlighter.process("0b:110101.101").toString()); - Assert.assertEquals("0b:11010100.1", textHighlighter.process("0b:11010100.1").toString()); - Assert.assertEquals("0b:110101.0", textHighlighter.process("0b:110101.0").toString()); - Assert.assertEquals("0b:0", textHighlighter.process("0b:0").toString()); - Assert.assertEquals("0b:1010100101111010101001", textHighlighter.process("0b:1010100101111010101001").toString()); - Assert.assertEquals("0b:101 010 01 0 111 1 0 10101001", textHighlighter.process("0b:101 010 01 0 111 1 0 10101001").toString()); - - try { - me.setNumeralBase(NumeralBase.bin); - Assert.assertEquals("110101", textHighlighter.process("110101").toString()); - Assert.assertEquals("110101.", textHighlighter.process("110101.").toString()); - Assert.assertEquals("110101.101", textHighlighter.process("110101.101").toString()); - Assert.assertEquals("11010100.1", textHighlighter.process("11010100.1").toString()); - Assert.assertEquals("110101.0", textHighlighter.process("110101.0").toString()); - Assert.assertEquals("0", textHighlighter.process("0").toString()); - Assert.assertEquals("1010100101111010101001", textHighlighter.process("1010100101111010101001").toString()); - Assert.assertEquals("101 010 01 0 111 1 0 10101001", textHighlighter.process("101 010 01 0 111 1 0 10101001").toString()); - } finally { - me.setNumeralBase(NumeralBase.dec); - } - } -} +/* + * 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.JsclMathEngine; +import jscl.MathEngine; +import jscl.NumeralBase; +import junit.framework.Assert; +import org.junit.Test; +import org.solovyev.android.calculator.model.CalculatorEngine; +import org.solovyev.android.calculator.text.TextProcessor; +import org.solovyev.android.calculator.view.TextHighlighter; + +import java.util.Date; +import java.util.Random; + +/** + * User: serso + * Date: 10/12/11 + * Time: 10:07 PM + */ +public class TextHighlighterTest { + + @Test + public void testProcess() throws Exception { + TextProcessor textHighlighter = new TextHighlighter(0, false, JsclMathEngine.instance); + + final Random random = new Random(new Date().getTime()); + for (int i = 0; i < 1000; i++) { + final StringBuilder sb = new StringBuilder(); + for (int j = 0; j < 1000; j++) { + sb.append(random.nextBoolean() ? "(" : ")"); + } + try { + textHighlighter.process(sb.toString()); + } catch (Exception e) { + System.out.println(sb.toString()); + throw e; + } + } + + Assert.assertEquals(")(((())())", textHighlighter.process(")(((())())").toString()); + Assert.assertEquals(")", textHighlighter.process(")").toString()); + Assert.assertEquals(")()(", textHighlighter.process(")()(").toString()); + + textHighlighter = new TextHighlighter(0, true, JsclMathEngine.instance); + Assert.assertEquals("1 000 000", textHighlighter.process("1000000").toString()); + Assert.assertEquals("1 000 000", textHighlighter.process("1000000").toString()); + Assert.assertEquals("0.1E3", textHighlighter.process("0.1E3").toString()); + Assert.assertEquals("1E3", textHighlighter.process("1E3").toString()); + Assert.assertEquals("20x:", textHighlighter.process("20x:").toString()); + Assert.assertEquals("20g", textHighlighter.process("20g").toString()); + Assert.assertEquals("22g", textHighlighter.process("22g").toString()); + Assert.assertEquals("20ю", textHighlighter.process("20ю").toString()); + Assert.assertEquals("20ъ", textHighlighter.process("20ъ").toString()); + Assert.assertEquals("3!!", textHighlighter.process("3!!").toString()); + Assert.assertEquals("2", textHighlighter.process("2").toString()); + Assert.assertEquals("21", textHighlighter.process("21").toString()); + Assert.assertEquals("214", textHighlighter.process("214").toString()); + Assert.assertEquals("2 145", textHighlighter.process("2 145").toString()); + Assert.assertEquals("1 000 000E3", textHighlighter.process("1000000E3").toString()); + Assert.assertEquals("-1 000 000E3", textHighlighter.process("-1000000E3").toString()); + Assert.assertEquals("-1 000 000E-3", textHighlighter.process("-1000000E-3").toString()); + Assert.assertEquals("-1 000 000E-30000", textHighlighter.process("-1000000E-30000").toString()); + textHighlighter = new TextHighlighter(0, false, JsclMathEngine.instance); + + textHighlighter.process("cannot calculate 3^10^10 !!!\n" + + " unable to enter 0. FIXED\n" + + " empty display in Xperia Rayo\n" + + " check привиденная FIXED\n" + + " set display result only if text in editor was not changed FIXED\n" + + " shift M text to the left\n" + + " do not show SYNTAX ERROR always (may be show send clock?q) FIXED\n" + + " ln(8)*log(8) => ln(8)*og(8) FIXED\n" + + " copy/paste ln(8)*log(8)\n" + + " 6!^2 ERROR"); + + Assert.assertEquals("sin(2)", textHighlighter.process("sin(2)").toString()); + Assert.assertEquals("atanh(2)", textHighlighter.process("atanh(2)").toString()); + + + Assert.assertEquals("0x:E", textHighlighter.process("0x:E").toString()); + Assert.assertEquals("0x:6F", textHighlighter.process("0x:6F").toString()); + Assert.assertEquals("0x:6F.", textHighlighter.process("0x:6F.").toString()); + Assert.assertEquals("0x:6F.2", textHighlighter.process("0x:6F.2").toString()); + Assert.assertEquals("0x:6F.B", textHighlighter.process("0x:6F.B").toString()); + Assert.assertEquals("0x:006F.B", textHighlighter.process("0x:006F.B").toString()); + Assert.assertEquals("0x:0", textHighlighter.process("0x:0").toString()); + Assert.assertEquals("0x:FF33233FFE", textHighlighter.process("0x:FF33233FFE").toString()); + Assert.assertEquals("0x:FF33 233 FFE", textHighlighter.process("0x:FF33 233 FFE").toString()); + + final MathEngine me = CalculatorEngine.instance.getEngine(); + try { + me.setNumeralBase(NumeralBase.hex); + Assert.assertEquals("E", textHighlighter.process("E").toString()); + Assert.assertEquals(".E", textHighlighter.process(".E").toString()); + Assert.assertEquals("E+", textHighlighter.process("E+").toString()); + Assert.assertEquals("E.", textHighlighter.process("E.").toString()); + Assert.assertEquals(".E.", textHighlighter.process(".E.").toString()); + Assert.assertEquals("6F", textHighlighter.process("6F").toString()); + Assert.assertEquals("6F", textHighlighter.process("6F").toString()); + Assert.assertEquals("6F.", textHighlighter.process("6F.").toString()); + Assert.assertEquals("6F.2", textHighlighter.process("6F.2").toString()); + Assert.assertEquals("6F.B", textHighlighter.process("6F.B").toString()); + Assert.assertEquals("006F.B", textHighlighter.process("006F.B").toString()); + } finally { + me.setNumeralBase(NumeralBase.dec); + } + + Assert.assertEquals("0b:110101", textHighlighter.process("0b:110101").toString()); + Assert.assertEquals("0b:110101.", textHighlighter.process("0b:110101.").toString()); + Assert.assertEquals("0b:110101.101", textHighlighter.process("0b:110101.101").toString()); + Assert.assertEquals("0b:11010100.1", textHighlighter.process("0b:11010100.1").toString()); + Assert.assertEquals("0b:110101.0", textHighlighter.process("0b:110101.0").toString()); + Assert.assertEquals("0b:0", textHighlighter.process("0b:0").toString()); + Assert.assertEquals("0b:1010100101111010101001", textHighlighter.process("0b:1010100101111010101001").toString()); + Assert.assertEquals("0b:101 010 01 0 111 1 0 10101001", textHighlighter.process("0b:101 010 01 0 111 1 0 10101001").toString()); + + try { + me.setNumeralBase(NumeralBase.bin); + Assert.assertEquals("110101", textHighlighter.process("110101").toString()); + Assert.assertEquals("110101.", textHighlighter.process("110101.").toString()); + Assert.assertEquals("110101.101", textHighlighter.process("110101.101").toString()); + Assert.assertEquals("11010100.1", textHighlighter.process("11010100.1").toString()); + Assert.assertEquals("110101.0", textHighlighter.process("110101.0").toString()); + Assert.assertEquals("0", textHighlighter.process("0").toString()); + Assert.assertEquals("1010100101111010101001", textHighlighter.process("1010100101111010101001").toString()); + Assert.assertEquals("101 010 01 0 111 1 0 10101001", textHighlighter.process("101 010 01 0 111 1 0 10101001").toString()); + } finally { + me.setNumeralBase(NumeralBase.dec); + } + } +} 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 a58e72a5..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 @@ -11,7 +11,8 @@ import junit.framework.Assert; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.junit.Test; -import org.solovyev.android.calculator.ICalculatorDisplay; +import org.solovyev.android.calculator.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; @@ -124,7 +125,7 @@ public class HistoryUtilsTest { HistoryHelper history = new SimpleHistoryHelper(); - ICalculatorDisplay calculatorDisplay = new TestCalculatorDisplay(); + CalculatorDisplay calculatorDisplay = new TestCalculatorDisplay(); calculatorDisplay.setErrorMessage("error_msg1"); calculatorDisplay.setText("Error"); calculatorDisplay.setSelection(1); @@ -214,7 +215,7 @@ public class HistoryUtilsTest { } - private static class TestCalculatorDisplay implements ICalculatorDisplay { + private static class TestCalculatorDisplay implements CalculatorDisplay { @NotNull private final TestEditor testEditor = new TestEditor(); 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 449ed9e2..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 @@ -15,6 +15,8 @@ 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; @@ -52,13 +54,13 @@ public class CalculatorEngineTest { } - 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("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°)").getResult()); + Assert.assertEquals("∂(cos(t), t, t, 1°)", cm.evaluate(JsclOperation.simplify, "∂(cos(t),t,t,1°)").getStringResult()); } finally { cm.getEngine().setAngleUnits(defaultAngleUnit); } @@ -111,120 +113,120 @@ public class CalculatorEngineTest { public void testEvaluate() throws Exception { final CalculatorEngine cm = CalculatorEngine.instance; - Assert.assertEquals("cos(t)+10%", cm.evaluate(JsclOperation.simplify, "cos(t)+10%").getResult()); + Assert.assertEquals("cos(t)+10%", cm.evaluate(JsclOperation.simplify, "cos(t)+10%").getStringResult()); final Generic expression = cm.getEngine().simplifyGeneric("cos(t)+10%"); 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()); + 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)").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()); + 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").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()); + 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))))").getResult()); - Assert.assertEquals("-3.41+3.41i", cm.evaluate(JsclOperation.numeric, "(5tan(2i)+2i)/(1-i)").getResult()); + Assert.assertEquals("0.921+Πi", cm.evaluate(JsclOperation.numeric, "ln(5cosh(38π√(2cos(2))))").getStringResult()); + Assert.assertEquals("-3.41+3.41i", cm.evaluate(JsclOperation.numeric, "(5tan(2i)+2i)/(1-i)").getStringResult()); } 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()); + 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!").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("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!").getResult()); + junit.framework.Assert.assertEquals("√(-1)!", cm.evaluate(JsclOperation.numeric, "i!").getStringResult()); fail(); } catch (CalculatorParseException e) { } - junit.framework.Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "(π/π)!").getResult()); + junit.framework.Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "(π/π)!").getStringResult()); try { - junit.framework.Assert.assertEquals("i", cm.evaluate(JsclOperation.numeric, "(-1)i!").getResult()); + junit.framework.Assert.assertEquals("i", cm.evaluate(JsclOperation.numeric, "(-1)i!").getStringResult()); fail(); } catch (CalculatorParseException e) { } - junit.framework.Assert.assertEquals("24i", cm.evaluate(JsclOperation.numeric, "4!i").getResult()); + junit.framework.Assert.assertEquals("24i", cm.evaluate(JsclOperation.numeric, "4!i").getStringResult()); CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("si", 5d)); 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()); + 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").getResult()); + Assert.assertEquals("5", cm.evaluate(JsclOperation.numeric, "si").getStringResult()); CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("k", 3.5d)); CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("k1", 4d)); - Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "k11").getResult()); + Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "k11").getStringResult()); CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t", (String) null)); - 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("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").getResult()); - Assert.assertEquals("3.957", cm.evaluate(JsclOperation.numeric, "ln(8)lg(8)+ln(8)").getResult()); + Assert.assertEquals("100", cm.evaluate(JsclOperation.numeric, "0.1E3").getStringResult()); + Assert.assertEquals("3.957", cm.evaluate(JsclOperation.numeric, "ln(8)lg(8)+ln(8)").getStringResult()); - Assert.assertEquals("0.933", cm.evaluate(JsclOperation.numeric, "0x:E/0x:F").getResult()); + Assert.assertEquals("0.933", cm.evaluate(JsclOperation.numeric, "0x:E/0x:F").getStringResult()); try { 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()); + 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))))))").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((0))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))").getResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "((((((0))))))").getStringResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((0))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))").getStringResult()); /* Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "30°").getResult()); @@ -247,11 +249,11 @@ public class CalculatorEngineTest { }*/ 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()); + 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)").getResult()); - Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "∂(t^2,t)").getResult()); + Assert.assertEquals("2t", cm.evaluate(JsclOperation.simplify, "∂(t^2,t)").getStringResult()); + Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "∂(t^2,t)").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)")); @@ -265,7 +267,7 @@ public class CalculatorEngineTest { public void testFormatting() throws Exception { final CalculatorEngine ce = CalculatorEngine.instance; - Assert.assertEquals("12 345", ce.evaluate(JsclOperation.simplify, "12345").getResult()); + Assert.assertEquals("12 345", ce.evaluate(JsclOperation.simplify, "12345").getStringResult()); } @@ -273,7 +275,7 @@ public class CalculatorEngineTest { public void testI() throws CalculatorParseException, CalculatorEvalException { final CalculatorEngine cm = CalculatorEngine.instance; - Assert.assertEquals("-i", cm.evaluate(JsclOperation.numeric, "i^3").getResult()); + Assert.assertEquals("-i", cm.evaluate(JsclOperation.numeric, "i^3").getStringResult()); for (int i = 0; i < 1000; i++) { double real = (Math.random()-0.5) * 1000; double imag = (Math.random()-0.5) * 1000; @@ -287,7 +289,7 @@ public class CalculatorEngineTest { sb.append(imag); sb.append("^").append(exp); try { - cm.evaluate(JsclOperation.numeric, sb.toString()).getResult(); + cm.evaluate(JsclOperation.numeric, sb.toString()).getStringResult(); } catch (Throwable e) { fail(sb.toString()); } @@ -302,7 +304,7 @@ public class CalculatorEngineTest { 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()); + 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(); @@ -312,13 +314,13 @@ public class CalculatorEngineTest { 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()); + 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").getResult()); + Assert.assertEquals("5", cm.evaluate(JsclOperation.numeric, "si").getStringResult()); try { cm.evaluate(JsclOperation.numeric, "sin"); @@ -337,11 +339,11 @@ public class CalculatorEngineTest { decimalGroupSymbols.setGroupingSeparator('\''); cm.setDecimalGroupSymbols(decimalGroupSymbols); cm.setPrecision(2); - Assert.assertEquals("12'345'678.9", cm.evaluate(JsclOperation.numeric, "1.23456789E7").getResult()); + Assert.assertEquals("12'345'678.9", cm.evaluate(JsclOperation.numeric, "1.23456789E7").getStringResult()); cm.setPrecision(10); - 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()); + 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()); @@ -355,36 +357,36 @@ public class CalculatorEngineTest { 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("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)").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("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)").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("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)").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, "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)").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("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)").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, "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)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ap(1, 1)").getResult()); + 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)").getResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ap(1, 0)").getStringResult()); } @@ -393,17 +395,17 @@ public class CalculatorEngineTest { 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()); + 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:π").getResult(); + cm.evaluate(JsclOperation.numeric, "0b:π").getStringResult(); Assert.fail(); } catch (CalculatorParseException e) { // ok @@ -412,12 +414,12 @@ public class CalculatorEngineTest { 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()); + 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").getResult()); - Assert.assertEquals("E", cm.evaluate(JsclOperation.numeric, "E").getResult()); + Assert.assertEquals("63 7B", cm.evaluate(JsclOperation.numeric, "56CE+CAD").getStringResult()); + Assert.assertEquals("E", cm.evaluate(JsclOperation.numeric, "E").getStringResult()); } finally { cm.setNumeralBase(defaultNumeralBase); } @@ -432,12 +434,12 @@ public class CalculatorEngineTest { // 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()); + 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/FromJsclSimplifyTextProcessorTest.java b/calculatorpp/src/test/java/org/solovyev/android/calculator/model/FromJsclSimplifyTextProcessorTest.java index ad630885..a52ee3af 100644 --- a/calculatorpp/src/test/java/org/solovyev/android/calculator/model/FromJsclSimplifyTextProcessorTest.java +++ b/calculatorpp/src/test/java/org/solovyev/android/calculator/model/FromJsclSimplifyTextProcessorTest.java @@ -1,69 +1,70 @@ -package org.solovyev.android.calculator.model; - -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; - -import java.text.DecimalFormatSymbols; - -/** - * User: serso - * Date: 10/20/11 - * Time: 3:43 PM - */ -public class FromJsclSimplifyTextProcessorTest { - - @BeforeClass - public static void setUp() throws Exception { - CalculatorEngine.instance.init(null, null); - } - - @Test - public void testProcess() throws Exception { - FromJsclSimplifyTextProcessor tp = new FromJsclSimplifyTextProcessor(); - //Assert.assertEquals("(e)", tp.process("(2.718281828459045)")); - //Assert.assertEquals("ee", tp.process("2.718281828459045*2.718281828459045")); - //Assert.assertEquals("((e)(e))", tp.process("((2.718281828459045)*(2.718281828459045))")); - DecimalFormatSymbols decimalGroupSymbols = new DecimalFormatSymbols(); - decimalGroupSymbols.setGroupingSeparator(' '); - CalculatorEngine.instance.setDecimalGroupSymbols(decimalGroupSymbols); - //Assert.assertEquals("123 456 789e", tp.process("123456789*2.718281828459045")); - //Assert.assertEquals("123 456 789e", tp.process("123 456 789 * 2.718281828459045")); - //Assert.assertEquals("t11e", tp.process("t11*2.718281828459045")); - //Assert.assertEquals("e", tp.process("2.718281828459045")); - //Assert.assertEquals("tee", tp.process("t2.718281828459045*2.718281828459045")); - - CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t2.718281828459045", "2")); - CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t", (String)null)); - //Assert.assertEquals("t2.718281828459045e", tp.process("t2.718281828459045*2.718281828459045")); - //Assert.assertEquals("ee", tp.process("2.718281828459045*2.718281828459045")); - Assert.assertEquals("t×", tp.process("t*")); - Assert.assertEquals("×t", tp.process("*t")); - Assert.assertEquals("t2", tp.process("t*2")); - Assert.assertEquals("2t", tp.process("2*t")); - CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t", (String) null)); - Assert.assertEquals("t×", tp.process("t*")); - Assert.assertEquals("×t", tp.process("*t")); - - Assert.assertEquals("t2", tp.process("t*2")); - Assert.assertEquals("2t", tp.process("2*t")); - - Assert.assertEquals("t^2×2", tp.process("t^2*2")); - Assert.assertEquals("2t^2", tp.process("2*t^2")); - - Assert.assertEquals("t^[2×2t]", tp.process("t^[2*2*t]")); - Assert.assertEquals("2t^2[2t]", tp.process("2*t^2[2*t]")); - - CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("k", (String) null)); - Assert.assertEquals("(t+2k)[k+2t]", tp.process("(t+2*k)*[k+2*t]")); - Assert.assertEquals("(te+2k)e[k+2te]", tp.process("(t*e+2*k)*e*[k+2*t*e]")); - - - Assert.assertEquals("tlog(3)", tp.process("t*log(3)")); - Assert.assertEquals("t√(3)", tp.process("t*√(3)")); - Assert.assertEquals("20x", tp.process("20*x")); - Assert.assertEquals("20x", tp.process("20x")); - Assert.assertEquals("2×0x3", tp.process("2*0x3")); - Assert.assertEquals("2×0x:3", tp.process("2*0x:3")); - } -} +package org.solovyev.android.calculator.model; + +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.solovyev.android.calculator.text.FromJsclSimplifyTextProcessor; + +import java.text.DecimalFormatSymbols; + +/** + * User: serso + * Date: 10/20/11 + * Time: 3:43 PM + */ +public class FromJsclSimplifyTextProcessorTest { + + @BeforeClass + public static void setUp() throws Exception { + CalculatorEngine.instance.init(null, null); + } + + @Test + public void testProcess() throws Exception { + FromJsclSimplifyTextProcessor tp = new FromJsclSimplifyTextProcessor(); + //Assert.assertEquals("(e)", tp.process("(2.718281828459045)")); + //Assert.assertEquals("ee", tp.process("2.718281828459045*2.718281828459045")); + //Assert.assertEquals("((e)(e))", tp.process("((2.718281828459045)*(2.718281828459045))")); + DecimalFormatSymbols decimalGroupSymbols = new DecimalFormatSymbols(); + decimalGroupSymbols.setGroupingSeparator(' '); + CalculatorEngine.instance.setDecimalGroupSymbols(decimalGroupSymbols); + //Assert.assertEquals("123 456 789e", tp.process("123456789*2.718281828459045")); + //Assert.assertEquals("123 456 789e", tp.process("123 456 789 * 2.718281828459045")); + //Assert.assertEquals("t11e", tp.process("t11*2.718281828459045")); + //Assert.assertEquals("e", tp.process("2.718281828459045")); + //Assert.assertEquals("tee", tp.process("t2.718281828459045*2.718281828459045")); + + CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t2.718281828459045", "2")); + CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t", (String)null)); + //Assert.assertEquals("t2.718281828459045e", tp.process("t2.718281828459045*2.718281828459045")); + //Assert.assertEquals("ee", tp.process("2.718281828459045*2.718281828459045")); + Assert.assertEquals("t×", tp.process("t*")); + Assert.assertEquals("×t", tp.process("*t")); + Assert.assertEquals("t2", tp.process("t*2")); + Assert.assertEquals("2t", tp.process("2*t")); + CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t", (String) null)); + Assert.assertEquals("t×", tp.process("t*")); + Assert.assertEquals("×t", tp.process("*t")); + + Assert.assertEquals("t2", tp.process("t*2")); + Assert.assertEquals("2t", tp.process("2*t")); + + Assert.assertEquals("t^2×2", tp.process("t^2*2")); + Assert.assertEquals("2t^2", tp.process("2*t^2")); + + Assert.assertEquals("t^[2×2t]", tp.process("t^[2*2*t]")); + Assert.assertEquals("2t^2[2t]", tp.process("2*t^2[2*t]")); + + CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("k", (String) null)); + Assert.assertEquals("(t+2k)[k+2t]", tp.process("(t+2*k)*[k+2*t]")); + Assert.assertEquals("(te+2k)e[k+2te]", tp.process("(t*e+2*k)*e*[k+2*t*e]")); + + + Assert.assertEquals("tlog(3)", tp.process("t*log(3)")); + Assert.assertEquals("t√(3)", tp.process("t*√(3)")); + Assert.assertEquals("20x", tp.process("20*x")); + Assert.assertEquals("20x", tp.process("20x")); + Assert.assertEquals("2×0x3", tp.process("2*0x3")); + Assert.assertEquals("2×0x:3", tp.process("2*0x:3")); + } +} 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 6eb893c1..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 @@ -10,6 +10,8 @@ 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; @@ -98,11 +100,11 @@ public class NumeralBaseTest { 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 decResult = CalculatorEngine.instance.evaluate(JsclOperation.numeric, decExpression).getStringResult(); final String hexExpression = converter.convert(hex); - final String hexResult = CalculatorEngine.instance.evaluate(JsclOperation.numeric, hexExpression).getResult(); + final String hexResult = CalculatorEngine.instance.evaluate(JsclOperation.numeric, hexExpression).getStringResult(); final String binExpression = converter.convert(bin); - final String binResult = CalculatorEngine.instance.evaluate(JsclOperation.numeric, binExpression).getResult(); + final String binResult = CalculatorEngine.instance.evaluate(JsclOperation.numeric, binExpression).getStringResult(); Assert.assertEquals("dec-hex: " + decExpression + " : " + hexExpression, decResult, hexResult); Assert.assertEquals("dec-bin: " + decExpression + " : " + binExpression, decResult, binResult); diff --git a/calculatorpp/src/test/java/org/solovyev/android/calculator/model/ToJsclTextProcessorTest.java b/calculatorpp/src/test/java/org/solovyev/android/calculator/model/ToJsclTextProcessorTest.java index e1f92a65..05c4e995 100644 --- a/calculatorpp/src/test/java/org/solovyev/android/calculator/model/ToJsclTextProcessorTest.java +++ b/calculatorpp/src/test/java/org/solovyev/android/calculator/model/ToJsclTextProcessorTest.java @@ -1,161 +1,165 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - * or visit http://se.solovyev.org - */ - -package org.solovyev.android.calculator.model; - -import jscl.JsclMathEngine; -import jscl.NumeralBase; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; - -/** - * User: serso - * Date: 9/26/11 - * Time: 12:13 PM - */ -public class ToJsclTextProcessorTest { - - @BeforeClass - public static void setUp() throws Exception { - CalculatorEngine.instance.init(null, null); - } - - @Test - public void testSpecialCases() throws CalculatorParseException { - final TextProcessor preprocessor = ToJsclTextProcessor.getInstance(); - Assert.assertEquals( "3^E10", preprocessor.process("3^E10").toString()); - } - - @Test - public void testProcess() throws Exception { - final TextProcessor preprocessor = ToJsclTextProcessor.getInstance(); - - Assert.assertEquals( "", preprocessor.process("").toString()); - Assert.assertEquals( "()", preprocessor.process("[]").toString()); - Assert.assertEquals( "()*()", preprocessor.process("[][]").toString()); - Assert.assertEquals( "()*(1)", preprocessor.process("[][1]").toString()); - Assert.assertEquals( "(0)*(1)", preprocessor.process("[0][1]").toString()); - Assert.assertEquals( "(0)*(1E)", preprocessor.process("[0][1E]").toString()); - Assert.assertEquals( "(0)*(1E1)", preprocessor.process("[0][1E1]").toString()); - Assert.assertEquals( "(0)*(1E-1)", preprocessor.process("[0][1E-1]").toString()); - Assert.assertEquals( "(0)*(1.E-1)", preprocessor.process("[0][1.E-1]").toString()); - Assert.assertEquals( "(0)*(2*E-1)", preprocessor.process("[0][2*E-1]").toString()); - Assert.assertEquals( "(0)*ln(1)*(2*E-1)", preprocessor.process("[0]ln(1)[2*E-1]").toString()); - Assert.assertEquals( "sin(4)*asin(0.5)*√(2)", preprocessor.process("sin(4)asin(0.5)√(2)").toString()); - Assert.assertEquals( "sin(4)*cos(5)", preprocessor.process("sin(4)cos(5)").toString()); - Assert.assertEquals( "π*sin(4)*π*cos(√(5))", preprocessor.process("πsin(4)πcos(√(5))").toString()); - Assert.assertEquals( "π*sin(4)+π*cos(√(5))", preprocessor.process("πsin(4)+πcos(√(5))").toString()); - Assert.assertEquals( "π*sin(4)+π*cos(√(5+(√(-1))))", preprocessor.process("πsin(4)+πcos(√(5+i))").toString()); - Assert.assertEquals( "π*sin(4.01)+π*cos(√(5+(√(-1))))", preprocessor.process("πsin(4.01)+πcos(√(5+i))").toString()); - Assert.assertEquals( "e^π*sin(4.01)+π*cos(√(5+(√(-1))))", preprocessor.process("e^πsin(4.01)+πcos(√(5+i))").toString()); - Assert.assertEquals( "e^π*sin(4.01)+π*cos(√(5+(√(-1))))E2", preprocessor.process("e^πsin(4.01)+πcos(√(5+i))E2").toString()); - Assert.assertEquals( "e^π*sin(4.01)+π*cos(√(5+(√(-1))))E-2", preprocessor.process("e^πsin(4.01)+πcos(√(5+i))E-2").toString()); - Assert.assertEquals( "E2", preprocessor.process("E2").toString()); - Assert.assertEquals( "E-2", preprocessor.process("E-2").toString()); - Assert.assertEquals( "E-1/2", preprocessor.process("E-1/2").toString()); - Assert.assertEquals( "E-1.2", preprocessor.process("E-1.2").toString()); - Assert.assertEquals( "E+1.2", preprocessor.process("E+1.2").toString()); - Assert.assertEquals( "E(-1.2)", preprocessor.process("E(-1.2)").toString()); - Assert.assertEquals( "EE", preprocessor.process("EE").toString()); - - try { - CalculatorEngine.instance.getEngine().setNumeralBase(NumeralBase.hex); - Assert.assertEquals( "22F*exp(F)", preprocessor.process("22Fexp(F)").toString()); - } finally { - CalculatorEngine.instance.getEngine().setNumeralBase(NumeralBase.dec); - } - Assert.assertEquals( "0x:ABCDEF", preprocessor.process("0x:ABCDEF").toString()); - Assert.assertEquals( "0x:ABCDEF", preprocessor.process("0x:A BC DEF").toString()); - Assert.assertEquals( "0x:ABCDEF", preprocessor.process("0x:A BC DEF").toString()); - Assert.assertEquals( "0x:ABCDEF*0*x", preprocessor.process("0x:A BC DEF*0x").toString()); - Assert.assertEquals( "0x:ABCDEF001*0*x", preprocessor.process("0x:A BC DEF001*0x").toString()); - Assert.assertEquals( "0x:ABCDEF001*0*c", preprocessor.process("0x:A BC DEF001*0c").toString()); - Assert.assertEquals( "0x:ABCDEF001*c", preprocessor.process("0x:A BC DEF001*c").toString()); - Assert.assertEquals( "0b:1101", preprocessor.process("0b:1101").toString()); - Assert.assertEquals( "0x:1C", preprocessor.process("0x:1C").toString()); - Assert.assertEquals( "0x:1C", preprocessor.process(" 0x:1C").toString()); - Assert.assertEquals( "0x:1C*0x:1C*sin(0x:1C)-0b:1101+√(0x:1C)+exp(0x:1C)", preprocessor.process("0x:1C*0x:1C * sin(0x:1C) - 0b:1101 + √(0x:1C) + exp ( 0x:1C)").toString()); - Assert.assertEquals( "0x:1C*0x:1C*sin(0x:1C)-0b:1101+√(0x:1C)+exp(0x:1C)", preprocessor.process("0x:1C*0x:1C * sin(0x:1C) - 0b:1101 + √(0x:1C) + exp ( 0x:1C)").toString()); - - try { - preprocessor.process("ln()"); - Assert.fail(); - } catch (CalculatorParseException e) { - } - try { - preprocessor.process("ln()ln()"); - Assert.fail(); - } catch (CalculatorParseException e) { - } - - try { - preprocessor.process("eln()eln()ln()ln()ln()e"); - Assert.fail(); - } catch (CalculatorParseException e) { - } - - try { - preprocessor.process("ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln()))))))))))))))"); - Assert.fail(); - } catch (CalculatorParseException e) { - } - - try { - preprocessor.process("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) { - } - } - - @Test - public void testDegrees() throws Exception { - final TextProcessor preprocessor = ToJsclTextProcessor.getInstance(); - - Assert.assertEquals( "", preprocessor.process("").toString()); - /* try { - Assert.assertEquals( "π/180", preprocessor.process("°").toString()); - } catch (ParseException e) { - if ( !e.getMessage().startsWith("Could not find start of prefix") ){ - junit.framework.Assert.fail(); - } - } - Assert.assertEquals( "1*π/180", preprocessor.process("1°").toString()); - Assert.assertEquals( "20.0*π/180", preprocessor.process("20.0°").toString()); - Assert.assertEquals( "sin(30*π/180)", preprocessor.process("sin(30°)").toString()); - Assert.assertEquals( "asin(sin(π/6))*π/180", preprocessor.process("asin(sin(π/6))°").toString()); - Assert.assertEquals( "1*π/180*sin(1)", preprocessor.process("1°sin(1)").toString()); - try { - Assert.assertEquals( "1*π/180^sin(1)", preprocessor.process("1°^sin(1)").toString()); - junit.framework.Assert.fail(); - } catch (ParseException e) { - if ( !e.getMessage().equals("Power operation after postfix function is currently unsupported!") ) { - junit.framework.Assert.fail(); - } - }*/ - - } - - @Test - public void testPostfixFunction() throws Exception { - } - - @Test - public void testNumeralBases() throws Exception { - final TextProcessor processor = ToJsclTextProcessor.getInstance(); - - final NumeralBase defaultNumeralBase = JsclMathEngine.instance.getNumeralBase(); - try{ - JsclMathEngine.instance.setNumeralBase(NumeralBase.bin); - Assert.assertEquals("101", JsclMathEngine.instance.evaluate("10+11")); - - JsclMathEngine.instance.setNumeralBase(NumeralBase.hex); - Assert.assertEquals("56CE+CAD", processor.process("56CE+CAD").getExpression()); - } finally { - JsclMathEngine.instance.setNumeralBase(defaultNumeralBase); - } - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + * or visit http://se.solovyev.org + */ + +package org.solovyev.android.calculator.model; + +import jscl.JsclMathEngine; +import jscl.NumeralBase; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.solovyev.android.calculator.CalculatorParseException; +import org.solovyev.android.calculator.PreparedExpression; +import org.solovyev.android.calculator.ToJsclTextProcessor; +import org.solovyev.android.calculator.text.TextProcessor; + +/** + * User: serso + * Date: 9/26/11 + * Time: 12:13 PM + */ +public class ToJsclTextProcessorTest { + + @BeforeClass + public static void setUp() throws Exception { + CalculatorEngine.instance.init(null, null); + } + + @Test + public void testSpecialCases() throws CalculatorParseException { + final TextProcessor preprocessor = ToJsclTextProcessor.getInstance(); + Assert.assertEquals( "3^E10", preprocessor.process("3^E10").toString()); + } + + @Test + public void testProcess() throws Exception { + final TextProcessor preprocessor = ToJsclTextProcessor.getInstance(); + + Assert.assertEquals( "", preprocessor.process("").toString()); + Assert.assertEquals( "()", preprocessor.process("[]").toString()); + Assert.assertEquals( "()*()", preprocessor.process("[][]").toString()); + Assert.assertEquals( "()*(1)", preprocessor.process("[][1]").toString()); + Assert.assertEquals( "(0)*(1)", preprocessor.process("[0][1]").toString()); + Assert.assertEquals( "(0)*(1E)", preprocessor.process("[0][1E]").toString()); + Assert.assertEquals( "(0)*(1E1)", preprocessor.process("[0][1E1]").toString()); + Assert.assertEquals( "(0)*(1E-1)", preprocessor.process("[0][1E-1]").toString()); + Assert.assertEquals( "(0)*(1.E-1)", preprocessor.process("[0][1.E-1]").toString()); + Assert.assertEquals( "(0)*(2*E-1)", preprocessor.process("[0][2*E-1]").toString()); + Assert.assertEquals( "(0)*ln(1)*(2*E-1)", preprocessor.process("[0]ln(1)[2*E-1]").toString()); + Assert.assertEquals( "sin(4)*asin(0.5)*√(2)", preprocessor.process("sin(4)asin(0.5)√(2)").toString()); + Assert.assertEquals( "sin(4)*cos(5)", preprocessor.process("sin(4)cos(5)").toString()); + Assert.assertEquals( "π*sin(4)*π*cos(√(5))", preprocessor.process("πsin(4)πcos(√(5))").toString()); + Assert.assertEquals( "π*sin(4)+π*cos(√(5))", preprocessor.process("πsin(4)+πcos(√(5))").toString()); + Assert.assertEquals( "π*sin(4)+π*cos(√(5+(√(-1))))", preprocessor.process("πsin(4)+πcos(√(5+i))").toString()); + Assert.assertEquals( "π*sin(4.01)+π*cos(√(5+(√(-1))))", preprocessor.process("πsin(4.01)+πcos(√(5+i))").toString()); + Assert.assertEquals( "e^π*sin(4.01)+π*cos(√(5+(√(-1))))", preprocessor.process("e^πsin(4.01)+πcos(√(5+i))").toString()); + Assert.assertEquals( "e^π*sin(4.01)+π*cos(√(5+(√(-1))))E2", preprocessor.process("e^πsin(4.01)+πcos(√(5+i))E2").toString()); + Assert.assertEquals( "e^π*sin(4.01)+π*cos(√(5+(√(-1))))E-2", preprocessor.process("e^πsin(4.01)+πcos(√(5+i))E-2").toString()); + Assert.assertEquals( "E2", preprocessor.process("E2").toString()); + Assert.assertEquals( "E-2", preprocessor.process("E-2").toString()); + Assert.assertEquals( "E-1/2", preprocessor.process("E-1/2").toString()); + Assert.assertEquals( "E-1.2", preprocessor.process("E-1.2").toString()); + Assert.assertEquals( "E+1.2", preprocessor.process("E+1.2").toString()); + Assert.assertEquals( "E(-1.2)", preprocessor.process("E(-1.2)").toString()); + Assert.assertEquals( "EE", preprocessor.process("EE").toString()); + + try { + CalculatorEngine.instance.getEngine().setNumeralBase(NumeralBase.hex); + Assert.assertEquals( "22F*exp(F)", preprocessor.process("22Fexp(F)").toString()); + } finally { + CalculatorEngine.instance.getEngine().setNumeralBase(NumeralBase.dec); + } + Assert.assertEquals( "0x:ABCDEF", preprocessor.process("0x:ABCDEF").toString()); + Assert.assertEquals( "0x:ABCDEF", preprocessor.process("0x:A BC DEF").toString()); + Assert.assertEquals( "0x:ABCDEF", preprocessor.process("0x:A BC DEF").toString()); + Assert.assertEquals( "0x:ABCDEF*0*x", preprocessor.process("0x:A BC DEF*0x").toString()); + Assert.assertEquals( "0x:ABCDEF001*0*x", preprocessor.process("0x:A BC DEF001*0x").toString()); + Assert.assertEquals( "0x:ABCDEF001*0*c", preprocessor.process("0x:A BC DEF001*0c").toString()); + Assert.assertEquals( "0x:ABCDEF001*c", preprocessor.process("0x:A BC DEF001*c").toString()); + Assert.assertEquals( "0b:1101", preprocessor.process("0b:1101").toString()); + Assert.assertEquals( "0x:1C", preprocessor.process("0x:1C").toString()); + Assert.assertEquals( "0x:1C", preprocessor.process(" 0x:1C").toString()); + Assert.assertEquals( "0x:1C*0x:1C*sin(0x:1C)-0b:1101+√(0x:1C)+exp(0x:1C)", preprocessor.process("0x:1C*0x:1C * sin(0x:1C) - 0b:1101 + √(0x:1C) + exp ( 0x:1C)").toString()); + Assert.assertEquals( "0x:1C*0x:1C*sin(0x:1C)-0b:1101+√(0x:1C)+exp(0x:1C)", preprocessor.process("0x:1C*0x:1C * sin(0x:1C) - 0b:1101 + √(0x:1C) + exp ( 0x:1C)").toString()); + + try { + preprocessor.process("ln()"); + Assert.fail(); + } catch (CalculatorParseException e) { + } + try { + preprocessor.process("ln()ln()"); + Assert.fail(); + } catch (CalculatorParseException e) { + } + + try { + preprocessor.process("eln()eln()ln()ln()ln()e"); + Assert.fail(); + } catch (CalculatorParseException e) { + } + + try { + preprocessor.process("ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln()))))))))))))))"); + Assert.fail(); + } catch (CalculatorParseException e) { + } + + try { + preprocessor.process("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) { + } + } + + @Test + public void testDegrees() throws Exception { + final TextProcessor preprocessor = ToJsclTextProcessor.getInstance(); + + Assert.assertEquals( "", preprocessor.process("").toString()); + /* try { + Assert.assertEquals( "π/180", preprocessor.process("°").toString()); + } catch (ParseException e) { + if ( !e.getMessage().startsWith("Could not find start of prefix") ){ + junit.framework.Assert.fail(); + } + } + Assert.assertEquals( "1*π/180", preprocessor.process("1°").toString()); + Assert.assertEquals( "20.0*π/180", preprocessor.process("20.0°").toString()); + Assert.assertEquals( "sin(30*π/180)", preprocessor.process("sin(30°)").toString()); + Assert.assertEquals( "asin(sin(π/6))*π/180", preprocessor.process("asin(sin(π/6))°").toString()); + Assert.assertEquals( "1*π/180*sin(1)", preprocessor.process("1°sin(1)").toString()); + try { + Assert.assertEquals( "1*π/180^sin(1)", preprocessor.process("1°^sin(1)").toString()); + junit.framework.Assert.fail(); + } catch (ParseException e) { + if ( !e.getMessage().equals("Power operation after postfix function is currently unsupported!") ) { + junit.framework.Assert.fail(); + } + }*/ + + } + + @Test + public void testPostfixFunction() throws Exception { + } + + @Test + public void testNumeralBases() throws Exception { + final TextProcessor processor = ToJsclTextProcessor.getInstance(); + + final NumeralBase defaultNumeralBase = JsclMathEngine.instance.getNumeralBase(); + try{ + JsclMathEngine.instance.setNumeralBase(NumeralBase.bin); + Assert.assertEquals("101", JsclMathEngine.instance.evaluate("10+11")); + + JsclMathEngine.instance.setNumeralBase(NumeralBase.hex); + Assert.assertEquals("56CE+CAD", processor.process("56CE+CAD").getExpression()); + } finally { + JsclMathEngine.instance.setNumeralBase(defaultNumeralBase); + } + } +} diff --git a/pom.xml b/pom.xml index a4352372..61bbd3f4 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,7 @@ calculatorpp calculatorpp-service calculatorpp-test + calculatorpp-core @@ -71,6 +72,18 @@ 1.0.0 + + org.solovyev + jscl + 0.0.2 + + + xercesImpl + xerces + + + + org.solovyev.android android-common-other @@ -81,7 +94,13 @@ junit junit - 4.8.1 + 4.8.2 + + + + com.intellij + annotations + 7.0.3 @@ -103,6 +122,22 @@ 11.0.2 + + org.simpleframework + simple-xml + 2.6.1 + + + stax-api + stax + + + xpp3 + xpp3 + + + +