From 33db715776a198fc62b24c90a7321c2f46749fcf Mon Sep 17 00:00:00 2001 From: serso Date: Thu, 20 Sep 2012 12:07:55 +0400 Subject: [PATCH 1/6] Calculator++ core module --- calculatorpp-core/pom.xml | 36 + .../java/org/solovyev/math/units}/Unit.java | 34 +- .../solovyev/math/units}/UnitConverter.java | 84 +-- .../org/solovyev/math/units}/UnitImpl.java | 84 +-- .../org/solovyev/math/units}/UnitType.java | 32 +- calculatorpp-service/pom.xml | 69 +- calculatorpp/pom.xml | 709 +++++++++--------- .../calculator/AndroidNumeralBase.java | 316 ++++---- .../view/NumeralBaseConverterDialog.java | 198 ++--- .../view/UnitConverterViewBuilder.java | 518 ++++++------- .../android/AndroidNumeralBaseTest.java | 142 ++-- pom.xml | 9 +- 12 files changed, 1140 insertions(+), 1091 deletions(-) create mode 100644 calculatorpp-core/pom.xml rename {calculatorpp/src/main/java/org/solovyev/android => calculatorpp-core/src/main/java/org/solovyev/math/units}/Unit.java (80%) rename {calculatorpp/src/main/java/org/solovyev/android => calculatorpp-core/src/main/java/org/solovyev/math/units}/UnitConverter.java (92%) rename {calculatorpp/src/main/java/org/solovyev/android => calculatorpp-core/src/main/java/org/solovyev/math/units}/UnitImpl.java (90%) rename {calculatorpp/src/main/java/org/solovyev/android => calculatorpp-core/src/main/java/org/solovyev/math/units}/UnitType.java (81%) diff --git a/calculatorpp-core/pom.xml b/calculatorpp-core/pom.xml new file mode 100644 index 00000000..7f59b53d --- /dev/null +++ b/calculatorpp-core/pom.xml @@ -0,0 +1,36 @@ + + + 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 + + + + + + \ No newline at end of file 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-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..ea6c981a 100644 --- a/calculatorpp/pom.xml +++ b/calculatorpp/pom.xml @@ -1,353 +1,358 @@ - - - - - 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 + 0.0.2 + + + xercesImpl + xerces + + + + + + + + com.google.android + android + provided + + + + 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 + + + + 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/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/view/NumeralBaseConverterDialog.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/view/NumeralBaseConverterDialog.java index 2dfa459f..a8fa7a46 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.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); + } +} 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..d75c7240 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 @@ -1,259 +1,259 @@ -package org.solovyev.android.calculator.view; - -import android.app.Activity; -import android.content.Context; -import android.text.ClipboardManager; -import android.text.Editable; -import android.text.TextWatcher; -import android.view.View; -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.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; - -/** - * User: serso - * Date: 4/20/12 - * Time: 4:50 PM - */ -public class UnitConverterViewBuilder implements ViewBuilder { - - @NotNull - private List> fromUnitTypes = Collections.emptyList(); - - @NotNull - private List> toUnitTypes = Collections.emptyList(); - - @Nullable - private Unit fromValue; - - @NotNull - private UnitConverter converter = UnitConverter.Dummy.getInstance(); - - @Nullable - private View.OnClickListener okButtonOnClickListener; - - @Nullable - private CustomButtonData customButtonData; - - public void setFromUnitTypes(@NotNull List> fromUnitTypes) { - this.fromUnitTypes = fromUnitTypes; - } - - public void setToUnitTypes(@NotNull List> toUnitTypes) { - this.toUnitTypes = toUnitTypes; - } - - public void setFromValue(@Nullable Unit fromValue) { - this.fromValue = fromValue; - } - - public void setConverter(@NotNull UnitConverter converter) { - this.converter = converter; - } - - public void setOkButtonOnClickListener(@Nullable View.OnClickListener okButtonOnClickListener) { - this.okButtonOnClickListener = okButtonOnClickListener; - } - - public void setCustomButtonData(@Nullable CustomButtonData customButtonData) { - this.customButtonData = customButtonData; - } - - @NotNull - @Override - public View build(@NotNull final Context context) { - final View main = ViewFromLayoutBuilder.newInstance(R.layout.unit_converter).build(context); - - final Spinner fromSpinner = (Spinner) main.findViewById(R.id.unit_types_from); - final EditText fromEditText = (EditText) main.findViewById(R.id.units_from); - fromEditText.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - - @Override - public void afterTextChanged(Editable s) { - doConversion(main, context, UnitConverterViewBuilder.this.converter); - } - }); - - fillSpinner(main, context, R.id.unit_types_from, fromUnitTypes); - fillSpinner(main, context, R.id.unit_types_to, toUnitTypes); - - if (fromValue != null) { - fromEditText.setText(fromValue.getValue()); - - int i = fromUnitTypes.indexOf(fromValue.getUnitType()); - if ( i >= 0 ) { - fromSpinner.setSelection(i); - } - } - - final Button copyButton = (Button) main.findViewById(R.id.unit_converter_copy_button); - copyButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - final EditText toEditText = (EditText) main.findViewById(R.id.units_to); - - final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE); - clipboard.setText(toEditText.getText().toString()); - Toast.makeText(context, context.getText(R.string.c_result_copied), Toast.LENGTH_SHORT).show(); - } - }); - - final Button okButton = (Button) main.findViewById(R.id.unit_converter_ok_button); - if ( okButtonOnClickListener == null ) { - ((ViewGroup) okButton.getParent()).removeView(okButton); - } else { - okButton.setOnClickListener(this.okButtonOnClickListener); - } - - final Button customButton = (Button) main.findViewById(R.id.unit_converter_custom_button); - if ( customButtonData == null ) { - ((ViewGroup) customButton.getParent()).removeView(customButton); - } else { - customButton.setText(customButtonData.text); - customButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - customButtonData.clickListener.onClick(getFromUnit(main), getToUnit(main)); - } - }); - } - - - - return main; - } - - private void fillSpinner(@NotNull final View main, - @NotNull final Context context, - final int spinnerId, - @NotNull List> unitTypes) { - final Spinner spinner = (Spinner) main.findViewById(spinnerId); - - final ArrayAdapter> adapter = new ArrayAdapter>(context, android.R.layout.simple_spinner_item); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - for (UnitType fromUnitType : unitTypes) { - adapter.add(fromUnitType); - } - spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - doConversion(main, context, UnitConverterViewBuilder.this.converter); - } - - @Override - public void onNothingSelected(AdapterView parent) { - } - }); - spinner.setAdapter(adapter); - } - - private static void doConversion(@NotNull View main, @NotNull Context context, @NotNull UnitConverter converter) { - final EditText fromEditText = (EditText) main.findViewById(R.id.units_from); - - final EditText toEditText = (EditText) main.findViewById(R.id.units_to); - - final String from = fromEditText.getText().toString(); - try { - toEditText.setText(doConversion(converter, from, getFromUnitType(main), getToUnitType(main))); - } catch (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); - return UnitImpl.newInstance(toUnits.getText().toString(), getToUnitType(main)); - } - - @NotNull - private static UnitType getToUnitType(@NotNull View main) { - final Spinner toSpinner = (Spinner) main.findViewById(R.id.unit_types_to); - return (UnitType) toSpinner.getSelectedItem(); - } - - @NotNull - private static Unit getFromUnit(@NotNull View main) { - final EditText fromUnits = (EditText) main.findViewById(R.id.units_from); - return UnitImpl.newInstance(fromUnits.getText().toString(), getFromUnitType(main)); - } - - @NotNull - private static UnitType getFromUnitType(@NotNull View main) { - final Spinner fromSpinner = (Spinner) main.findViewById(R.id.unit_types_from); - return (UnitType) fromSpinner.getSelectedItem(); - } - - public static class CustomButtonData { - - @NotNull - private String text; - - @NotNull - private CustomButtonOnClickListener clickListener; - - - public CustomButtonData(@NotNull String text, @NotNull CustomButtonOnClickListener clickListener) { - this.text = text; - this.clickListener = clickListener; - } - } - - public static interface CustomButtonOnClickListener { - void onClick(@NotNull Unit fromUnits, @NotNull Unit toUnits); - } -} +package org.solovyev.android.calculator.view; + +import android.app.Activity; +import android.content.Context; +import android.text.ClipboardManager; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.View; +import android.view.ViewGroup; +import android.widget.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +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; + +/** + * User: serso + * Date: 4/20/12 + * Time: 4:50 PM + */ +public class UnitConverterViewBuilder implements ViewBuilder { + + @NotNull + private List> fromUnitTypes = Collections.emptyList(); + + @NotNull + private List> toUnitTypes = Collections.emptyList(); + + @Nullable + private Unit fromValue; + + @NotNull + private UnitConverter converter = UnitConverter.Dummy.getInstance(); + + @Nullable + private View.OnClickListener okButtonOnClickListener; + + @Nullable + private CustomButtonData customButtonData; + + public void setFromUnitTypes(@NotNull List> fromUnitTypes) { + this.fromUnitTypes = fromUnitTypes; + } + + public void setToUnitTypes(@NotNull List> toUnitTypes) { + this.toUnitTypes = toUnitTypes; + } + + public void setFromValue(@Nullable Unit fromValue) { + this.fromValue = fromValue; + } + + public void setConverter(@NotNull UnitConverter converter) { + this.converter = converter; + } + + public void setOkButtonOnClickListener(@Nullable View.OnClickListener okButtonOnClickListener) { + this.okButtonOnClickListener = okButtonOnClickListener; + } + + public void setCustomButtonData(@Nullable CustomButtonData customButtonData) { + this.customButtonData = customButtonData; + } + + @NotNull + @Override + public View build(@NotNull final Context context) { + final View main = ViewFromLayoutBuilder.newInstance(R.layout.unit_converter).build(context); + + final Spinner fromSpinner = (Spinner) main.findViewById(R.id.unit_types_from); + final EditText fromEditText = (EditText) main.findViewById(R.id.units_from); + fromEditText.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + + @Override + public void afterTextChanged(Editable s) { + doConversion(main, context, UnitConverterViewBuilder.this.converter); + } + }); + + fillSpinner(main, context, R.id.unit_types_from, fromUnitTypes); + fillSpinner(main, context, R.id.unit_types_to, toUnitTypes); + + if (fromValue != null) { + fromEditText.setText(fromValue.getValue()); + + int i = fromUnitTypes.indexOf(fromValue.getUnitType()); + if ( i >= 0 ) { + fromSpinner.setSelection(i); + } + } + + final Button copyButton = (Button) main.findViewById(R.id.unit_converter_copy_button); + copyButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + final EditText toEditText = (EditText) main.findViewById(R.id.units_to); + + final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE); + clipboard.setText(toEditText.getText().toString()); + Toast.makeText(context, context.getText(R.string.c_result_copied), Toast.LENGTH_SHORT).show(); + } + }); + + final Button okButton = (Button) main.findViewById(R.id.unit_converter_ok_button); + if ( okButtonOnClickListener == null ) { + ((ViewGroup) okButton.getParent()).removeView(okButton); + } else { + okButton.setOnClickListener(this.okButtonOnClickListener); + } + + final Button customButton = (Button) main.findViewById(R.id.unit_converter_custom_button); + if ( customButtonData == null ) { + ((ViewGroup) customButton.getParent()).removeView(customButton); + } else { + customButton.setText(customButtonData.text); + customButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + customButtonData.clickListener.onClick(getFromUnit(main), getToUnit(main)); + } + }); + } + + + + return main; + } + + private void fillSpinner(@NotNull final View main, + @NotNull final Context context, + final int spinnerId, + @NotNull List> unitTypes) { + final Spinner spinner = (Spinner) main.findViewById(spinnerId); + + final ArrayAdapter> adapter = new ArrayAdapter>(context, android.R.layout.simple_spinner_item); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + for (UnitType fromUnitType : unitTypes) { + adapter.add(fromUnitType); + } + spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + doConversion(main, context, UnitConverterViewBuilder.this.converter); + } + + @Override + public void onNothingSelected(AdapterView parent) { + } + }); + spinner.setAdapter(adapter); + } + + private static void doConversion(@NotNull View main, @NotNull Context context, @NotNull UnitConverter converter) { + final EditText fromEditText = (EditText) main.findViewById(R.id.units_from); + + final EditText toEditText = (EditText) main.findViewById(R.id.units_to); + + final String from = fromEditText.getText().toString(); + try { + toEditText.setText(doConversion(converter, from, getFromUnitType(main), getToUnitType(main))); + } catch (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); + return UnitImpl.newInstance(toUnits.getText().toString(), getToUnitType(main)); + } + + @NotNull + private static UnitType getToUnitType(@NotNull View main) { + final Spinner toSpinner = (Spinner) main.findViewById(R.id.unit_types_to); + return (UnitType) toSpinner.getSelectedItem(); + } + + @NotNull + private static Unit getFromUnit(@NotNull View main) { + final EditText fromUnits = (EditText) main.findViewById(R.id.units_from); + return UnitImpl.newInstance(fromUnits.getText().toString(), getFromUnitType(main)); + } + + @NotNull + private static UnitType getFromUnitType(@NotNull View main) { + final Spinner fromSpinner = (Spinner) main.findViewById(R.id.unit_types_from); + return (UnitType) fromSpinner.getSelectedItem(); + } + + public static class CustomButtonData { + + @NotNull + private String text; + + @NotNull + private CustomButtonOnClickListener clickListener; + + + public CustomButtonData(@NotNull String text, @NotNull CustomButtonOnClickListener clickListener) { + this.text = text; + this.clickListener = clickListener; + } + } + + public static interface CustomButtonOnClickListener { + void onClick(@NotNull Unit fromUnits, @NotNull Unit toUnits); + } +} 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/pom.xml b/pom.xml index a4352372..93bf95b9 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,7 @@ calculatorpp calculatorpp-service calculatorpp-test + calculatorpp-core @@ -81,7 +82,13 @@ junit junit - 4.8.1 + 4.8.2 + + + + com.intellij + annotations + 7.0.3 From 417cf88912e733116fcc4783bd87f25d2ed4c1a4 Mon Sep 17 00:00:00 2001 From: serso Date: Thu, 20 Sep 2012 12:59:21 +0400 Subject: [PATCH 2/6] Module separation --- calculatorpp-core/pom.xml | 5 + .../calculator/CalculatorEngineControl.java | 38 +- .../android/calculator/CalculatorLocator.java | 16 + .../calculator/CalculatorLocatorImpl.java | 36 + .../solovyev/android/calculator}/Editor.java | 54 +- .../calculator/ICalculatorDisplay.java | 80 +- .../android/calculator/JCalculatorEngine.java | 34 + .../jscl/FromJsclNumericTextProcessor.java | 56 +- .../calculator/jscl/JsclOperation.java | 140 +-- .../android/calculator/math/MathType.java | 893 +++++++++--------- .../model/CalculatorParseException.java | 13 +- .../calculator/model/DummyTextProcessor.java | 52 +- .../model/FromJsclSimplifyTextProcessor.java | 186 ++-- .../calculator/model/TextProcessor.java | 28 +- calculatorpp/pom.xml | 7 - .../calculator/CalculatorApplication.java | 186 ++-- .../history/CalculatorHistoryState.java | 221 ++--- .../history/EditorHistoryState.java | 175 ++-- .../history/TextViewEditorAdapter.java | 99 +- .../calculator/model/CalculatorEngine.java | 35 +- .../calculator/model/ToJsclTextProcessor.java | 8 +- .../calculator/history/HistoryUtilsTest.java | 641 ++++++------- pom.xml | 12 + 23 files changed, 1567 insertions(+), 1448 deletions(-) rename {calculatorpp => calculatorpp-core}/src/main/java/org/solovyev/android/calculator/CalculatorEngineControl.java (94%) create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorLocator.java create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorLocatorImpl.java rename {calculatorpp/src/main/java/org/solovyev/android/calculator/history => calculatorpp-core/src/main/java/org/solovyev/android/calculator}/Editor.java (86%) rename {calculatorpp => calculatorpp-core}/src/main/java/org/solovyev/android/calculator/ICalculatorDisplay.java (90%) create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/JCalculatorEngine.java rename {calculatorpp => calculatorpp-core}/src/main/java/org/solovyev/android/calculator/jscl/FromJsclNumericTextProcessor.java (96%) rename {calculatorpp => calculatorpp-core}/src/main/java/org/solovyev/android/calculator/jscl/JsclOperation.java (64%) rename {calculatorpp => calculatorpp-core}/src/main/java/org/solovyev/android/calculator/math/MathType.java (92%) rename {calculatorpp => calculatorpp-core}/src/main/java/org/solovyev/android/calculator/model/CalculatorParseException.java (73%) rename {calculatorpp => calculatorpp-core}/src/main/java/org/solovyev/android/calculator/model/DummyTextProcessor.java (95%) rename {calculatorpp => calculatorpp-core}/src/main/java/org/solovyev/android/calculator/model/FromJsclSimplifyTextProcessor.java (88%) rename {calculatorpp => calculatorpp-core}/src/main/java/org/solovyev/android/calculator/model/TextProcessor.java (95%) diff --git a/calculatorpp-core/pom.xml b/calculatorpp-core/pom.xml index 7f59b53d..22f023e6 100644 --- a/calculatorpp-core/pom.xml +++ b/calculatorpp-core/pom.xml @@ -30,6 +30,11 @@ annotations + + org.solovyev + jscl + + 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-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..5b74bde7 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorLocator.java @@ -0,0 +1,16 @@ +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 12:45 + */ +public interface CalculatorLocator { + + @NotNull + JCalculatorEngine getCalculatorEngine(); + + void setCalculatorEngine(@NotNull JCalculatorEngine calculatorEngine); +} 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..6cee9b7c --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorLocatorImpl.java @@ -0,0 +1,36 @@ +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 12:45 + */ +public class CalculatorLocatorImpl implements CalculatorLocator { + + @NotNull + private JCalculatorEngine calculatorEngine; + + @NotNull + private static final CalculatorLocator instance = new CalculatorLocatorImpl(); + + private CalculatorLocatorImpl() { + } + + @NotNull + public static CalculatorLocator getInstance() { + return instance; + } + + @NotNull + @Override + public JCalculatorEngine getCalculatorEngine() { + return calculatorEngine; + } + + @Override + public void setCalculatorEngine(@NotNull JCalculatorEngine calculatorEngine) { + this.calculatorEngine = calculatorEngine; + } +} 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/src/main/java/org/solovyev/android/calculator/ICalculatorDisplay.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/ICalculatorDisplay.java similarity index 90% rename from calculatorpp/src/main/java/org/solovyev/android/calculator/ICalculatorDisplay.java rename to calculatorpp-core/src/main/java/org/solovyev/android/calculator/ICalculatorDisplay.java index 1c0b892f..96a61018 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/ICalculatorDisplay.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/ICalculatorDisplay.java @@ -1,40 +1,40 @@ -/* - * 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(); -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + * or visit http://se.solovyev.org + */ + +package org.solovyev.android.calculator; + +import jscl.math.Generic; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.calculator.Editor; +import org.solovyev.android.calculator.jscl.JsclOperation; + +/** + * User: serso + * Date: 12/17/11 + * Time: 9:45 PM + */ +public interface 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-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/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 96% 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..b664c9ff 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.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("*", ""); + } +} 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 64% 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..f3a21199 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.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 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 92% 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..4aa60280 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.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()) { + 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/CalculatorParseException.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/model/CalculatorParseException.java similarity index 73% rename from calculatorpp/src/main/java/org/solovyev/android/calculator/model/CalculatorParseException.java rename to calculatorpp-core/src/main/java/org/solovyev/android/calculator/model/CalculatorParseException.java index e164578f..157a6c82 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/CalculatorParseException.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/model/CalculatorParseException.java @@ -6,10 +6,8 @@ package org.solovyev.android.calculator.model; -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/model/DummyTextProcessor.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/model/DummyTextProcessor.java similarity index 95% rename from calculatorpp/src/main/java/org/solovyev/android/calculator/model/DummyTextProcessor.java rename to calculatorpp-core/src/main/java/org/solovyev/android/calculator/model/DummyTextProcessor.java index 19cbc293..779a1fe8 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/DummyTextProcessor.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/model/DummyTextProcessor.java @@ -1,26 +1,26 @@ -/* - * 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.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(); + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/FromJsclSimplifyTextProcessor.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/model/FromJsclSimplifyTextProcessor.java similarity index 88% rename from calculatorpp/src/main/java/org/solovyev/android/calculator/model/FromJsclSimplifyTextProcessor.java rename to calculatorpp-core/src/main/java/org/solovyev/android/calculator/model/FromJsclSimplifyTextProcessor.java index d2fb4860..d541edea 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/FromJsclSimplifyTextProcessor.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/model/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.model; + +import jscl.math.Generic; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.calculator.CalculatorLocator; +import org.solovyev.android.calculator.CalculatorLocatorImpl; +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(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/model/TextProcessor.java similarity index 95% rename from calculatorpp/src/main/java/org/solovyev/android/calculator/model/TextProcessor.java rename to calculatorpp-core/src/main/java/org/solovyev/android/calculator/model/TextProcessor.java index 48b3b1a3..a72672fe 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/TextProcessor.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/model/TextProcessor.java @@ -1,14 +1,14 @@ -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.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; +} diff --git a/calculatorpp/pom.xml b/calculatorpp/pom.xml index ea6c981a..a117fa62 100644 --- a/calculatorpp/pom.xml +++ b/calculatorpp/pom.xml @@ -82,13 +82,6 @@ org.solovyev jscl - 0.0.2 - - - xercesImpl - xerces - - 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/history/CalculatorHistoryState.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/history/CalculatorHistoryState.java index e9cb74e2..a90b457a 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/CalculatorHistoryState.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/history/CalculatorHistoryState.java @@ -1,110 +1,111 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - */ - -package org.solovyev.android.calculator.history; - -import org.jetbrains.annotations.NotNull; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; -import org.solovyev.android.calculator.ICalculatorDisplay; - -/** - * User: serso - * Date: 9/11/11 - * Time: 12:16 AM - */ - -@Root -public class CalculatorHistoryState extends AbstractHistoryState { - - @Element - @NotNull - private EditorHistoryState editorState; - - @Element - @NotNull - private CalculatorDisplayHistoryState displayState; - - private CalculatorHistoryState() { - // for xml - } - - private CalculatorHistoryState(@NotNull EditorHistoryState editorState, - @NotNull CalculatorDisplayHistoryState displayState) { - this.editorState = editorState; - this.displayState = displayState; - } - - public static CalculatorHistoryState newInstance(@NotNull Editor editor, @NotNull ICalculatorDisplay display) { - final EditorHistoryState editorHistoryState = EditorHistoryState.newInstance(editor); - final CalculatorDisplayHistoryState displayHistoryState = CalculatorDisplayHistoryState.newInstance(display); - return new CalculatorHistoryState(editorHistoryState, displayHistoryState); - } - - @NotNull - public EditorHistoryState getEditorState() { - return editorState; - } - - public void setEditorState(@NotNull EditorHistoryState editorState) { - this.editorState = editorState; - } - - @NotNull - public CalculatorDisplayHistoryState getDisplayState() { - return displayState; - } - - public void setDisplayState(@NotNull CalculatorDisplayHistoryState displayState) { - this.displayState = displayState; - } - - @Override - public String toString() { - return "CalculatorHistoryState{" + - "editorState=" + editorState + - ", displayState=" + displayState + - '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - CalculatorHistoryState that = (CalculatorHistoryState) o; - - if (this.isSaved() != that.isSaved()) return false; - if (this.getId() != that.getId()) return false; - if (!displayState.equals(that.displayState)) return false; - if (!editorState.equals(that.editorState)) return false; - - return true; - } - - @Override - public int hashCode() { - int result = Boolean.valueOf(isSaved()).hashCode(); - result = 31 * result + getId(); - result = 31 * result + editorState.hashCode(); - result = 31 * result + displayState.hashCode(); - return result; - } - - public void setValuesFromHistory(@NotNull Editor editor, @NotNull ICalculatorDisplay display) { - this.getEditorState().setValuesFromHistory(editor); - this.getDisplayState().setValuesFromHistory(display); - } - - @Override - protected CalculatorHistoryState clone() { - final CalculatorHistoryState clone = (CalculatorHistoryState)super.clone(); - - clone.editorState = this.editorState.clone(); - clone.displayState = this.displayState.clone(); - - return clone; - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + */ + +package org.solovyev.android.calculator.history; + +import org.jetbrains.annotations.NotNull; +import org.simpleframework.xml.Element; +import org.simpleframework.xml.Root; +import org.solovyev.android.calculator.Editor; +import org.solovyev.android.calculator.ICalculatorDisplay; + +/** + * User: serso + * Date: 9/11/11 + * Time: 12:16 AM + */ + +@Root +public class CalculatorHistoryState extends AbstractHistoryState { + + @Element + @NotNull + private EditorHistoryState editorState; + + @Element + @NotNull + private CalculatorDisplayHistoryState displayState; + + private CalculatorHistoryState() { + // for xml + } + + private CalculatorHistoryState(@NotNull EditorHistoryState editorState, + @NotNull CalculatorDisplayHistoryState displayState) { + this.editorState = editorState; + this.displayState = displayState; + } + + public static CalculatorHistoryState newInstance(@NotNull Editor editor, @NotNull ICalculatorDisplay display) { + final EditorHistoryState editorHistoryState = EditorHistoryState.newInstance(editor); + final CalculatorDisplayHistoryState displayHistoryState = CalculatorDisplayHistoryState.newInstance(display); + return new CalculatorHistoryState(editorHistoryState, displayHistoryState); + } + + @NotNull + public EditorHistoryState getEditorState() { + return editorState; + } + + public void setEditorState(@NotNull EditorHistoryState editorState) { + this.editorState = editorState; + } + + @NotNull + public CalculatorDisplayHistoryState getDisplayState() { + return displayState; + } + + public void setDisplayState(@NotNull CalculatorDisplayHistoryState displayState) { + this.displayState = displayState; + } + + @Override + public String toString() { + return "CalculatorHistoryState{" + + "editorState=" + editorState + + ", displayState=" + displayState + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + CalculatorHistoryState that = (CalculatorHistoryState) o; + + if (this.isSaved() != that.isSaved()) return false; + if (this.getId() != that.getId()) return false; + if (!displayState.equals(that.displayState)) return false; + if (!editorState.equals(that.editorState)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = Boolean.valueOf(isSaved()).hashCode(); + result = 31 * result + getId(); + result = 31 * result + editorState.hashCode(); + result = 31 * result + displayState.hashCode(); + return result; + } + + public void setValuesFromHistory(@NotNull Editor editor, @NotNull ICalculatorDisplay display) { + this.getEditorState().setValuesFromHistory(editor); + this.getDisplayState().setValuesFromHistory(display); + } + + @Override + protected CalculatorHistoryState clone() { + final CalculatorHistoryState clone = (CalculatorHistoryState)super.clone(); + + clone.editorState = this.editorState.clone(); + clone.displayState = this.displayState.clone(); + + return clone; + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/EditorHistoryState.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/history/EditorHistoryState.java index f087312b..ea2de8ab 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/history/EditorHistoryState.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/history/EditorHistoryState.java @@ -1,87 +1,88 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - */ - -package org.solovyev.android.calculator.history; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; - -@Root -public class EditorHistoryState implements Cloneable{ - - @Element - private int cursorPosition; - - @Element(required = false) - @Nullable - private String text; - - private EditorHistoryState() { - // for xml - } - - @NotNull - public static EditorHistoryState newInstance(@NotNull Editor editor) { - final EditorHistoryState result = new EditorHistoryState(); - - result.text = String.valueOf(editor.getText()); - result.cursorPosition = editor.getSelection(); - - return result; - } - - public void setValuesFromHistory(@NotNull Editor editor) { - editor.setText(this.getText()); - editor.setSelection(this.getCursorPosition()); - } - - @Nullable - public String getText() { - return text; - } - - public int getCursorPosition() { - return cursorPosition; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof EditorHistoryState)) return false; - - EditorHistoryState that = (EditorHistoryState) o; - - if (cursorPosition != that.cursorPosition) return false; - if (text != null ? !text.equals(that.text) : that.text != null) return false; - - return true; - } - - @Override - public int hashCode() { - int result = cursorPosition; - result = 31 * result + (text != null ? text.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "EditorHistoryState{" + - "cursorPosition=" + cursorPosition + - ", text='" + text + '\'' + - '}'; - } - - @Override - protected EditorHistoryState clone() { - try { - return (EditorHistoryState)super.clone(); - } catch (CloneNotSupportedException e) { - throw new UnsupportedOperationException(e); - } - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + */ + +package org.solovyev.android.calculator.history; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.simpleframework.xml.Element; +import org.simpleframework.xml.Root; +import org.solovyev.android.calculator.Editor; + +@Root +public class EditorHistoryState implements Cloneable{ + + @Element + private int cursorPosition; + + @Element(required = false) + @Nullable + private String text; + + private EditorHistoryState() { + // for xml + } + + @NotNull + public static EditorHistoryState newInstance(@NotNull Editor editor) { + final EditorHistoryState result = new EditorHistoryState(); + + result.text = String.valueOf(editor.getText()); + result.cursorPosition = editor.getSelection(); + + return result; + } + + public void setValuesFromHistory(@NotNull Editor editor) { + editor.setText(this.getText()); + editor.setSelection(this.getCursorPosition()); + } + + @Nullable + public String getText() { + return text; + } + + public int getCursorPosition() { + return cursorPosition; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof EditorHistoryState)) return false; + + EditorHistoryState that = (EditorHistoryState) o; + + if (cursorPosition != that.cursorPosition) return false; + if (text != null ? !text.equals(that.text) : that.text != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = cursorPosition; + result = 31 * result + (text != null ? text.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "EditorHistoryState{" + + "cursorPosition=" + cursorPosition + + ", text='" + text + '\'' + + '}'; + } + + @Override + protected EditorHistoryState clone() { + try { + return (EditorHistoryState)super.clone(); + } catch (CloneNotSupportedException e) { + throw new UnsupportedOperationException(e); + } + } +} 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..3448880e 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 @@ -16,13 +16,16 @@ 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.JCalculatorEngine; import org.solovyev.android.calculator.R; import org.solovyev.android.calculator.jscl.JsclOperation; +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; @@ -40,7 +43,7 @@ import java.util.concurrent.TimeUnit; * Time: 11:38 PM */ -public enum CalculatorEngine { +public enum CalculatorEngine implements JCalculatorEngine { instance; @@ -130,7 +133,8 @@ public enum CalculatorEngine { this.engine.setUseGroupingSeparator(true); } - @NotNull + @Override + @NotNull public String getMultiplicationSign() { return multiplicationSign; } @@ -229,10 +233,12 @@ public enum CalculatorEngine { 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())); + 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())); - parseException.setObject(new CalculatorParseException(R.string.msg_2, CalculatorApplication.getInstance(), jsclExpression)); + 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)); @@ -278,11 +284,13 @@ public enum CalculatorEngine { } if (calculationResultLocal == null) { - throw new CalculatorParseException(R.string.msg_3, CalculatorApplication.getInstance(), jsclExpression); + final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_3, MessageType.error, CalculatorApplication.getInstance()); + throw new CalculatorParseException(jsclExpression, androidMessage); } } catch (InterruptedException e) { - throw new CalculatorParseException(R.string.msg_4, CalculatorApplication.getInstance(), jsclExpression); + final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_4, MessageType.error, CalculatorApplication.getInstance()); + throw new CalculatorParseException(jsclExpression, androidMessage); } final Generic genericResult = calculationResult.getObject(); @@ -356,27 +364,32 @@ public enum CalculatorEngine { } } - @NotNull + @Override + @NotNull public AndroidMathRegistry getVarsRegistry() { return varsRegistry; } - @NotNull + @Override + @NotNull public AndroidMathRegistry getFunctionsRegistry() { return functionsRegistry; } - @NotNull + @Override + @NotNull public AndroidMathRegistry getOperatorsRegistry() { return operatorsRegistry; } - @NotNull + @Override + @NotNull public AndroidMathRegistry getPostfixFunctionsRegistry() { return postfixFunctionsRegistry; } - @NotNull + @Override + @NotNull public MathEngine getEngine() { return engine; } diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/ToJsclTextProcessor.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/model/ToJsclTextProcessor.java index d12f3ca2..12067da4 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/ToJsclTextProcessor.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/model/ToJsclTextProcessor.java @@ -10,9 +10,11 @@ import jscl.math.function.IConstant; import org.jetbrains.annotations.NotNull; import org.solovyev.android.calculator.CalculatorApplication; import org.solovyev.android.calculator.R; +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 +77,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/test/java/org/solovyev/android/calculator/history/HistoryUtilsTest.java b/calculatorpp/src/test/java/org/solovyev/android/calculator/history/HistoryUtilsTest.java index a58e72a5..c05f2a32 100644 --- a/calculatorpp/src/test/java/org/solovyev/android/calculator/history/HistoryUtilsTest.java +++ b/calculatorpp/src/test/java/org/solovyev/android/calculator/history/HistoryUtilsTest.java @@ -1,320 +1,321 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - * or visit http://se.solovyev.org - */ - -package org.solovyev.android.calculator.history; - -import jscl.math.Generic; -import junit.framework.Assert; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.junit.Test; -import org.solovyev.android.calculator.ICalculatorDisplay; -import org.solovyev.android.calculator.jscl.JsclOperation; -import org.solovyev.common.equals.CollectionEqualizer; -import org.solovyev.common.equals.EqualsTool; -import org.solovyev.common.history.HistoryHelper; -import org.solovyev.common.history.SimpleHistoryHelper; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -/** - * User: serso - * Date: 12/17/11 - * Time: 10:01 PM - */ -public class HistoryUtilsTest { - - @Test - public void testFromXml() throws Exception { - - } - - private static final String emptyHistory = "\n" + - " \n" + - ""; - - private static final String toXml1 = "\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " 3\n" + - " 1+1\n" + - " \n" + - " \n" + - " \n" + - " 1\n" + - " Error\n" + - " \n" + - " simplify\n" + - " \n" + - " \n" + - " \n" + - ""; - - private static final String toXml2 = "\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " 3\n" + - " 1+1\n" + - " \n" + - " \n" + - " \n" + - " 1\n" + - " Error\n" + - " \n" + - " simplify\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " 2\n" + - " 5/6\n" + - " \n" + - " \n" + - " \n" + - " 3\n" + - " 5/6\n" + - " \n" + - " numeric\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " 1\n" + - " null\n" + - " \n" + - " \n" + - " \n" + - " 1\n" + - " Error\n" + - " \n" + - " elementary\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " 0\n" + - " 4+5/35sin(41)+dfdsfsdfs\n" + - " \n" + - " \n" + - " \n" + - " 1\n" + - " 4+5/35sin(41)+dfdsfsdfs\n" + - " \n" + - " numeric\n" + - " \n" + - " \n" + - " \n" + - ""; - - @Test - public void testToXml() throws Exception { - final Date date = new Date(100000000); - - HistoryHelper history = new SimpleHistoryHelper(); - - ICalculatorDisplay calculatorDisplay = new TestCalculatorDisplay(); - calculatorDisplay.setErrorMessage("error_msg1"); - calculatorDisplay.setText("Error"); - calculatorDisplay.setSelection(1); - calculatorDisplay.setJsclOperation(JsclOperation.simplify); - - Editor calculatorEditor = new TestEditor(); - calculatorEditor.setSelection(3); - calculatorEditor.setText("1+1"); - - CalculatorHistoryState state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay); - state.setTime(date.getTime()); - history.addState(state); - - Assert.assertEquals(emptyHistory, HistoryUtils.toXml(history.getStates())); - - - state.setSaved(true); - - Assert.assertEquals(toXml1, HistoryUtils.toXml(history.getStates())); - - calculatorDisplay = new TestCalculatorDisplay(); - calculatorDisplay.setErrorMessage(null); - calculatorDisplay.setText("5/6"); - calculatorDisplay.setSelection(3); - calculatorDisplay.setJsclOperation(JsclOperation.numeric); - - calculatorEditor = new TestEditor(); - calculatorEditor.setSelection(2); - calculatorEditor.setText("5/6"); - - state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay); - state.setSaved(true); - state.setTime(date.getTime()); - history.addState(state); - - calculatorDisplay = new TestCalculatorDisplay(); - calculatorDisplay.setErrorMessage("error_msg2"); - calculatorDisplay.setText("Error"); - calculatorDisplay.setSelection(1); - calculatorDisplay.setJsclOperation(JsclOperation.elementary); - - calculatorEditor = new TestEditor(); - calculatorEditor.setSelection(1); - calculatorEditor.setText(null); - - state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay); - state.setSaved(true); - state.setTime(date.getTime()); - history.addState(state); - - calculatorDisplay = new TestCalculatorDisplay(); - calculatorDisplay.setErrorMessage(null); - calculatorDisplay.setText("4+5/35sin(41)+dfdsfsdfs"); - calculatorDisplay.setSelection(1); - calculatorDisplay.setJsclOperation(JsclOperation.numeric); - - calculatorEditor = new TestEditor(); - calculatorEditor.setSelection(0); - calculatorEditor.setText("4+5/35sin(41)+dfdsfsdfs"); - - state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay); - state.setSaved(true); - state.setTime(date.getTime()); - history.addState(state); - - String xml = HistoryUtils.toXml(history.getStates()); - Assert.assertEquals(toXml2, xml); - - final List fromXml = new ArrayList(); - final HistoryHelper historyFromXml = new SimpleHistoryHelper(); - HistoryUtils.fromXml(xml, fromXml); - for (CalculatorHistoryState historyState : fromXml) { - historyFromXml.addState(historyState); - } - - Assert.assertEquals(history.getStates().size(), historyFromXml.getStates().size()); - - for (CalculatorHistoryState historyState : history.getStates()) { - historyState.setId(0); - historyState.setSaved(true); - } - for (CalculatorHistoryState historyState : historyFromXml.getStates()) { - historyState.setId(0); - historyState.setSaved(true); - } - Assert.assertTrue(EqualsTool.areEqual(history.getStates(), historyFromXml.getStates(), new CollectionEqualizer(null))); - } - - - private static class TestCalculatorDisplay implements ICalculatorDisplay { - - @NotNull - private final TestEditor testEditor = new TestEditor(); - - private boolean valid; - - private String errorMessage; - - private JsclOperation operation; - - private Generic genericResult; - - @Override - public boolean isValid() { - return this.valid; - } - - @Override - public void setValid(boolean valid) { - this.valid = valid; - } - - @Override - public String getErrorMessage() { - return this.errorMessage; - } - - @Override - public void setErrorMessage(@Nullable String errorMessage) { - this.errorMessage = errorMessage; - } - - @Override - public void setJsclOperation(@NotNull JsclOperation jsclOperation) { - this.operation = jsclOperation; - } - - @NotNull - @Override - public JsclOperation getJsclOperation() { - return this.operation; - } - - @Override - public void setGenericResult(@Nullable Generic genericResult) { - this.genericResult = genericResult; - } - - @Override - public Generic getGenericResult() { - return this.genericResult; - } - - @Override - public CharSequence getText() { - return this.testEditor.getText(); - } - - @Override - public void setText(@Nullable CharSequence text) { - this.testEditor.setText(text); - } - - @Override - public int getSelection() { - return this.testEditor.getSelection(); - } - - @Override - public void setSelection(int selection) { - this.testEditor.setSelection(selection); - } - } - - private static class TestEditor implements Editor { - - @Nullable - private CharSequence text; - - private int selection; - - @Nullable - @Override - public CharSequence getText() { - return this.text; - } - - @Override - public void setText(@Nullable CharSequence text) { - this.text = text; - } - - @Override - public int getSelection() { - return this.selection; - } - - @Override - public void setSelection(int selection) { - this.selection = selection; - } - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + * or visit http://se.solovyev.org + */ + +package org.solovyev.android.calculator.history; + +import jscl.math.Generic; +import junit.framework.Assert; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.junit.Test; +import org.solovyev.android.calculator.Editor; +import org.solovyev.android.calculator.ICalculatorDisplay; +import org.solovyev.android.calculator.jscl.JsclOperation; +import org.solovyev.common.equals.CollectionEqualizer; +import org.solovyev.common.equals.EqualsTool; +import org.solovyev.common.history.HistoryHelper; +import org.solovyev.common.history.SimpleHistoryHelper; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * User: serso + * Date: 12/17/11 + * Time: 10:01 PM + */ +public class HistoryUtilsTest { + + @Test + public void testFromXml() throws Exception { + + } + + private static final String emptyHistory = "\n" + + " \n" + + ""; + + private static final String toXml1 = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " 3\n" + + " 1+1\n" + + " \n" + + " \n" + + " \n" + + " 1\n" + + " Error\n" + + " \n" + + " simplify\n" + + " \n" + + " \n" + + " \n" + + ""; + + private static final String toXml2 = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " 3\n" + + " 1+1\n" + + " \n" + + " \n" + + " \n" + + " 1\n" + + " Error\n" + + " \n" + + " simplify\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " 2\n" + + " 5/6\n" + + " \n" + + " \n" + + " \n" + + " 3\n" + + " 5/6\n" + + " \n" + + " numeric\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " 1\n" + + " null\n" + + " \n" + + " \n" + + " \n" + + " 1\n" + + " Error\n" + + " \n" + + " elementary\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " 0\n" + + " 4+5/35sin(41)+dfdsfsdfs\n" + + " \n" + + " \n" + + " \n" + + " 1\n" + + " 4+5/35sin(41)+dfdsfsdfs\n" + + " \n" + + " numeric\n" + + " \n" + + " \n" + + " \n" + + ""; + + @Test + public void testToXml() throws Exception { + final Date date = new Date(100000000); + + HistoryHelper history = new SimpleHistoryHelper(); + + ICalculatorDisplay calculatorDisplay = new TestCalculatorDisplay(); + calculatorDisplay.setErrorMessage("error_msg1"); + calculatorDisplay.setText("Error"); + calculatorDisplay.setSelection(1); + calculatorDisplay.setJsclOperation(JsclOperation.simplify); + + Editor calculatorEditor = new TestEditor(); + calculatorEditor.setSelection(3); + calculatorEditor.setText("1+1"); + + CalculatorHistoryState state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay); + state.setTime(date.getTime()); + history.addState(state); + + Assert.assertEquals(emptyHistory, HistoryUtils.toXml(history.getStates())); + + + state.setSaved(true); + + Assert.assertEquals(toXml1, HistoryUtils.toXml(history.getStates())); + + calculatorDisplay = new TestCalculatorDisplay(); + calculatorDisplay.setErrorMessage(null); + calculatorDisplay.setText("5/6"); + calculatorDisplay.setSelection(3); + calculatorDisplay.setJsclOperation(JsclOperation.numeric); + + calculatorEditor = new TestEditor(); + calculatorEditor.setSelection(2); + calculatorEditor.setText("5/6"); + + state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay); + state.setSaved(true); + state.setTime(date.getTime()); + history.addState(state); + + calculatorDisplay = new TestCalculatorDisplay(); + calculatorDisplay.setErrorMessage("error_msg2"); + calculatorDisplay.setText("Error"); + calculatorDisplay.setSelection(1); + calculatorDisplay.setJsclOperation(JsclOperation.elementary); + + calculatorEditor = new TestEditor(); + calculatorEditor.setSelection(1); + calculatorEditor.setText(null); + + state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay); + state.setSaved(true); + state.setTime(date.getTime()); + history.addState(state); + + calculatorDisplay = new TestCalculatorDisplay(); + calculatorDisplay.setErrorMessage(null); + calculatorDisplay.setText("4+5/35sin(41)+dfdsfsdfs"); + calculatorDisplay.setSelection(1); + calculatorDisplay.setJsclOperation(JsclOperation.numeric); + + calculatorEditor = new TestEditor(); + calculatorEditor.setSelection(0); + calculatorEditor.setText("4+5/35sin(41)+dfdsfsdfs"); + + state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay); + state.setSaved(true); + state.setTime(date.getTime()); + history.addState(state); + + String xml = HistoryUtils.toXml(history.getStates()); + Assert.assertEquals(toXml2, xml); + + final List fromXml = new ArrayList(); + final HistoryHelper historyFromXml = new SimpleHistoryHelper(); + HistoryUtils.fromXml(xml, fromXml); + for (CalculatorHistoryState historyState : fromXml) { + historyFromXml.addState(historyState); + } + + Assert.assertEquals(history.getStates().size(), historyFromXml.getStates().size()); + + for (CalculatorHistoryState historyState : history.getStates()) { + historyState.setId(0); + historyState.setSaved(true); + } + for (CalculatorHistoryState historyState : historyFromXml.getStates()) { + historyState.setId(0); + historyState.setSaved(true); + } + Assert.assertTrue(EqualsTool.areEqual(history.getStates(), historyFromXml.getStates(), new CollectionEqualizer(null))); + } + + + private static class TestCalculatorDisplay implements ICalculatorDisplay { + + @NotNull + private final TestEditor testEditor = new TestEditor(); + + private boolean valid; + + private String errorMessage; + + private JsclOperation operation; + + private Generic genericResult; + + @Override + public boolean isValid() { + return this.valid; + } + + @Override + public void setValid(boolean valid) { + this.valid = valid; + } + + @Override + public String getErrorMessage() { + return this.errorMessage; + } + + @Override + public void setErrorMessage(@Nullable String errorMessage) { + this.errorMessage = errorMessage; + } + + @Override + public void setJsclOperation(@NotNull JsclOperation jsclOperation) { + this.operation = jsclOperation; + } + + @NotNull + @Override + public JsclOperation getJsclOperation() { + return this.operation; + } + + @Override + public void setGenericResult(@Nullable Generic genericResult) { + this.genericResult = genericResult; + } + + @Override + public Generic getGenericResult() { + return this.genericResult; + } + + @Override + public CharSequence getText() { + return this.testEditor.getText(); + } + + @Override + public void setText(@Nullable CharSequence text) { + this.testEditor.setText(text); + } + + @Override + public int getSelection() { + return this.testEditor.getSelection(); + } + + @Override + public void setSelection(int selection) { + this.testEditor.setSelection(selection); + } + } + + private static class TestEditor implements Editor { + + @Nullable + private CharSequence text; + + private int selection; + + @Nullable + @Override + public CharSequence getText() { + return this.text; + } + + @Override + public void setText(@Nullable CharSequence text) { + this.text = text; + } + + @Override + public int getSelection() { + return this.selection; + } + + @Override + public void setSelection(int selection) { + this.selection = selection; + } + } +} diff --git a/pom.xml b/pom.xml index 93bf95b9..041abf06 100644 --- a/pom.xml +++ b/pom.xml @@ -72,6 +72,18 @@ 1.0.0 + + org.solovyev + jscl + 0.0.2 + + + xercesImpl + xerces + + + + org.solovyev.android android-common-other From eb37fe495b18e6a82a26fb55fa7f2f299083302b Mon Sep 17 00:00:00 2001 From: serso Date: Thu, 20 Sep 2012 13:19:48 +0400 Subject: [PATCH 3/6] Module separation --- calculatorpp-core/pom.xml | 5 + .../{model => }/CalculatorParseException.java | 2 +- .../history/AbstractHistoryState.java | 161 ++-- .../CalculatorDisplayHistoryState.java | 276 +++--- .../history/CalculatorHistoryState.java | 0 .../history/EditorHistoryState.java | 0 .../android/calculator/history/History.java | 66 +- .../calculator/history/HistoryUtils.java | 122 +-- .../jscl/FromJsclNumericTextProcessor.java | 4 +- .../calculator/jscl/JsclOperation.java | 6 +- .../android/calculator/math/MathType.java | 2 +- .../{model => text}/DummyTextProcessor.java | 3 +- .../FromJsclSimplifyTextProcessor.java | 4 +- .../{model => text}/TextProcessor.java | 3 +- calculatorpp/pom.xml | 11 - .../android/calculator/CalculatorDisplay.java | 695 +++++++------- .../android/calculator/CalculatorEditor.java | 331 ++++--- .../android/calculator/CalculatorModel.java | 821 ++++++++-------- .../calculator/model/CalculatorEngine.java | 2 + .../calculator/model/ToJsclTextProcessor.java | 2 + .../plot/CalculatorPlotActivity.java | 720 +++++++------- .../view/NumeralBaseConverterDialog.java | 2 +- .../calculator/view/TextHighlighter.java | 478 +++++----- .../calculator/TextHighlighterTest.java | 274 +++--- .../model/CalculatorEngineTest.java | 887 +++++++++--------- .../FromJsclSimplifyTextProcessorTest.java | 139 +-- .../calculator/model/NumeralBaseTest.java | 293 +++--- .../model/ToJsclTextProcessorTest.java | 324 +++---- pom.xml | 16 + 29 files changed, 2834 insertions(+), 2815 deletions(-) rename calculatorpp-core/src/main/java/org/solovyev/android/calculator/{model => }/CalculatorParseException.java (93%) rename {calculatorpp => calculatorpp-core}/src/main/java/org/solovyev/android/calculator/history/AbstractHistoryState.java (94%) rename {calculatorpp => calculatorpp-core}/src/main/java/org/solovyev/android/calculator/history/CalculatorDisplayHistoryState.java (95%) rename {calculatorpp => calculatorpp-core}/src/main/java/org/solovyev/android/calculator/history/CalculatorHistoryState.java (100%) rename {calculatorpp => calculatorpp-core}/src/main/java/org/solovyev/android/calculator/history/EditorHistoryState.java (100%) rename {calculatorpp => calculatorpp-core}/src/main/java/org/solovyev/android/calculator/history/History.java (95%) rename {calculatorpp => calculatorpp-core}/src/main/java/org/solovyev/android/calculator/history/HistoryUtils.java (96%) rename calculatorpp-core/src/main/java/org/solovyev/android/calculator/{model => text}/DummyTextProcessor.java (78%) rename calculatorpp-core/src/main/java/org/solovyev/android/calculator/{model => text}/FromJsclSimplifyTextProcessor.java (92%) rename calculatorpp-core/src/main/java/org/solovyev/android/calculator/{model => text}/TextProcessor.java (65%) diff --git a/calculatorpp-core/pom.xml b/calculatorpp-core/pom.xml index 22f023e6..17ae2ad2 100644 --- a/calculatorpp-core/pom.xml +++ b/calculatorpp-core/pom.xml @@ -35,6 +35,11 @@ jscl + + org.simpleframework + simple-xml + + diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/model/CalculatorParseException.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorParseException.java similarity index 93% rename from calculatorpp-core/src/main/java/org/solovyev/android/calculator/model/CalculatorParseException.java rename to calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorParseException.java index 157a6c82..8ebfccf7 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/model/CalculatorParseException.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorParseException.java @@ -4,7 +4,7 @@ * or visit http://se.solovyev.org */ -package org.solovyev.android.calculator.model; +package org.solovyev.android.calculator; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; 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 95% 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..b637e901 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 @@ -1,138 +1,138 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - */ - -package org.solovyev.android.calculator.history; - -import jscl.math.Generic; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; -import org.simpleframework.xml.Transient; -import org.solovyev.android.calculator.ICalculatorDisplay; -import org.solovyev.android.calculator.jscl.JsclOperation; - -/** - * User: serso - * Date: 9/17/11 - * Time: 11:05 PM - */ - -@Root -public class CalculatorDisplayHistoryState implements Cloneable { - - @Transient - private boolean valid = true; - - @Transient - @Nullable - private String errorMessage = null; - - @Element - @NotNull - private EditorHistoryState editorState; - - @Element - @NotNull - private JsclOperation jsclOperation; - - @Transient - @Nullable - private Generic genericResult; - - private CalculatorDisplayHistoryState() { - // for xml - } - - @NotNull - public static CalculatorDisplayHistoryState newInstance(@NotNull ICalculatorDisplay display) { - final CalculatorDisplayHistoryState result = new CalculatorDisplayHistoryState(); - - result.editorState = EditorHistoryState.newInstance(display); - result.valid = display.isValid(); - result.jsclOperation = display.getJsclOperation(); - result.genericResult = display.getGenericResult(); - result.errorMessage = display.getErrorMessage(); - - return result; - } - - public void setValuesFromHistory(@NotNull ICalculatorDisplay display) { - this.getEditorState().setValuesFromHistory(display); - display.setValid(this.isValid()); - display.setErrorMessage(this.getErrorMessage()); - display.setJsclOperation(this.getJsclOperation()); - display.setGenericResult(this.getGenericResult()); - } - - - public boolean isValid() { - return valid; - } - - @NotNull - public EditorHistoryState getEditorState() { - return editorState; - } - - @NotNull - public JsclOperation getJsclOperation() { - return jsclOperation; - } - - @Nullable - public String getErrorMessage() { - return errorMessage; - } - - @Nullable - public Generic getGenericResult() { - return genericResult; - } - - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - CalculatorDisplayHistoryState that = (CalculatorDisplayHistoryState) o; - - if (!editorState.equals(that.editorState)) return false; - if (jsclOperation != that.jsclOperation) return false; - - return true; - } - - @Override - public int hashCode() { - int result = editorState.hashCode(); - result = 31 * result + jsclOperation.hashCode(); - return result; - } - - @Override - public String toString() { - return "CalculatorDisplayHistoryState{" + - "valid=" + valid + - ", errorMessage='" + errorMessage + '\'' + - ", editorHistoryState=" + editorState + - ", jsclOperation=" + jsclOperation + - '}'; - } - - @Override - protected CalculatorDisplayHistoryState clone() { - try { - final CalculatorDisplayHistoryState clone = (CalculatorDisplayHistoryState) super.clone(); - - clone.editorState = this.editorState.clone(); - - return clone; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + */ + +package org.solovyev.android.calculator.history; + +import jscl.math.Generic; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.simpleframework.xml.Element; +import org.simpleframework.xml.Root; +import org.simpleframework.xml.Transient; +import org.solovyev.android.calculator.ICalculatorDisplay; +import org.solovyev.android.calculator.jscl.JsclOperation; + +/** + * User: serso + * Date: 9/17/11 + * Time: 11:05 PM + */ + +@Root +public class CalculatorDisplayHistoryState implements Cloneable { + + @Transient + private boolean valid = true; + + @Transient + @Nullable + private String errorMessage = null; + + @Element + @NotNull + private EditorHistoryState editorState; + + @Element + @NotNull + private JsclOperation jsclOperation; + + @Transient + @Nullable + private Generic genericResult; + + private CalculatorDisplayHistoryState() { + // for xml + } + + @NotNull + public static CalculatorDisplayHistoryState newInstance(@NotNull ICalculatorDisplay display) { + final CalculatorDisplayHistoryState result = new CalculatorDisplayHistoryState(); + + result.editorState = EditorHistoryState.newInstance(display); + result.valid = display.isValid(); + result.jsclOperation = display.getJsclOperation(); + result.genericResult = display.getGenericResult(); + result.errorMessage = display.getErrorMessage(); + + return result; + } + + public void setValuesFromHistory(@NotNull ICalculatorDisplay display) { + this.getEditorState().setValuesFromHistory(display); + display.setValid(this.isValid()); + display.setErrorMessage(this.getErrorMessage()); + display.setJsclOperation(this.getJsclOperation()); + display.setGenericResult(this.getGenericResult()); + } + + + public boolean isValid() { + return valid; + } + + @NotNull + public EditorHistoryState getEditorState() { + return editorState; + } + + @NotNull + public JsclOperation getJsclOperation() { + return jsclOperation; + } + + @Nullable + public String getErrorMessage() { + return errorMessage; + } + + @Nullable + public Generic getGenericResult() { + return genericResult; + } + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + CalculatorDisplayHistoryState that = (CalculatorDisplayHistoryState) o; + + if (!editorState.equals(that.editorState)) return false; + if (jsclOperation != that.jsclOperation) return false; + + return true; + } + + @Override + public int hashCode() { + int result = editorState.hashCode(); + result = 31 * result + jsclOperation.hashCode(); + return result; + } + + @Override + public String toString() { + return "CalculatorDisplayHistoryState{" + + "valid=" + valid + + ", errorMessage='" + errorMessage + '\'' + + ", editorHistoryState=" + editorState + + ", jsclOperation=" + jsclOperation + + '}'; + } + + @Override + protected CalculatorDisplayHistoryState clone() { + try { + final CalculatorDisplayHistoryState clone = (CalculatorDisplayHistoryState) super.clone(); + + clone.editorState = this.editorState.clone(); + + return clone; + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } +} 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 100% 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 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 100% 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 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-core/src/main/java/org/solovyev/android/calculator/jscl/FromJsclNumericTextProcessor.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/jscl/FromJsclNumericTextProcessor.java index b664c9ff..2251e5b8 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/jscl/FromJsclNumericTextProcessor.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/jscl/FromJsclNumericTextProcessor.java @@ -8,8 +8,8 @@ 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; +import org.solovyev.android.calculator.CalculatorParseException; +import org.solovyev.android.calculator.text.TextProcessor; /** * User: serso diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/jscl/JsclOperation.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/jscl/JsclOperation.java index f3a21199..978964b7 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/jscl/JsclOperation.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/jscl/JsclOperation.java @@ -10,9 +10,9 @@ import jscl.math.Generic; import jscl.text.ParseException; import org.jetbrains.annotations.NotNull; import org.solovyev.android.calculator.CalculatorLocatorImpl; -import org.solovyev.android.calculator.model.DummyTextProcessor; -import org.solovyev.android.calculator.model.FromJsclSimplifyTextProcessor; -import org.solovyev.android.calculator.model.TextProcessor; +import org.solovyev.android.calculator.text.DummyTextProcessor; +import org.solovyev.android.calculator.text.FromJsclSimplifyTextProcessor; +import org.solovyev.android.calculator.text.TextProcessor; public enum JsclOperation { diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/math/MathType.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/math/MathType.java index 4aa60280..549ae9fd 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/math/MathType.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/math/MathType.java @@ -13,7 +13,7 @@ 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.model.CalculatorParseException; +import org.solovyev.android.calculator.CalculatorParseException; import org.solovyev.common.collections.CollectionsUtils; import java.util.*; diff --git a/calculatorpp-core/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-core/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 779a1fe8..22a969c5 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/model/DummyTextProcessor.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/text/DummyTextProcessor.java @@ -4,10 +4,11 @@ * or visit http://se.solovyev.org */ -package org.solovyev.android.calculator.model; +package org.solovyev.android.calculator.text; import jscl.math.Generic; import org.jetbrains.annotations.NotNull; +import org.solovyev.android.calculator.CalculatorParseException; /** * User: serso diff --git a/calculatorpp-core/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 92% rename from calculatorpp-core/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 d541edea..3fc4da64 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/model/FromJsclSimplifyTextProcessor.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/text/FromJsclSimplifyTextProcessor.java @@ -1,11 +1,11 @@ -package org.solovyev.android.calculator.model; +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.CalculatorLocator; 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; diff --git a/calculatorpp-core/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-core/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 a72672fe..b9e96935 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/model/TextProcessor.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/text/TextProcessor.java @@ -1,6 +1,7 @@ -package org.solovyev.android.calculator.model; +package org.solovyev.android.calculator.text; import org.jetbrains.annotations.NotNull; +import org.solovyev.android.calculator.CalculatorParseException; /** * User: serso diff --git a/calculatorpp/pom.xml b/calculatorpp/pom.xml index a117fa62..82523499 100644 --- a/calculatorpp/pom.xml +++ b/calculatorpp/pom.xml @@ -102,17 +102,6 @@ org.simpleframework simple-xml - 2.6.1 - - - stax-api - stax - - - xpp3 - xpp3 - - diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorDisplay.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorDisplay.java index 77b21101..54fc4e6f 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorDisplay.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorDisplay.java @@ -1,348 +1,347 @@ -/* - * 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 - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + */ + +package org.solovyev.android.calculator; + +import android.content.Context; +import android.graphics.Color; +import android.text.Html; +import android.util.AttributeSet; +import android.util.Log; +import jscl.NumeralBase; +import jscl.math.Generic; +import jscl.math.function.Constant; +import jscl.math.function.IConstant; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.calculator.jscl.JsclOperation; +import org.solovyev.android.calculator.model.CalculatorEngine; +import org.solovyev.android.calculator.text.TextProcessor; +import org.solovyev.android.calculator.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..6d544a8f 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorModel.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorModel.java @@ -1,411 +1,410 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - */ - -package org.solovyev.android.calculator; - -import android.app.Activity; -import android.app.AlertDialog; -import android.content.Context; -import android.content.SharedPreferences; -import android.os.Handler; -import android.text.ClipboardManager; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.EditText; -import android.widget.TextView; -import android.widget.Toast; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.solovyev.android.CursorControl; -import org.solovyev.android.calculator.history.CalculatorHistory; -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; -import org.solovyev.common.MutableObject; -import org.solovyev.common.history.HistoryAction; -import org.solovyev.common.msg.Message; -import org.solovyev.common.text.StringUtils; - -import java.util.ArrayList; -import java.util.List; - -/** - * User: serso - * Date: 9/12/11 - * Time: 11:15 PM - */ -public enum CalculatorModel implements CursorControl, HistoryControl, CalculatorEngineControl { - - instance; - - // millis to wait before evaluation after user edit action - public static final int EVAL_DELAY_MILLIS = 0; - - @NotNull - private CalculatorEditor editor; - - @NotNull - private CalculatorDisplay display; - - @NotNull - private CalculatorEngine calculatorEngine; - - public CalculatorModel init(@NotNull final Activity activity, @NotNull SharedPreferences preferences, @NotNull CalculatorEngine calculator) { - Log.d(this.getClass().getName(), "CalculatorModel initialization with activity: " + activity); - this.calculatorEngine = calculator; - - this.editor = (CalculatorEditor) activity.findViewById(R.id.calculatorEditor); - this.editor.init(preferences); - preferences.registerOnSharedPreferenceChangeListener(editor); - - this.display = (CalculatorDisplay) activity.findViewById(R.id.calculatorDisplay); - this.display.setOnClickListener(new CalculatorDisplayOnClickListener(activity)); - - final CalculatorHistoryState lastState = CalculatorHistory.instance.getLastHistoryState(); - if (lastState == null) { - saveHistoryState(); - } else { - setCurrentHistoryState(lastState); - } - - - return this; - } - - private static void showEvaluationError(@NotNull Activity activity, @NotNull final String errorMessage) { - final LayoutInflater layoutInflater = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE); - - final View errorMessageView = layoutInflater.inflate(R.layout.display_error_message, null); - ((TextView) errorMessageView.findViewById(R.id.error_message_text_view)).setText(errorMessage); - - final AlertDialog.Builder builder = new AlertDialog.Builder(activity) - .setPositiveButton(R.string.c_cancel, null) - .setView(errorMessageView); - - builder.create().show(); - } - - public void copyResult(@NotNull Context context) { - copyResult(context, display); - } - - public static void copyResult(@NotNull Context context, @NotNull final CalculatorDisplay display) { - if (display.isValid()) { - final CharSequence text = display.getText(); - if (!StringUtils.isEmpty(text)) { - final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE); - clipboard.setText(text.toString()); - Toast.makeText(context, context.getText(R.string.c_result_copied), Toast.LENGTH_SHORT).show(); - } - } - } - - private void saveHistoryState() { - CalculatorHistory.instance.addState(getCurrentHistoryState()); - } - - public void setCursorOnStart() { - editor.setSelection(0); - } - - public void setCursorOnEnd() { - editor.setSelection(editor.getText().length()); - } - - public void moveCursorLeft() { - if (editor.getSelectionStart() > 0) { - editor.setSelection(editor.getSelectionStart() - 1); - } - } - - public void moveCursorRight() { - if (editor.getSelectionStart() < editor.getText().length()) { - editor.setSelection(editor.getSelectionStart() + 1); - } - } - - public void doTextOperation(@NotNull TextOperation operation) { - doTextOperation(operation, true); - } - - public void doTextOperation(@NotNull TextOperation operation, boolean delayEvaluate) { - doTextOperation(operation, delayEvaluate, JsclOperation.numeric, false); - } - - public void doTextOperation(@NotNull TextOperation operation, boolean delayEvaluate, @NotNull JsclOperation jsclOperation, boolean forceEval) { - final String editorStateBefore = this.editor.getText().toString(); - - Log.d(CalculatorModel.class.getName(), "Editor state changed before '" + editorStateBefore + "'"); - operation.doOperation(this.editor); - //Log.d(CalculatorModel.class.getName(), "Doing text operation" + StringUtils.fromStackTrace(Thread.currentThread().getStackTrace())); - - final String editorStateAfter = this.editor.getText().toString(); - if (forceEval ||!editorStateBefore.equals(editorStateAfter)) { - - editor.redraw(); - - evaluate(delayEvaluate, editorStateAfter, jsclOperation, null); - } - } - - @NotNull - private final static MutableObject pendingOperation = new MutableObject(); - - private void evaluate(boolean delayEvaluate, - @NotNull final String expression, - @NotNull final JsclOperation operation, - @Nullable CalculatorHistoryState historyState) { - - final CalculatorHistoryState localHistoryState; - if (historyState == null) { - //this.display.setText(""); - localHistoryState = getCurrentHistoryState(); - } else { - this.display.setText(historyState.getDisplayState().getEditorState().getText()); - localHistoryState = historyState; - } - - pendingOperation.setObject(new Runnable() { - @Override - public void run() { - // allow only one runner at one time - synchronized (pendingOperation) { - //lock all operations with history - if (pendingOperation.getObject() == this) { - // actually nothing shall be logged while text operations are done - evaluate(expression, operation, this); - - if (pendingOperation.getObject() == this) { - // todo serso: of course there is small probability that someone will set pendingOperation after if statement but before .setObject(null) - pendingOperation.setObject(null); - localHistoryState.setDisplayState(getCurrentHistoryState().getDisplayState()); - } - } - } - } - }); - - if (delayEvaluate) { - if (historyState == null) { - CalculatorHistory.instance.addState(localHistoryState); - } - // todo serso: this is not correct - operation is processing still in the same thread - new Handler().postDelayed(pendingOperation.getObject(), EVAL_DELAY_MILLIS); - } else { - pendingOperation.getObject().run(); - if (historyState == null) { - CalculatorHistory.instance.addState(localHistoryState); - } - } - } - - @Override - public void evaluate() { - evaluate(false, this.editor.getText().toString(), JsclOperation.numeric, null); - } - - public void evaluate(@NotNull JsclOperation operation) { - evaluate(false, this.editor.getText().toString(), operation, null); - } - - @Override - public void simplify() { - evaluate(false, this.editor.getText().toString(), JsclOperation.simplify, null); - } - - private void evaluate(@Nullable final String expression, - @NotNull JsclOperation operation, - @NotNull Runnable currentRunner) { - - if (!StringUtils.isEmpty(expression)) { - try { - Log.d(CalculatorModel.class.getName(), "Trying to evaluate '" + operation + "': " + expression /*+ StringUtils.fromStackTrace(Thread.currentThread().getStackTrace())*/); - final CalculatorEngine.Result result = calculatorEngine.evaluate(operation, expression); - - // todo serso: second condition might replaced with expression.equals(this.editor.getText().toString()) ONLY if expression will be formatted with text highlighter - if (currentRunner == pendingOperation.getObject() && this.editor.getText().length() > 0) { - display.setText(result.getResult()); - } else { - display.setText(""); - } - display.setJsclOperation(result.getUserOperation()); - display.setGenericResult(result.getGenericResult()); - } catch (CalculatorParseException e) { - handleEvaluationException(expression, display, operation, e); - } catch (CalculatorEvalException e) { - handleEvaluationException(expression, display, operation, e); - } - } else { - this.display.setText(""); - this.display.setJsclOperation(operation); - this.display.setGenericResult(null); - } - - - - this.display.redraw(); - } - - private void handleEvaluationException(@NotNull String expression, - @NotNull CalculatorDisplay localDisplay, - @NotNull JsclOperation operation, - @NotNull Message e) { - Log.d(CalculatorModel.class.getName(), "Evaluation failed for : " + expression + ". Error message: " + e); - if ( StringUtils.isEmpty(localDisplay.getText()) ) { - // if previous display state was empty -> show error - localDisplay.setText(R.string.c_syntax_error); - } else { - // show previous result instead of error caption (actually previous result will be greyed) - } - localDisplay.setJsclOperation(operation); - localDisplay.setGenericResult(null); - localDisplay.setValid(false); - localDisplay.setErrorMessage(e.getLocalizedMessage()); - } - - public void clear() { - if (!StringUtils.isEmpty(editor.getText()) || !StringUtils.isEmpty(display.getText())) { - editor.getText().clear(); - display.setText(""); - saveHistoryState(); - } - } - - public void processDigitButtonAction(@Nullable final String text) { - processDigitButtonAction(text, true); - } - - public void processDigitButtonAction(@Nullable final String text, boolean delayEvaluate) { - - if (!StringUtils.isEmpty(text)) { - doTextOperation(new CalculatorModel.TextOperation() { - - @Override - public void doOperation(@NotNull EditText editor) { - int cursorPositionOffset = 0; - final StringBuilder textToBeInserted = new StringBuilder(text); - - final MathType.Result mathType = MathType.getType(text, 0, false); - switch (mathType.getMathType()) { - case function: - textToBeInserted.append("()"); - cursorPositionOffset = -1; - break; - case operator: - textToBeInserted.append("()"); - cursorPositionOffset = -1; - break; - case comma: - textToBeInserted.append(" "); - break; - } - - if (cursorPositionOffset == 0) { - if (MathType.openGroupSymbols.contains(text)) { - cursorPositionOffset = -1; - } - } - - editor.getText().insert(editor.getSelectionStart(), textToBeInserted.toString()); - editor.setSelection(editor.getSelectionStart() + cursorPositionOffset, editor.getSelectionEnd() + cursorPositionOffset); - } - }, delayEvaluate); - } - } - - public static interface TextOperation { - - void doOperation(@NotNull EditText editor); - - } - - @Override - public void doHistoryAction(@NotNull HistoryAction historyAction) { - synchronized (CalculatorHistory.instance) { - if (CalculatorHistory.instance.isActionAvailable(historyAction)) { - final CalculatorHistoryState newState = CalculatorHistory.instance.doAction(historyAction, getCurrentHistoryState()); - if (newState != null) { - setCurrentHistoryState(newState); - } - } - } - } - - @Override - public void setCurrentHistoryState(@NotNull CalculatorHistoryState editorHistoryState) { - synchronized (CalculatorHistory.instance) { - Log.d(this.getClass().getName(), "Saved history found: " + editorHistoryState); - - editorHistoryState.setValuesFromHistory(new TextViewEditorAdapter(this.editor), this.display); - - final String expression = this.editor.getText().toString(); - if ( !StringUtils.isEmpty(expression) ) { - if ( StringUtils.isEmpty(this.display.getText().toString()) ) { - evaluate(false, expression, this.display.getJsclOperation(), editorHistoryState); - } - } - - editor.redraw(); - display.redraw(); - } - } - - @Override - @NotNull - public CalculatorHistoryState getCurrentHistoryState() { - synchronized (CalculatorHistory.instance) { - return CalculatorHistoryState.newInstance(new TextViewEditorAdapter(this.editor), this.display); - } - } - - @NotNull - public CalculatorDisplay getDisplay() { - return display; - } - - private static class CalculatorDisplayOnClickListener implements View.OnClickListener { - - @NotNull - private final Activity activity; - - public CalculatorDisplayOnClickListener(@NotNull Activity activity) { - this.activity = activity; - } - - @Override - public void onClick(View v) { - if (v instanceof CalculatorDisplay) { - final CalculatorDisplay cd = (CalculatorDisplay) v; - - if (cd.isValid()) { - final List filteredMenuItems = new ArrayList(CalculatorDisplay.MenuItem.values().length); - for (CalculatorDisplay.MenuItem menuItem : CalculatorDisplay.MenuItem.values()) { - if (menuItem.isItemVisible(cd)) { - filteredMenuItems.add(menuItem); - } - } - - if (!filteredMenuItems.isEmpty()) { - AMenuBuilder.newInstance(activity, MenuImpl.newInstance(filteredMenuItems)).create(cd).show(); - } - - } else { - final String errorMessage = cd.getErrorMessage(); - if (errorMessage != null) { - showEvaluationError(activity, errorMessage); - } - } - } - } - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + */ + +package org.solovyev.android.calculator; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Handler; +import android.text.ClipboardManager; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.CursorControl; +import org.solovyev.android.calculator.history.CalculatorHistory; +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.history.HistoryControl; +import org.solovyev.android.menu.AMenuBuilder; +import org.solovyev.android.menu.MenuImpl; +import org.solovyev.common.MutableObject; +import org.solovyev.common.history.HistoryAction; +import org.solovyev.common.msg.Message; +import org.solovyev.common.text.StringUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * User: serso + * Date: 9/12/11 + * Time: 11:15 PM + */ +public enum CalculatorModel implements CursorControl, HistoryControl, CalculatorEngineControl { + + instance; + + // millis to wait before evaluation after user edit action + public static final int EVAL_DELAY_MILLIS = 0; + + @NotNull + private CalculatorEditor editor; + + @NotNull + private CalculatorDisplay display; + + @NotNull + private CalculatorEngine calculatorEngine; + + public CalculatorModel init(@NotNull final Activity activity, @NotNull SharedPreferences preferences, @NotNull CalculatorEngine calculator) { + Log.d(this.getClass().getName(), "CalculatorModel initialization with activity: " + activity); + this.calculatorEngine = calculator; + + this.editor = (CalculatorEditor) activity.findViewById(R.id.calculatorEditor); + this.editor.init(preferences); + preferences.registerOnSharedPreferenceChangeListener(editor); + + this.display = (CalculatorDisplay) activity.findViewById(R.id.calculatorDisplay); + this.display.setOnClickListener(new CalculatorDisplayOnClickListener(activity)); + + final CalculatorHistoryState lastState = CalculatorHistory.instance.getLastHistoryState(); + if (lastState == null) { + saveHistoryState(); + } else { + setCurrentHistoryState(lastState); + } + + + return this; + } + + private static void showEvaluationError(@NotNull Activity activity, @NotNull final String errorMessage) { + final LayoutInflater layoutInflater = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE); + + final View errorMessageView = layoutInflater.inflate(R.layout.display_error_message, null); + ((TextView) errorMessageView.findViewById(R.id.error_message_text_view)).setText(errorMessage); + + final AlertDialog.Builder builder = new AlertDialog.Builder(activity) + .setPositiveButton(R.string.c_cancel, null) + .setView(errorMessageView); + + builder.create().show(); + } + + public void copyResult(@NotNull Context context) { + copyResult(context, display); + } + + public static void copyResult(@NotNull Context context, @NotNull final CalculatorDisplay display) { + if (display.isValid()) { + final CharSequence text = display.getText(); + if (!StringUtils.isEmpty(text)) { + final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE); + clipboard.setText(text.toString()); + Toast.makeText(context, context.getText(R.string.c_result_copied), Toast.LENGTH_SHORT).show(); + } + } + } + + private void saveHistoryState() { + CalculatorHistory.instance.addState(getCurrentHistoryState()); + } + + public void setCursorOnStart() { + editor.setSelection(0); + } + + public void setCursorOnEnd() { + editor.setSelection(editor.getText().length()); + } + + public void moveCursorLeft() { + if (editor.getSelectionStart() > 0) { + editor.setSelection(editor.getSelectionStart() - 1); + } + } + + public void moveCursorRight() { + if (editor.getSelectionStart() < editor.getText().length()) { + editor.setSelection(editor.getSelectionStart() + 1); + } + } + + public void doTextOperation(@NotNull TextOperation operation) { + doTextOperation(operation, true); + } + + public void doTextOperation(@NotNull TextOperation operation, boolean delayEvaluate) { + doTextOperation(operation, delayEvaluate, JsclOperation.numeric, false); + } + + public void doTextOperation(@NotNull TextOperation operation, boolean delayEvaluate, @NotNull JsclOperation jsclOperation, boolean forceEval) { + final String editorStateBefore = this.editor.getText().toString(); + + Log.d(CalculatorModel.class.getName(), "Editor state changed before '" + editorStateBefore + "'"); + operation.doOperation(this.editor); + //Log.d(CalculatorModel.class.getName(), "Doing text operation" + StringUtils.fromStackTrace(Thread.currentThread().getStackTrace())); + + final String editorStateAfter = this.editor.getText().toString(); + if (forceEval ||!editorStateBefore.equals(editorStateAfter)) { + + editor.redraw(); + + evaluate(delayEvaluate, editorStateAfter, jsclOperation, null); + } + } + + @NotNull + private final static MutableObject pendingOperation = new MutableObject(); + + private void evaluate(boolean delayEvaluate, + @NotNull final String expression, + @NotNull final JsclOperation operation, + @Nullable CalculatorHistoryState historyState) { + + final CalculatorHistoryState localHistoryState; + if (historyState == null) { + //this.display.setText(""); + localHistoryState = getCurrentHistoryState(); + } else { + this.display.setText(historyState.getDisplayState().getEditorState().getText()); + localHistoryState = historyState; + } + + pendingOperation.setObject(new Runnable() { + @Override + public void run() { + // allow only one runner at one time + synchronized (pendingOperation) { + //lock all operations with history + if (pendingOperation.getObject() == this) { + // actually nothing shall be logged while text operations are done + evaluate(expression, operation, this); + + if (pendingOperation.getObject() == this) { + // todo serso: of course there is small probability that someone will set pendingOperation after if statement but before .setObject(null) + pendingOperation.setObject(null); + localHistoryState.setDisplayState(getCurrentHistoryState().getDisplayState()); + } + } + } + } + }); + + if (delayEvaluate) { + if (historyState == null) { + CalculatorHistory.instance.addState(localHistoryState); + } + // todo serso: this is not correct - operation is processing still in the same thread + new Handler().postDelayed(pendingOperation.getObject(), EVAL_DELAY_MILLIS); + } else { + pendingOperation.getObject().run(); + if (historyState == null) { + CalculatorHistory.instance.addState(localHistoryState); + } + } + } + + @Override + public void evaluate() { + evaluate(false, this.editor.getText().toString(), JsclOperation.numeric, null); + } + + public void evaluate(@NotNull JsclOperation operation) { + evaluate(false, this.editor.getText().toString(), operation, null); + } + + @Override + public void simplify() { + evaluate(false, this.editor.getText().toString(), JsclOperation.simplify, null); + } + + private void evaluate(@Nullable final String expression, + @NotNull JsclOperation operation, + @NotNull Runnable currentRunner) { + + if (!StringUtils.isEmpty(expression)) { + try { + Log.d(CalculatorModel.class.getName(), "Trying to evaluate '" + operation + "': " + expression /*+ StringUtils.fromStackTrace(Thread.currentThread().getStackTrace())*/); + final CalculatorEngine.Result result = calculatorEngine.evaluate(operation, expression); + + // todo serso: second condition might replaced with expression.equals(this.editor.getText().toString()) ONLY if expression will be formatted with text highlighter + if (currentRunner == pendingOperation.getObject() && this.editor.getText().length() > 0) { + display.setText(result.getResult()); + } else { + display.setText(""); + } + display.setJsclOperation(result.getUserOperation()); + display.setGenericResult(result.getGenericResult()); + } catch (CalculatorParseException e) { + handleEvaluationException(expression, display, operation, e); + } catch (CalculatorEvalException e) { + handleEvaluationException(expression, display, operation, e); + } + } else { + this.display.setText(""); + this.display.setJsclOperation(operation); + this.display.setGenericResult(null); + } + + + + this.display.redraw(); + } + + private void handleEvaluationException(@NotNull String expression, + @NotNull CalculatorDisplay localDisplay, + @NotNull JsclOperation operation, + @NotNull Message e) { + Log.d(CalculatorModel.class.getName(), "Evaluation failed for : " + expression + ". Error message: " + e); + if ( StringUtils.isEmpty(localDisplay.getText()) ) { + // if previous display state was empty -> show error + localDisplay.setText(R.string.c_syntax_error); + } else { + // show previous result instead of error caption (actually previous result will be greyed) + } + localDisplay.setJsclOperation(operation); + localDisplay.setGenericResult(null); + localDisplay.setValid(false); + localDisplay.setErrorMessage(e.getLocalizedMessage()); + } + + public void clear() { + if (!StringUtils.isEmpty(editor.getText()) || !StringUtils.isEmpty(display.getText())) { + editor.getText().clear(); + display.setText(""); + saveHistoryState(); + } + } + + public void processDigitButtonAction(@Nullable final String text) { + processDigitButtonAction(text, true); + } + + public void processDigitButtonAction(@Nullable final String text, boolean delayEvaluate) { + + if (!StringUtils.isEmpty(text)) { + doTextOperation(new CalculatorModel.TextOperation() { + + @Override + public void doOperation(@NotNull EditText editor) { + int cursorPositionOffset = 0; + final StringBuilder textToBeInserted = new StringBuilder(text); + + final MathType.Result mathType = MathType.getType(text, 0, false); + switch (mathType.getMathType()) { + case function: + textToBeInserted.append("()"); + cursorPositionOffset = -1; + break; + case operator: + textToBeInserted.append("()"); + cursorPositionOffset = -1; + break; + case comma: + textToBeInserted.append(" "); + break; + } + + if (cursorPositionOffset == 0) { + if (MathType.openGroupSymbols.contains(text)) { + cursorPositionOffset = -1; + } + } + + editor.getText().insert(editor.getSelectionStart(), textToBeInserted.toString()); + editor.setSelection(editor.getSelectionStart() + cursorPositionOffset, editor.getSelectionEnd() + cursorPositionOffset); + } + }, delayEvaluate); + } + } + + public static interface TextOperation { + + void doOperation(@NotNull EditText editor); + + } + + @Override + public void doHistoryAction(@NotNull HistoryAction historyAction) { + synchronized (CalculatorHistory.instance) { + if (CalculatorHistory.instance.isActionAvailable(historyAction)) { + final CalculatorHistoryState newState = CalculatorHistory.instance.doAction(historyAction, getCurrentHistoryState()); + if (newState != null) { + setCurrentHistoryState(newState); + } + } + } + } + + @Override + public void setCurrentHistoryState(@NotNull CalculatorHistoryState editorHistoryState) { + synchronized (CalculatorHistory.instance) { + Log.d(this.getClass().getName(), "Saved history found: " + editorHistoryState); + + editorHistoryState.setValuesFromHistory(new TextViewEditorAdapter(this.editor), this.display); + + final String expression = this.editor.getText().toString(); + if ( !StringUtils.isEmpty(expression) ) { + if ( StringUtils.isEmpty(this.display.getText().toString()) ) { + evaluate(false, expression, this.display.getJsclOperation(), editorHistoryState); + } + } + + editor.redraw(); + display.redraw(); + } + } + + @Override + @NotNull + public CalculatorHistoryState getCurrentHistoryState() { + synchronized (CalculatorHistory.instance) { + return CalculatorHistoryState.newInstance(new TextViewEditorAdapter(this.editor), this.display); + } + } + + @NotNull + public CalculatorDisplay getDisplay() { + return display; + } + + private static class CalculatorDisplayOnClickListener implements View.OnClickListener { + + @NotNull + private final Activity activity; + + public CalculatorDisplayOnClickListener(@NotNull Activity activity) { + this.activity = activity; + } + + @Override + public void onClick(View v) { + if (v instanceof CalculatorDisplay) { + final CalculatorDisplay cd = (CalculatorDisplay) v; + + if (cd.isValid()) { + final List filteredMenuItems = new ArrayList(CalculatorDisplay.MenuItem.values().length); + for (CalculatorDisplay.MenuItem menuItem : CalculatorDisplay.MenuItem.values()) { + if (menuItem.isItemVisible(cd)) { + filteredMenuItems.add(menuItem); + } + } + + if (!filteredMenuItems.isEmpty()) { + AMenuBuilder.newInstance(activity, MenuImpl.newInstance(filteredMenuItems)).create(cd).show(); + } + + } else { + final String errorMessage = cd.getErrorMessage(); + if (errorMessage != null) { + showEvaluationError(activity, errorMessage); + } + } + } + } + } +} 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 3448880e..fb385c9a 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 @@ -16,9 +16,11 @@ 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.CalculatorParseException; import org.solovyev.android.calculator.JCalculatorEngine; import org.solovyev.android.calculator.R; 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; diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/ToJsclTextProcessor.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/model/ToJsclTextProcessor.java index 12067da4..630ad312 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/ToJsclTextProcessor.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/model/ToJsclTextProcessor.java @@ -9,7 +9,9 @@ package org.solovyev.android.calculator.model; 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.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; 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..0e2040eb 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.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; + } + } +} 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 a8fa7a46..4c94fe06 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 @@ -12,7 +12,7 @@ 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.CalculatorParseException; import org.solovyev.android.calculator.model.ToJsclTextProcessor; import org.solovyev.common.MutableObject; import org.solovyev.common.text.StringUtils; 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..2ebf22da 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,240 @@ -/* - * 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.CalculatorParseException; +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/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/model/CalculatorEngineTest.java b/calculatorpp/src/test/java/org/solovyev/android/calculator/model/CalculatorEngineTest.java index 449ed9e2..187c504d 100644 --- a/calculatorpp/src/test/java/org/solovyev/android/calculator/model/CalculatorEngineTest.java +++ b/calculatorpp/src/test/java/org/solovyev/android/calculator/model/CalculatorEngineTest.java @@ -1,443 +1,444 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - */ - -package org.solovyev.android.calculator.model; - -import jscl.AngleUnit; -import jscl.JsclMathEngine; -import jscl.NumeralBase; -import jscl.math.Expression; -import jscl.math.Generic; -import jscl.math.function.Constant; -import jscl.math.function.CustomFunction; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; -import org.solovyev.android.calculator.jscl.JsclOperation; - -import java.text.DecimalFormatSymbols; -import java.util.Locale; - -import static junit.framework.Assert.fail; - -/** - * User: serso - * Date: 9/17/11 - * Time: 9:47 PM - */ - -public class CalculatorEngineTest { - - @BeforeClass - public static void setUp() throws Exception { - CalculatorEngine.instance.init(null, null); - CalculatorEngine.instance.setPrecision(3); - CalculatorEngine.instance.setThreadKiller(new CalculatorEngine.ThreadKillerImpl()); - } - - @Test - public void testDegrees() throws Exception { - final CalculatorEngine cm = CalculatorEngine.instance; - - final AngleUnit defaultAngleUnit = cm.getEngine().getAngleUnits(); - try { - cm.getEngine().setAngleUnits(AngleUnit.rad); - cm.setPrecision(3); - try { - Assert.assertEquals("0.017", cm.evaluate(JsclOperation.numeric, "°")); - fail(); - } catch (CalculatorParseException e) { - - } - - Assert.assertEquals("0.017", cm.evaluate(JsclOperation.numeric, "1°").getResult()); - Assert.assertEquals("0.349", cm.evaluate(JsclOperation.numeric, "20.0°").getResult()); - Assert.assertEquals("0.5", cm.evaluate(JsclOperation.numeric, "sin(30°)").getResult()); - Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "asin(sin(30°))").getResult()); - Assert.assertEquals("∂(cos(t), t, t, 1°)", cm.evaluate(JsclOperation.numeric, "∂(cos(t),t,t,1°)").getResult()); - - Assert.assertEquals("∂(cos(t), t, t, 1°)", cm.evaluate(JsclOperation.simplify, "∂(cos(t),t,t,1°)").getResult()); - } finally { - cm.getEngine().setAngleUnits(defaultAngleUnit); - } - } - - @Test - public void testLongExecution() throws Exception { - final CalculatorEngine cm = CalculatorEngine.instance; - - try { - cm.evaluate(JsclOperation.numeric, "3^10^10^10"); - Assert.fail(); - } catch (CalculatorParseException e) { - if (e.getMessageCode().equals(Messages.msg_3)) { - - } else { - System.out.print(e.getCause().getMessage()); - Assert.fail(); - } - } - - try { - cm.evaluate(JsclOperation.numeric, "9999999!"); - Assert.fail(); - } catch (CalculatorParseException e) { - if (e.getMessageCode().equals(Messages.msg_3)) { - - } else { - System.out.print(e.getCause().getMessage()); - Assert.fail(); - } - } - - /*final long start = System.currentTimeMillis(); - try { - cm.evaluate(JsclOperation.numeric, "3^10^10^10"); - Assert.fail(); - } catch (ParseException e) { - if (e.getMessage().startsWith("Too long calculation")) { - final long end = System.currentTimeMillis(); - Assert.assertTrue(end - start < 1000); - } else { - Assert.fail(); - } - }*/ - - } - - @Test - public void testEvaluate() throws Exception { - final CalculatorEngine cm = CalculatorEngine.instance; - - Assert.assertEquals("cos(t)+10%", cm.evaluate(JsclOperation.simplify, "cos(t)+10%").getResult()); - - final Generic expression = cm.getEngine().simplifyGeneric("cos(t)+10%"); - expression.substitute(new Constant("t"), Expression.valueOf(100d)); - - Assert.assertEquals("it", cm.evaluate(JsclOperation.simplify, "it").getResult()); - Assert.assertEquals("10%", cm.evaluate(JsclOperation.simplify, "10%").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(0, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq(1, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq( 1, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.simplify, "eq( 1, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "lg(10)").getResult()); - Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "2+2").getResult()); - final AngleUnit defaultAngleUnit = cm.getEngine().getAngleUnits(); - try { - cm.getEngine().setAngleUnits(AngleUnit.rad); - Assert.assertEquals("-0.757", cm.evaluate(JsclOperation.numeric, "sin(4)").getResult()); - Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "asin(0.5)").getResult()); - Assert.assertEquals("-0.396", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)").getResult()); - Assert.assertEquals("-0.56", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)√(2)").getResult()); - Assert.assertEquals("-0.56", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)√(2)").getResult()); - } finally { - cm.getEngine().setAngleUnits(defaultAngleUnit); - } - Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "e^2").getResult()); - Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "exp(1)^2").getResult()); - Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "exp(2)").getResult()); - Assert.assertEquals("2+i", cm.evaluate(JsclOperation.numeric, "2*1+√(-1)").getResult()); - try { - cm.getEngine().setAngleUnits(AngleUnit.rad); - Assert.assertEquals("0.921+Πi", cm.evaluate(JsclOperation.numeric, "ln(5cosh(38π√(2cos(2))))").getResult()); - Assert.assertEquals("-3.41+3.41i", cm.evaluate(JsclOperation.numeric, "(5tan(2i)+2i)/(1-i)").getResult()); - } finally { - cm.getEngine().setAngleUnits(defaultAngleUnit); - } - Assert.assertEquals("7.389i", cm.evaluate(JsclOperation.numeric, "iexp(2)").getResult()); - Assert.assertEquals("2+7.389i", cm.evaluate(JsclOperation.numeric, "2+iexp(2)").getResult()); - Assert.assertEquals("2+7.389i", cm.evaluate(JsclOperation.numeric, "2+√(-1)exp(2)").getResult()); - Assert.assertEquals("2-2.5i", cm.evaluate(JsclOperation.numeric, "2-2.5i").getResult()); - Assert.assertEquals("-2-2.5i", cm.evaluate(JsclOperation.numeric, "-2-2.5i").getResult()); - Assert.assertEquals("-2+2.5i", cm.evaluate(JsclOperation.numeric, "-2+2.5i").getResult()); - Assert.assertEquals("-2+2.1i", cm.evaluate(JsclOperation.numeric, "-2+2.1i").getResult()); - Assert.assertEquals("-0.1-0.2i", cm.evaluate(JsclOperation.numeric, "(1-i)/(2+6i)").getResult()); - - junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "4!").getResult()); - junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "(2+2)!").getResult()); - junit.framework.Assert.assertEquals("120", cm.evaluate(JsclOperation.numeric, "(2+2+1)!").getResult()); - junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "(2.0+2.0)!").getResult()); - junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "4.0!").getResult()); - junit.framework.Assert.assertEquals("720", cm.evaluate(JsclOperation.numeric, "(3!)!").getResult()); - junit.framework.Assert.assertEquals("36", Expression.valueOf("3!^2").numeric().toString()); - junit.framework.Assert.assertEquals("3", Expression.valueOf("cubic(27)").numeric().toString()); - try { - junit.framework.Assert.assertEquals("√(-1)!", cm.evaluate(JsclOperation.numeric, "i!").getResult()); - fail(); - } catch (CalculatorParseException e) { - } - - junit.framework.Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "(π/π)!").getResult()); - - try { - junit.framework.Assert.assertEquals("i", cm.evaluate(JsclOperation.numeric, "(-1)i!").getResult()); - fail(); - } catch (CalculatorParseException e) { - - } - junit.framework.Assert.assertEquals("24i", cm.evaluate(JsclOperation.numeric, "4!i").getResult()); - - CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("si", 5d)); - - try { - cm.getEngine().setAngleUnits(AngleUnit.rad); - Assert.assertEquals("0.451", cm.evaluate(JsclOperation.numeric, "acos(0.8999999999999811)").getResult()); - Assert.assertEquals("-0.959", cm.evaluate(JsclOperation.numeric, "sin(5)").getResult()); - Assert.assertEquals("-4.795", cm.evaluate(JsclOperation.numeric, "sin(5)si").getResult()); - Assert.assertEquals("-23.973", cm.evaluate(JsclOperation.numeric, "sisin(5)si").getResult()); - Assert.assertEquals("-23.973", cm.evaluate(JsclOperation.numeric, "si*sin(5)si").getResult()); - Assert.assertEquals("-3.309", cm.evaluate(JsclOperation.numeric, "sisin(5si)si").getResult()); - } finally { - cm.getEngine().setAngleUnits(defaultAngleUnit); - } - - CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("s", 1d)); - Assert.assertEquals("5", cm.evaluate(JsclOperation.numeric, "si").getResult()); - - CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("k", 3.5d)); - CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("k1", 4d)); - Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "k11").getResult()); - - CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t", (String) null)); - Assert.assertEquals("11t", cm.evaluate(JsclOperation.numeric, "t11").getResult()); - Assert.assertEquals("11et", cm.evaluate(JsclOperation.numeric, "t11e").getResult()); - Assert.assertEquals("∞", cm.evaluate(JsclOperation.numeric, "∞").getResult()); - Assert.assertEquals("∞", cm.evaluate(JsclOperation.numeric, "Infinity").getResult()); - Assert.assertEquals("11∞t", cm.evaluate(JsclOperation.numeric, "t11∞").getResult()); - Assert.assertEquals("-t+t^3", cm.evaluate(JsclOperation.numeric, "t(t-1)(t+1)").getResult()); - - Assert.assertEquals("100", cm.evaluate(JsclOperation.numeric, "0.1E3").getResult()); - Assert.assertEquals("3.957", cm.evaluate(JsclOperation.numeric, "ln(8)lg(8)+ln(8)").getResult()); - - Assert.assertEquals("0.933", cm.evaluate(JsclOperation.numeric, "0x:E/0x:F").getResult()); - - try { - cm.getEngine().setNumeralBase(NumeralBase.hex); - Assert.assertEquals("E/F", cm.evaluate(JsclOperation.numeric, "0x:E/0x:F").getResult()); - Assert.assertEquals("E/F", cm.evaluate(JsclOperation.simplify, "0x:E/0x:F").getResult()); - Assert.assertEquals("E/F", cm.evaluate(JsclOperation.numeric, "E/F").getResult()); - Assert.assertEquals("E/F", cm.evaluate(JsclOperation.simplify, "E/F").getResult()); - } finally { - cm.getEngine().setNumeralBase(NumeralBase.dec); - } - - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "((((((0))))))").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((0))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))").getResult()); - - - /* Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "30°").getResult()); - Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "(10+20)°").getResult()); - Assert.assertEquals("1.047", cm.evaluate(JsclOperation.numeric, "(10+20)°*2").getResult()); - try { - Assert.assertEquals("0.278", cm.evaluate(JsclOperation.numeric, "30°^2").getResult()); - junit.framework.Assert.fail(); - } catch (ParseException e) { - if ( !e.getMessage().equals("Power operation after postfix function is currently unsupported!") ) { - junit.framework.Assert.fail(); - } - }*/ - -/* try { - cm.setTimeout(5000); - Assert.assertEquals("2", cm.evaluate(JsclOperation.numeric, "2!").getResult()); - } finally { - cm.setTimeout(3000); - }*/ - - CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t", (String) null)); - Assert.assertEquals("2t", cm.evaluate(JsclOperation.simplify, "∂(t^2,t)").getResult()); - Assert.assertEquals("2t", cm.evaluate(JsclOperation.numeric, "∂(t^2,t)").getResult()); - CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t", "2")); - Assert.assertEquals("2t", cm.evaluate(JsclOperation.simplify, "∂(t^2,t)").getResult()); - Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "∂(t^2,t)").getResult()); - - Assert.assertEquals("-x+x*ln(x)", cm.getEngine().simplify("∫(ln(x), x)")); - Assert.assertEquals("-(x-x*ln(x))/(ln(2)+ln(5))", cm.getEngine().simplify("∫(log(10, x), x)")); - - Assert.assertEquals("∫((ln(2)+ln(5))/ln(x), x)", cm.getEngine().simplify("∫(ln(10)/ln(x), x)")); - Assert.assertEquals("∫(ln(10)/ln(x), x)", Expression.valueOf("∫(log(x, 10), x)").expand().toString()); - Assert.assertEquals("∫((ln(2)+ln(5))/ln(x), x)", cm.getEngine().simplify("∫(log(x, 10), x)")); - } - - @Test - public void testFormatting() throws Exception { - final CalculatorEngine ce = CalculatorEngine.instance; - - Assert.assertEquals("12 345", ce.evaluate(JsclOperation.simplify, "12345").getResult()); - - } - - @Test - public void testI() throws CalculatorParseException, CalculatorEvalException { - final CalculatorEngine cm = CalculatorEngine.instance; - - Assert.assertEquals("-i", cm.evaluate(JsclOperation.numeric, "i^3").getResult()); - for (int i = 0; i < 1000; i++) { - double real = (Math.random()-0.5) * 1000; - double imag = (Math.random()-0.5) * 1000; - int exp = (int)(Math.random() * 10); - - final StringBuilder sb = new StringBuilder(); - sb.append(real); - if ( imag > 0 ) { - sb.append("+"); - } - sb.append(imag); - sb.append("^").append(exp); - try { - cm.evaluate(JsclOperation.numeric, sb.toString()).getResult(); - } catch (Throwable e) { - fail(sb.toString()); - } - } - } - - @Test - public void testEmptyFunction() throws Exception { - final CalculatorEngine cm = CalculatorEngine.instance; - try { - cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(acos(acos(acos(acos(acos(acos(acos(acos(cos(cos(cos(cos(cosh(acos(cos(cos(cos(cos(cos(acos(acos(acos(acos(acos(acos(acos(acos(cos(cos(cos(cos(cosh(acos(cos())))))))))))))))))))))))))))))))))))))"); - Assert.fail(); - } catch (CalculatorParseException e) { - } - Assert.assertEquals("0.34+1.382i", cm.evaluate(JsclOperation.numeric, "ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(100)))))))))))))))").getResult()); - try { - cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos())))))))))))))))))))))))))))))))))))"); - Assert.fail(); - } catch (CalculatorParseException e) { - } - - final AngleUnit defaultAngleUnit = cm.getEngine().getAngleUnits(); - try { - cm.getEngine().setAngleUnits(AngleUnit.rad); - Assert.assertEquals("0.739", cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(1))))))))))))))))))))))))))))))))))))").getResult()); - } finally { - cm.getEngine().setAngleUnits(defaultAngleUnit); - } - - CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("si", 5d)); - Assert.assertEquals("5", cm.evaluate(JsclOperation.numeric, "si").getResult()); - - try { - cm.evaluate(JsclOperation.numeric, "sin"); - Assert.fail(); - } catch (CalculatorParseException e) { - } - } - - @Test - public void testRounding() throws Exception { - final CalculatorEngine cm = CalculatorEngine.instance; - - try { - DecimalFormatSymbols decimalGroupSymbols = new DecimalFormatSymbols(Locale.getDefault()); - decimalGroupSymbols.setDecimalSeparator('.'); - decimalGroupSymbols.setGroupingSeparator('\''); - cm.setDecimalGroupSymbols(decimalGroupSymbols); - cm.setPrecision(2); - Assert.assertEquals("12'345'678.9", cm.evaluate(JsclOperation.numeric, "1.23456789E7").getResult()); - cm.setPrecision(10); - Assert.assertEquals("12'345'678.9", cm.evaluate(JsclOperation.numeric, "1.23456789E7").getResult()); - Assert.assertEquals("123'456'789", cm.evaluate(JsclOperation.numeric, "1.234567890E8").getResult()); - Assert.assertEquals("1'234'567'890.1", cm.evaluate(JsclOperation.numeric, "1.2345678901E9").getResult()); - } finally { - cm.setPrecision(3); - DecimalFormatSymbols decimalGroupSymbols = new DecimalFormatSymbols(Locale.getDefault()); - decimalGroupSymbols.setDecimalSeparator('.'); - decimalGroupSymbols.setGroupingSeparator(JsclMathEngine.GROUPING_SEPARATOR_DEFAULT.charAt(0)); - cm.setDecimalGroupSymbols(decimalGroupSymbols); - } - } - - @Test - public void testComparisonFunction() throws Exception { - final CalculatorEngine cm = CalculatorEngine.instance; - - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(0, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq(1, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq(1, 1.0)").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(1, 1.000000000000001)").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(1, 0)").getResult()); - - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "lt(0, 1)").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "lt(1, 1)").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "lt(1, 0)").getResult()); - - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "gt(0, 1)").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "gt(1, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "gt(1, 0)").getResult()); - - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ne(0, 1)").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ne(1, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ne(1, 0)").getResult()); - - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "le(0, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "le(1, 1)").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "le(1, 0)").getResult()); - - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ge(0, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ge(1, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ge(1, 0)").getResult()); - - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ap(0, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ap(1, 1)").getResult()); - //Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ap(1, 1.000000000000001)").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ap(1, 0)").getResult()); - - } - - - @Test - public void testNumeralSystems() throws Exception { - final CalculatorEngine cm = CalculatorEngine.instance; - - Assert.assertEquals("11 259 375", cm.evaluate(JsclOperation.numeric, "0x:ABCDEF").getResult()); - Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "0x:ABCDEF*e").getResult()); - Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "e*0x:ABCDEF").getResult()); - Assert.assertEquals("e", cm.evaluate(JsclOperation.numeric, "e*0x:ABCDEF/0x:ABCDEF").getResult()); - Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "0x:ABCDEF*e*0x:ABCDEF/0x:ABCDEF").getResult()); - Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "c+0x:ABCDEF*e*0x:ABCDEF/0x:ABCDEF-c+0x:C-0x:C").getResult()); - Assert.assertEquals("1 446 257 064 651.832", cm.evaluate(JsclOperation.numeric, "28*28 * sin(28) - 0b:1101 + √(28) + exp ( 28) ").getResult()); - Assert.assertEquals("13", cm.evaluate(JsclOperation.numeric, "0b:1101").getResult()); - - try { - cm.evaluate(JsclOperation.numeric, "0b:π").getResult(); - Assert.fail(); - } catch (CalculatorParseException e) { - // ok - } - - final NumeralBase defaultNumeralBase = cm.getEngine().getNumeralBase(); - try{ - cm.getEngine().setNumeralBase(NumeralBase.bin); - Assert.assertEquals("101", cm.evaluate(JsclOperation.numeric, "10+11").getResult()); - Assert.assertEquals("10/11", cm.evaluate(JsclOperation.numeric, "10/11").getResult()); - - cm.getEngine().setNumeralBase(NumeralBase.hex); - Assert.assertEquals("63 7B", cm.evaluate(JsclOperation.numeric, "56CE+CAD").getResult()); - Assert.assertEquals("E", cm.evaluate(JsclOperation.numeric, "E").getResult()); - } finally { - cm.setNumeralBase(defaultNumeralBase); - } - } - - @Test - public void testLog() throws Exception { - final CalculatorEngine cm = CalculatorEngine.instance; - - Assert.assertEquals("∞", Expression.valueOf("1/0").numeric().toString()); - Assert.assertEquals("∞", Expression.valueOf("ln(10)/ln(1)").numeric().toString()); - - // logarithm - Assert.assertEquals("ln(x)/ln(base)", ((CustomFunction) cm.getFunctionsRegistry().get("log")).getContent()); - Assert.assertEquals("∞", cm.evaluate(JsclOperation.numeric, "log(1, 10)").getResult()); - Assert.assertEquals("3.322", cm.evaluate(JsclOperation.numeric, "log(2, 10)").getResult()); - Assert.assertEquals("1.431", cm.evaluate(JsclOperation.numeric, "log(5, 10)").getResult()); - Assert.assertEquals("0.96", cm.evaluate(JsclOperation.numeric, "log(11, 10)").getResult()); - Assert.assertEquals("1/(bln(a))", cm.evaluate(JsclOperation.simplify, "∂(log(a, b), b)").getResult()); - Assert.assertEquals("-ln(b)/(aln(a)^2)", cm.evaluate(JsclOperation.simplify, "∂(log(a, b), a)").getResult()); - - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + */ + +package org.solovyev.android.calculator.model; + +import jscl.AngleUnit; +import jscl.JsclMathEngine; +import jscl.NumeralBase; +import jscl.math.Expression; +import jscl.math.Generic; +import jscl.math.function.Constant; +import jscl.math.function.CustomFunction; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.solovyev.android.calculator.CalculatorParseException; +import org.solovyev.android.calculator.jscl.JsclOperation; + +import java.text.DecimalFormatSymbols; +import java.util.Locale; + +import static junit.framework.Assert.fail; + +/** + * User: serso + * Date: 9/17/11 + * Time: 9:47 PM + */ + +public class CalculatorEngineTest { + + @BeforeClass + public static void setUp() throws Exception { + CalculatorEngine.instance.init(null, null); + CalculatorEngine.instance.setPrecision(3); + CalculatorEngine.instance.setThreadKiller(new CalculatorEngine.ThreadKillerImpl()); + } + + @Test + public void testDegrees() throws Exception { + final CalculatorEngine cm = CalculatorEngine.instance; + + final AngleUnit defaultAngleUnit = cm.getEngine().getAngleUnits(); + try { + cm.getEngine().setAngleUnits(AngleUnit.rad); + cm.setPrecision(3); + try { + Assert.assertEquals("0.017", cm.evaluate(JsclOperation.numeric, "°")); + fail(); + } catch (CalculatorParseException e) { + + } + + Assert.assertEquals("0.017", cm.evaluate(JsclOperation.numeric, "1°").getResult()); + Assert.assertEquals("0.349", cm.evaluate(JsclOperation.numeric, "20.0°").getResult()); + Assert.assertEquals("0.5", cm.evaluate(JsclOperation.numeric, "sin(30°)").getResult()); + Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "asin(sin(30°))").getResult()); + Assert.assertEquals("∂(cos(t), t, t, 1°)", cm.evaluate(JsclOperation.numeric, "∂(cos(t),t,t,1°)").getResult()); + + Assert.assertEquals("∂(cos(t), t, t, 1°)", cm.evaluate(JsclOperation.simplify, "∂(cos(t),t,t,1°)").getResult()); + } finally { + cm.getEngine().setAngleUnits(defaultAngleUnit); + } + } + + @Test + public void testLongExecution() throws Exception { + final CalculatorEngine cm = CalculatorEngine.instance; + + try { + cm.evaluate(JsclOperation.numeric, "3^10^10^10"); + Assert.fail(); + } catch (CalculatorParseException e) { + if (e.getMessageCode().equals(Messages.msg_3)) { + + } else { + System.out.print(e.getCause().getMessage()); + Assert.fail(); + } + } + + try { + cm.evaluate(JsclOperation.numeric, "9999999!"); + Assert.fail(); + } catch (CalculatorParseException e) { + if (e.getMessageCode().equals(Messages.msg_3)) { + + } else { + System.out.print(e.getCause().getMessage()); + Assert.fail(); + } + } + + /*final long start = System.currentTimeMillis(); + try { + cm.evaluate(JsclOperation.numeric, "3^10^10^10"); + Assert.fail(); + } catch (ParseException e) { + if (e.getMessage().startsWith("Too long calculation")) { + final long end = System.currentTimeMillis(); + Assert.assertTrue(end - start < 1000); + } else { + Assert.fail(); + } + }*/ + + } + + @Test + public void testEvaluate() throws Exception { + final CalculatorEngine cm = CalculatorEngine.instance; + + Assert.assertEquals("cos(t)+10%", cm.evaluate(JsclOperation.simplify, "cos(t)+10%").getResult()); + + final Generic expression = cm.getEngine().simplifyGeneric("cos(t)+10%"); + expression.substitute(new Constant("t"), Expression.valueOf(100d)); + + Assert.assertEquals("it", cm.evaluate(JsclOperation.simplify, "it").getResult()); + Assert.assertEquals("10%", cm.evaluate(JsclOperation.simplify, "10%").getResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(0, 1)").getResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq(1, 1)").getResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq( 1, 1)").getResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.simplify, "eq( 1, 1)").getResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "lg(10)").getResult()); + Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "2+2").getResult()); + final AngleUnit defaultAngleUnit = cm.getEngine().getAngleUnits(); + try { + cm.getEngine().setAngleUnits(AngleUnit.rad); + Assert.assertEquals("-0.757", cm.evaluate(JsclOperation.numeric, "sin(4)").getResult()); + Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "asin(0.5)").getResult()); + Assert.assertEquals("-0.396", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)").getResult()); + Assert.assertEquals("-0.56", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)√(2)").getResult()); + Assert.assertEquals("-0.56", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)√(2)").getResult()); + } finally { + cm.getEngine().setAngleUnits(defaultAngleUnit); + } + Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "e^2").getResult()); + Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "exp(1)^2").getResult()); + Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "exp(2)").getResult()); + Assert.assertEquals("2+i", cm.evaluate(JsclOperation.numeric, "2*1+√(-1)").getResult()); + try { + cm.getEngine().setAngleUnits(AngleUnit.rad); + Assert.assertEquals("0.921+Πi", cm.evaluate(JsclOperation.numeric, "ln(5cosh(38π√(2cos(2))))").getResult()); + Assert.assertEquals("-3.41+3.41i", cm.evaluate(JsclOperation.numeric, "(5tan(2i)+2i)/(1-i)").getResult()); + } finally { + cm.getEngine().setAngleUnits(defaultAngleUnit); + } + Assert.assertEquals("7.389i", cm.evaluate(JsclOperation.numeric, "iexp(2)").getResult()); + Assert.assertEquals("2+7.389i", cm.evaluate(JsclOperation.numeric, "2+iexp(2)").getResult()); + Assert.assertEquals("2+7.389i", cm.evaluate(JsclOperation.numeric, "2+√(-1)exp(2)").getResult()); + Assert.assertEquals("2-2.5i", cm.evaluate(JsclOperation.numeric, "2-2.5i").getResult()); + Assert.assertEquals("-2-2.5i", cm.evaluate(JsclOperation.numeric, "-2-2.5i").getResult()); + Assert.assertEquals("-2+2.5i", cm.evaluate(JsclOperation.numeric, "-2+2.5i").getResult()); + Assert.assertEquals("-2+2.1i", cm.evaluate(JsclOperation.numeric, "-2+2.1i").getResult()); + Assert.assertEquals("-0.1-0.2i", cm.evaluate(JsclOperation.numeric, "(1-i)/(2+6i)").getResult()); + + junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "4!").getResult()); + junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "(2+2)!").getResult()); + junit.framework.Assert.assertEquals("120", cm.evaluate(JsclOperation.numeric, "(2+2+1)!").getResult()); + junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "(2.0+2.0)!").getResult()); + junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "4.0!").getResult()); + junit.framework.Assert.assertEquals("720", cm.evaluate(JsclOperation.numeric, "(3!)!").getResult()); + junit.framework.Assert.assertEquals("36", Expression.valueOf("3!^2").numeric().toString()); + junit.framework.Assert.assertEquals("3", Expression.valueOf("cubic(27)").numeric().toString()); + try { + junit.framework.Assert.assertEquals("√(-1)!", cm.evaluate(JsclOperation.numeric, "i!").getResult()); + fail(); + } catch (CalculatorParseException e) { + } + + junit.framework.Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "(π/π)!").getResult()); + + try { + junit.framework.Assert.assertEquals("i", cm.evaluate(JsclOperation.numeric, "(-1)i!").getResult()); + fail(); + } catch (CalculatorParseException e) { + + } + junit.framework.Assert.assertEquals("24i", cm.evaluate(JsclOperation.numeric, "4!i").getResult()); + + CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("si", 5d)); + + try { + cm.getEngine().setAngleUnits(AngleUnit.rad); + Assert.assertEquals("0.451", cm.evaluate(JsclOperation.numeric, "acos(0.8999999999999811)").getResult()); + Assert.assertEquals("-0.959", cm.evaluate(JsclOperation.numeric, "sin(5)").getResult()); + Assert.assertEquals("-4.795", cm.evaluate(JsclOperation.numeric, "sin(5)si").getResult()); + Assert.assertEquals("-23.973", cm.evaluate(JsclOperation.numeric, "sisin(5)si").getResult()); + Assert.assertEquals("-23.973", cm.evaluate(JsclOperation.numeric, "si*sin(5)si").getResult()); + Assert.assertEquals("-3.309", cm.evaluate(JsclOperation.numeric, "sisin(5si)si").getResult()); + } finally { + cm.getEngine().setAngleUnits(defaultAngleUnit); + } + + CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("s", 1d)); + Assert.assertEquals("5", cm.evaluate(JsclOperation.numeric, "si").getResult()); + + CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("k", 3.5d)); + CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("k1", 4d)); + Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "k11").getResult()); + + CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t", (String) null)); + Assert.assertEquals("11t", cm.evaluate(JsclOperation.numeric, "t11").getResult()); + Assert.assertEquals("11et", cm.evaluate(JsclOperation.numeric, "t11e").getResult()); + Assert.assertEquals("∞", cm.evaluate(JsclOperation.numeric, "∞").getResult()); + Assert.assertEquals("∞", cm.evaluate(JsclOperation.numeric, "Infinity").getResult()); + Assert.assertEquals("11∞t", cm.evaluate(JsclOperation.numeric, "t11∞").getResult()); + Assert.assertEquals("-t+t^3", cm.evaluate(JsclOperation.numeric, "t(t-1)(t+1)").getResult()); + + Assert.assertEquals("100", cm.evaluate(JsclOperation.numeric, "0.1E3").getResult()); + Assert.assertEquals("3.957", cm.evaluate(JsclOperation.numeric, "ln(8)lg(8)+ln(8)").getResult()); + + Assert.assertEquals("0.933", cm.evaluate(JsclOperation.numeric, "0x:E/0x:F").getResult()); + + try { + cm.getEngine().setNumeralBase(NumeralBase.hex); + Assert.assertEquals("E/F", cm.evaluate(JsclOperation.numeric, "0x:E/0x:F").getResult()); + Assert.assertEquals("E/F", cm.evaluate(JsclOperation.simplify, "0x:E/0x:F").getResult()); + Assert.assertEquals("E/F", cm.evaluate(JsclOperation.numeric, "E/F").getResult()); + Assert.assertEquals("E/F", cm.evaluate(JsclOperation.simplify, "E/F").getResult()); + } finally { + cm.getEngine().setNumeralBase(NumeralBase.dec); + } + + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "((((((0))))))").getResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((0))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))").getResult()); + + + /* Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "30°").getResult()); + Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "(10+20)°").getResult()); + Assert.assertEquals("1.047", cm.evaluate(JsclOperation.numeric, "(10+20)°*2").getResult()); + try { + Assert.assertEquals("0.278", cm.evaluate(JsclOperation.numeric, "30°^2").getResult()); + junit.framework.Assert.fail(); + } catch (ParseException e) { + if ( !e.getMessage().equals("Power operation after postfix function is currently unsupported!") ) { + junit.framework.Assert.fail(); + } + }*/ + +/* try { + cm.setTimeout(5000); + Assert.assertEquals("2", cm.evaluate(JsclOperation.numeric, "2!").getResult()); + } finally { + cm.setTimeout(3000); + }*/ + + CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t", (String) null)); + Assert.assertEquals("2t", cm.evaluate(JsclOperation.simplify, "∂(t^2,t)").getResult()); + Assert.assertEquals("2t", cm.evaluate(JsclOperation.numeric, "∂(t^2,t)").getResult()); + CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t", "2")); + Assert.assertEquals("2t", cm.evaluate(JsclOperation.simplify, "∂(t^2,t)").getResult()); + Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "∂(t^2,t)").getResult()); + + Assert.assertEquals("-x+x*ln(x)", cm.getEngine().simplify("∫(ln(x), x)")); + Assert.assertEquals("-(x-x*ln(x))/(ln(2)+ln(5))", cm.getEngine().simplify("∫(log(10, x), x)")); + + Assert.assertEquals("∫((ln(2)+ln(5))/ln(x), x)", cm.getEngine().simplify("∫(ln(10)/ln(x), x)")); + Assert.assertEquals("∫(ln(10)/ln(x), x)", Expression.valueOf("∫(log(x, 10), x)").expand().toString()); + Assert.assertEquals("∫((ln(2)+ln(5))/ln(x), x)", cm.getEngine().simplify("∫(log(x, 10), x)")); + } + + @Test + public void testFormatting() throws Exception { + final CalculatorEngine ce = CalculatorEngine.instance; + + Assert.assertEquals("12 345", ce.evaluate(JsclOperation.simplify, "12345").getResult()); + + } + + @Test + public void testI() throws CalculatorParseException, CalculatorEvalException { + final CalculatorEngine cm = CalculatorEngine.instance; + + Assert.assertEquals("-i", cm.evaluate(JsclOperation.numeric, "i^3").getResult()); + for (int i = 0; i < 1000; i++) { + double real = (Math.random()-0.5) * 1000; + double imag = (Math.random()-0.5) * 1000; + int exp = (int)(Math.random() * 10); + + final StringBuilder sb = new StringBuilder(); + sb.append(real); + if ( imag > 0 ) { + sb.append("+"); + } + sb.append(imag); + sb.append("^").append(exp); + try { + cm.evaluate(JsclOperation.numeric, sb.toString()).getResult(); + } catch (Throwable e) { + fail(sb.toString()); + } + } + } + + @Test + public void testEmptyFunction() throws Exception { + final CalculatorEngine cm = CalculatorEngine.instance; + try { + cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(acos(acos(acos(acos(acos(acos(acos(acos(cos(cos(cos(cos(cosh(acos(cos(cos(cos(cos(cos(acos(acos(acos(acos(acos(acos(acos(acos(cos(cos(cos(cos(cosh(acos(cos())))))))))))))))))))))))))))))))))))))"); + Assert.fail(); + } catch (CalculatorParseException e) { + } + Assert.assertEquals("0.34+1.382i", cm.evaluate(JsclOperation.numeric, "ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(100)))))))))))))))").getResult()); + try { + cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos())))))))))))))))))))))))))))))))))))"); + Assert.fail(); + } catch (CalculatorParseException e) { + } + + final AngleUnit defaultAngleUnit = cm.getEngine().getAngleUnits(); + try { + cm.getEngine().setAngleUnits(AngleUnit.rad); + Assert.assertEquals("0.739", cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(1))))))))))))))))))))))))))))))))))))").getResult()); + } finally { + cm.getEngine().setAngleUnits(defaultAngleUnit); + } + + CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("si", 5d)); + Assert.assertEquals("5", cm.evaluate(JsclOperation.numeric, "si").getResult()); + + try { + cm.evaluate(JsclOperation.numeric, "sin"); + Assert.fail(); + } catch (CalculatorParseException e) { + } + } + + @Test + public void testRounding() throws Exception { + final CalculatorEngine cm = CalculatorEngine.instance; + + try { + DecimalFormatSymbols decimalGroupSymbols = new DecimalFormatSymbols(Locale.getDefault()); + decimalGroupSymbols.setDecimalSeparator('.'); + decimalGroupSymbols.setGroupingSeparator('\''); + cm.setDecimalGroupSymbols(decimalGroupSymbols); + cm.setPrecision(2); + Assert.assertEquals("12'345'678.9", cm.evaluate(JsclOperation.numeric, "1.23456789E7").getResult()); + cm.setPrecision(10); + Assert.assertEquals("12'345'678.9", cm.evaluate(JsclOperation.numeric, "1.23456789E7").getResult()); + Assert.assertEquals("123'456'789", cm.evaluate(JsclOperation.numeric, "1.234567890E8").getResult()); + Assert.assertEquals("1'234'567'890.1", cm.evaluate(JsclOperation.numeric, "1.2345678901E9").getResult()); + } finally { + cm.setPrecision(3); + DecimalFormatSymbols decimalGroupSymbols = new DecimalFormatSymbols(Locale.getDefault()); + decimalGroupSymbols.setDecimalSeparator('.'); + decimalGroupSymbols.setGroupingSeparator(JsclMathEngine.GROUPING_SEPARATOR_DEFAULT.charAt(0)); + cm.setDecimalGroupSymbols(decimalGroupSymbols); + } + } + + @Test + public void testComparisonFunction() throws Exception { + final CalculatorEngine cm = CalculatorEngine.instance; + + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(0, 1)").getResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq(1, 1)").getResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq(1, 1.0)").getResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(1, 1.000000000000001)").getResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(1, 0)").getResult()); + + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "lt(0, 1)").getResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "lt(1, 1)").getResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "lt(1, 0)").getResult()); + + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "gt(0, 1)").getResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "gt(1, 1)").getResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "gt(1, 0)").getResult()); + + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ne(0, 1)").getResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ne(1, 1)").getResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ne(1, 0)").getResult()); + + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "le(0, 1)").getResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "le(1, 1)").getResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "le(1, 0)").getResult()); + + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ge(0, 1)").getResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ge(1, 1)").getResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ge(1, 0)").getResult()); + + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ap(0, 1)").getResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ap(1, 1)").getResult()); + //Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ap(1, 1.000000000000001)").getResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ap(1, 0)").getResult()); + + } + + + @Test + public void testNumeralSystems() throws Exception { + final CalculatorEngine cm = CalculatorEngine.instance; + + Assert.assertEquals("11 259 375", cm.evaluate(JsclOperation.numeric, "0x:ABCDEF").getResult()); + Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "0x:ABCDEF*e").getResult()); + Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "e*0x:ABCDEF").getResult()); + Assert.assertEquals("e", cm.evaluate(JsclOperation.numeric, "e*0x:ABCDEF/0x:ABCDEF").getResult()); + Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "0x:ABCDEF*e*0x:ABCDEF/0x:ABCDEF").getResult()); + Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "c+0x:ABCDEF*e*0x:ABCDEF/0x:ABCDEF-c+0x:C-0x:C").getResult()); + Assert.assertEquals("1 446 257 064 651.832", cm.evaluate(JsclOperation.numeric, "28*28 * sin(28) - 0b:1101 + √(28) + exp ( 28) ").getResult()); + Assert.assertEquals("13", cm.evaluate(JsclOperation.numeric, "0b:1101").getResult()); + + try { + cm.evaluate(JsclOperation.numeric, "0b:π").getResult(); + Assert.fail(); + } catch (CalculatorParseException e) { + // ok + } + + final NumeralBase defaultNumeralBase = cm.getEngine().getNumeralBase(); + try{ + cm.getEngine().setNumeralBase(NumeralBase.bin); + Assert.assertEquals("101", cm.evaluate(JsclOperation.numeric, "10+11").getResult()); + Assert.assertEquals("10/11", cm.evaluate(JsclOperation.numeric, "10/11").getResult()); + + cm.getEngine().setNumeralBase(NumeralBase.hex); + Assert.assertEquals("63 7B", cm.evaluate(JsclOperation.numeric, "56CE+CAD").getResult()); + Assert.assertEquals("E", cm.evaluate(JsclOperation.numeric, "E").getResult()); + } finally { + cm.setNumeralBase(defaultNumeralBase); + } + } + + @Test + public void testLog() throws Exception { + final CalculatorEngine cm = CalculatorEngine.instance; + + Assert.assertEquals("∞", Expression.valueOf("1/0").numeric().toString()); + Assert.assertEquals("∞", Expression.valueOf("ln(10)/ln(1)").numeric().toString()); + + // logarithm + Assert.assertEquals("ln(x)/ln(base)", ((CustomFunction) cm.getFunctionsRegistry().get("log")).getContent()); + Assert.assertEquals("∞", cm.evaluate(JsclOperation.numeric, "log(1, 10)").getResult()); + Assert.assertEquals("3.322", cm.evaluate(JsclOperation.numeric, "log(2, 10)").getResult()); + Assert.assertEquals("1.431", cm.evaluate(JsclOperation.numeric, "log(5, 10)").getResult()); + Assert.assertEquals("0.96", cm.evaluate(JsclOperation.numeric, "log(11, 10)").getResult()); + Assert.assertEquals("1/(bln(a))", cm.evaluate(JsclOperation.simplify, "∂(log(a, b), b)").getResult()); + Assert.assertEquals("-ln(b)/(aln(a)^2)", cm.evaluate(JsclOperation.simplify, "∂(log(a, b), a)").getResult()); + + } +} 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..b7046569 100644 --- a/calculatorpp/src/test/java/org/solovyev/android/calculator/model/NumeralBaseTest.java +++ b/calculatorpp/src/test/java/org/solovyev/android/calculator/model/NumeralBaseTest.java @@ -1,146 +1,147 @@ -package org.solovyev.android.calculator.model; - -import au.com.bytecode.opencsv.CSVReader; -import jscl.JsclMathEngine; -import jscl.MathEngine; -import jscl.math.Expression; -import jscl.text.ParseException; -import jscl.util.ExpressionGeneratorWithInput; -import org.jetbrains.annotations.NotNull; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; -import org.solovyev.android.calculator.jscl.JsclOperation; -import org.solovyev.common.Converter; - -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.List; - -/** - * User: serso - * Date: 12/14/11 - * Time: 4:16 PM - */ -public class NumeralBaseTest { - - @BeforeClass - public static void setUp() throws Exception { - CalculatorEngine.instance.init(null, null); - CalculatorEngine.instance.setPrecision(3); - CalculatorEngine.instance.setThreadKiller(new CalculatorEngine.ThreadKillerImpl()); - } - - @Test - public void testConversion() throws Exception { - CSVReader reader = null; - try { - final MathEngine me = JsclMathEngine.instance; - - reader = new CSVReader(new InputStreamReader(NumeralBaseTest.class.getResourceAsStream("/org/solovyev/android/calculator/model/nb_table.csv")), '\t'); - - // skip first line - reader.readNext(); - - String[] line = reader.readNext(); - for (; line != null; line = reader.readNext()) { - testExpression(line, new DummyExpression()); - testExpression(line, new Expression1()); - testExpression(line, new Expression2()); - testExpression(line, new Expression3()); - - final String dec = line[0].toUpperCase(); - final String hex = "0x:" + line[1].toUpperCase(); - final String bin = "0b:" + line[2].toUpperCase(); - - final List input = new ArrayList(); - input.add(dec); - input.add(hex); - input.add(bin); - - //System.out.println("Dec: " + dec); - //System.out.println("Hex: " + hex); - //System.out.println("Bin: " + bin); - - final ExpressionGeneratorWithInput eg = new ExpressionGeneratorWithInput(input, 20); - final List expressions = eg.generate(); - - final String decExpression = expressions.get(0); - final String hexExpression = expressions.get(1); - final String binExpression = expressions.get(2); - - //System.out.println("Dec expression: " + decExpression); - //System.out.println("Hex expression: " + hexExpression); - //System.out.println("Bin expression: " + binExpression); - - final String decResult = Expression.valueOf(decExpression).numeric().toString(); - //System.out.println("Dec result: " + decResult); - - final String hexResult = Expression.valueOf(hexExpression).numeric().toString(); - //System.out.println("Hex result: " + hexResult); - - final String binResult = Expression.valueOf(binExpression).numeric().toString(); - //System.out.println("Bin result: " + binResult); - - Assert.assertEquals("dec-hex: " + decExpression + " : " + hexExpression, decResult, hexResult); - Assert.assertEquals("dec-bin: " + decExpression + " : " + binExpression, decResult, binResult); - } - } finally { - if (reader != null) { - reader.close(); - } - } - } - - public static void testExpression(@NotNull String[] line, @NotNull Converter converter) throws ParseException, CalculatorEvalException, CalculatorParseException { - final String dec = line[0].toUpperCase(); - final String hex = "0x:" + line[1].toUpperCase(); - final String bin = "0b:" + line[2].toUpperCase(); - - final String decExpression = converter.convert(dec); - final String decResult = CalculatorEngine.instance.evaluate(JsclOperation.numeric, decExpression).getResult(); - final String hexExpression = converter.convert(hex); - final String hexResult = CalculatorEngine.instance.evaluate(JsclOperation.numeric, hexExpression).getResult(); - final String binExpression = converter.convert(bin); - final String binResult = CalculatorEngine.instance.evaluate(JsclOperation.numeric, binExpression).getResult(); - - Assert.assertEquals("dec-hex: " + decExpression + " : " + hexExpression, decResult, hexResult); - Assert.assertEquals("dec-bin: " + decExpression + " : " + binExpression, decResult, binResult); - } - - private static class DummyExpression implements Converter { - - @NotNull - @Override - public String convert(@NotNull String s) { - return s; - } - } - - private static class Expression1 implements Converter { - - @NotNull - @Override - public String convert(@NotNull String s) { - return s + "*" + s; - } - } - - private static class Expression2 implements Converter { - - @NotNull - @Override - public String convert(@NotNull String s) { - return s + "*" + s + " * sin(" + s + ") - 0b:1101"; - } - } - - private static class Expression3 implements Converter { - - @NotNull - @Override - public String convert(@NotNull String s) { - return s + "*" + s + " * sin(" + s + ") - 0b:1101 + √(" + s + ") + exp ( " + s + ")"; - } - } -} +package org.solovyev.android.calculator.model; + +import au.com.bytecode.opencsv.CSVReader; +import jscl.JsclMathEngine; +import jscl.MathEngine; +import jscl.math.Expression; +import jscl.text.ParseException; +import jscl.util.ExpressionGeneratorWithInput; +import org.jetbrains.annotations.NotNull; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.solovyev.android.calculator.CalculatorParseException; +import org.solovyev.android.calculator.jscl.JsclOperation; +import org.solovyev.common.Converter; + +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + +/** + * User: serso + * Date: 12/14/11 + * Time: 4:16 PM + */ +public class NumeralBaseTest { + + @BeforeClass + public static void setUp() throws Exception { + CalculatorEngine.instance.init(null, null); + CalculatorEngine.instance.setPrecision(3); + CalculatorEngine.instance.setThreadKiller(new CalculatorEngine.ThreadKillerImpl()); + } + + @Test + public void testConversion() throws Exception { + CSVReader reader = null; + try { + final MathEngine me = JsclMathEngine.instance; + + reader = new CSVReader(new InputStreamReader(NumeralBaseTest.class.getResourceAsStream("/org/solovyev/android/calculator/model/nb_table.csv")), '\t'); + + // skip first line + reader.readNext(); + + String[] line = reader.readNext(); + for (; line != null; line = reader.readNext()) { + testExpression(line, new DummyExpression()); + testExpression(line, new Expression1()); + testExpression(line, new Expression2()); + testExpression(line, new Expression3()); + + final String dec = line[0].toUpperCase(); + final String hex = "0x:" + line[1].toUpperCase(); + final String bin = "0b:" + line[2].toUpperCase(); + + final List input = new ArrayList(); + input.add(dec); + input.add(hex); + input.add(bin); + + //System.out.println("Dec: " + dec); + //System.out.println("Hex: " + hex); + //System.out.println("Bin: " + bin); + + final ExpressionGeneratorWithInput eg = new ExpressionGeneratorWithInput(input, 20); + final List expressions = eg.generate(); + + final String decExpression = expressions.get(0); + final String hexExpression = expressions.get(1); + final String binExpression = expressions.get(2); + + //System.out.println("Dec expression: " + decExpression); + //System.out.println("Hex expression: " + hexExpression); + //System.out.println("Bin expression: " + binExpression); + + final String decResult = Expression.valueOf(decExpression).numeric().toString(); + //System.out.println("Dec result: " + decResult); + + final String hexResult = Expression.valueOf(hexExpression).numeric().toString(); + //System.out.println("Hex result: " + hexResult); + + final String binResult = Expression.valueOf(binExpression).numeric().toString(); + //System.out.println("Bin result: " + binResult); + + Assert.assertEquals("dec-hex: " + decExpression + " : " + hexExpression, decResult, hexResult); + Assert.assertEquals("dec-bin: " + decExpression + " : " + binExpression, decResult, binResult); + } + } finally { + if (reader != null) { + reader.close(); + } + } + } + + public static void testExpression(@NotNull String[] line, @NotNull Converter converter) throws ParseException, CalculatorEvalException, CalculatorParseException { + final String dec = line[0].toUpperCase(); + final String hex = "0x:" + line[1].toUpperCase(); + final String bin = "0b:" + line[2].toUpperCase(); + + final String decExpression = converter.convert(dec); + final String decResult = CalculatorEngine.instance.evaluate(JsclOperation.numeric, decExpression).getResult(); + final String hexExpression = converter.convert(hex); + final String hexResult = CalculatorEngine.instance.evaluate(JsclOperation.numeric, hexExpression).getResult(); + final String binExpression = converter.convert(bin); + final String binResult = CalculatorEngine.instance.evaluate(JsclOperation.numeric, binExpression).getResult(); + + Assert.assertEquals("dec-hex: " + decExpression + " : " + hexExpression, decResult, hexResult); + Assert.assertEquals("dec-bin: " + decExpression + " : " + binExpression, decResult, binResult); + } + + private static class DummyExpression implements Converter { + + @NotNull + @Override + public String convert(@NotNull String s) { + return s; + } + } + + private static class Expression1 implements Converter { + + @NotNull + @Override + public String convert(@NotNull String s) { + return s + "*" + s; + } + } + + private static class Expression2 implements Converter { + + @NotNull + @Override + public String convert(@NotNull String s) { + return s + "*" + s + " * sin(" + s + ") - 0b:1101"; + } + } + + private static class Expression3 implements Converter { + + @NotNull + @Override + public String convert(@NotNull String s) { + return s + "*" + s + " * sin(" + s + ") - 0b:1101 + √(" + s + ") + exp ( " + s + ")"; + } + } +} 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..57f2bb1d 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,163 @@ -/* - * 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.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 041abf06..61bbd3f4 100644 --- a/pom.xml +++ b/pom.xml @@ -122,6 +122,22 @@ 11.0.2 + + org.simpleframework + simple-xml + 2.6.1 + + + stax-api + stax + + + xpp3 + xpp3 + + + + From 1d2aaa9d4713474d1cd6da6a298987ffa76a1c4c Mon Sep 17 00:00:00 2001 From: serso Date: Thu, 20 Sep 2012 18:35:11 +0400 Subject: [PATCH 4/6] New core implementation --- .../calculator}/AbstractNumberBuilder.java | 170 +++--- .../android/calculator/Calculator.java | 9 + .../calculator}/CalculatorEvalException.java | 145 +++-- .../calculator/CalculatorEventContainer.java | 57 ++ .../calculator/CalculatorEventData.java | 11 + .../calculator/CalculatorEventDataId.java | 17 + .../calculator/CalculatorEventDataIdImpl.java | 61 ++ .../calculator/CalculatorEventDataImpl.java | 47 ++ .../calculator/CalculatorEventListener.java | 17 + .../calculator/CalculatorEventType.java | 9 + .../android/calculator/CalculatorImpl.java | 168 ++++++ ...orDisplay.java => JCalculatorDisplay.java} | 2 +- .../ListCalculatorEventContainer.java | 50 ++ .../calculator}/LiteNumberBuilder.java | 110 ++-- .../android/calculator}/NumberBuilder.java | 405 +++++++------- .../calculator}/PreparedExpression.java | 130 ++--- .../calculator}/ToJsclTextProcessor.java | 3 +- .../CalculatorDisplayHistoryState.java | 6 +- .../calculator/history/CalculatorHistory.java | 20 + .../history/CalculatorHistoryImpl.java | 131 +++++ .../history/CalculatorHistoryState.java | 6 +- .../common/utils/ListListenersContainer.java | 77 +++ .../calculator/CalculatorActivity.java | 4 +- .../android/calculator/CalculatorDisplay.java | 4 +- .../android/calculator/CalculatorModel.java | 21 +- .../history/AbstractHistoryActivity.java | 526 +++++++++--------- .../history/AndroidCalculatorHistory.java | 18 + .../history/AndroidCalculatorHistoryImpl.java | 145 +++++ .../calculator/history/CalculatorHistory.java | 149 ----- .../history/HistoryActivityTab.java | 74 +-- .../history/HistoryItemMenuItem.java | 308 +++++----- .../history/SavedHistoryActivityTab.java | 74 +-- .../calculator/model/CalculatorEngine.java | 5 +- .../plot/CalculatorPlotActivity.java | 4 +- .../view/NumeralBaseConverterDialog.java | 2 +- .../calculator/view/TextHighlighter.java | 3 + .../calculator/history/HistoryUtilsTest.java | 6 +- .../model/CalculatorEngineTest.java | 1 + .../calculator/model/NumeralBaseTest.java | 1 + .../model/ToJsclTextProcessorTest.java | 2 + 40 files changed, 1845 insertions(+), 1153 deletions(-) rename {calculatorpp/src/main/java/org/solovyev/android/calculator/model => calculatorpp-core/src/main/java/org/solovyev/android/calculator}/AbstractNumberBuilder.java (95%) create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/Calculator.java rename {calculatorpp/src/main/java/org/solovyev/android/calculator/model => calculatorpp-core/src/main/java/org/solovyev/android/calculator}/CalculatorEvalException.java (92%) create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventContainer.java create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventData.java create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataId.java create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataIdImpl.java create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataImpl.java create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventListener.java create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventType.java create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorImpl.java rename calculatorpp-core/src/main/java/org/solovyev/android/calculator/{ICalculatorDisplay.java => JCalculatorDisplay.java} (90%) create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/ListCalculatorEventContainer.java rename {calculatorpp/src/main/java/org/solovyev/android/calculator/model => calculatorpp-core/src/main/java/org/solovyev/android/calculator}/LiteNumberBuilder.java (93%) rename {calculatorpp/src/main/java/org/solovyev/android/calculator/model => calculatorpp-core/src/main/java/org/solovyev/android/calculator}/NumberBuilder.java (94%) rename {calculatorpp/src/main/java/org/solovyev/android/calculator/model => calculatorpp-core/src/main/java/org/solovyev/android/calculator}/PreparedExpression.java (91%) rename {calculatorpp/src/main/java/org/solovyev/android/calculator/model => calculatorpp-core/src/main/java/org/solovyev/android/calculator}/ToJsclTextProcessor.java (95%) create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorHistory.java create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorHistoryImpl.java create mode 100644 calculatorpp-core/src/main/java/org/solovyev/common/utils/ListListenersContainer.java create mode 100644 calculatorpp/src/main/java/org/solovyev/android/calculator/history/AndroidCalculatorHistory.java create mode 100644 calculatorpp/src/main/java/org/solovyev/android/calculator/history/AndroidCalculatorHistoryImpl.java delete mode 100644 calculatorpp/src/main/java/org/solovyev/android/calculator/history/CalculatorHistory.java 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..b310b90c --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/Calculator.java @@ -0,0 +1,9 @@ +package org.solovyev.android.calculator; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 16:38 + */ +public interface Calculator extends CalculatorEventContainer { +} 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/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..5d3b348b --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventData.java @@ -0,0 +1,11 @@ +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.Nullable; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 16:51 + */ +public interface CalculatorEventData extends CalculatorEventDataId { +} 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..58d8dc11 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataId.java @@ -0,0 +1,17 @@ +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.Nullable; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 18:18 + */ +public interface CalculatorEventDataId { + + // the higher id => the later event + long getEventId(); + + @Nullable + Long getCalculationId(); +} 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..1767719f --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataIdImpl.java @@ -0,0 +1,61 @@ +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 18:18 + */ +public class CalculatorEventDataIdImpl implements CalculatorEventDataId { + + private final long eventId; + + @Nullable + private final Long calculationId; + + private CalculatorEventDataIdImpl(long id, + @Nullable Long calculationId) { + this.eventId = id; + this.calculationId = calculationId; + } + + @NotNull + public static CalculatorEventDataId newInstance(long id, + @Nullable Long calculationId) { + return new CalculatorEventDataIdImpl(id, calculationId); + } + + @Override + public long getEventId() { + return this.eventId; + } + + @Nullable + @Override + public Long getCalculationId() { + return this.calculationId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof CalculatorEventDataIdImpl)) return false; + + CalculatorEventDataIdImpl that = (CalculatorEventDataIdImpl) o; + + if (eventId != that.eventId) return false; + if (calculationId != null ? !calculationId.equals(that.calculationId) : that.calculationId != null) + return false; + + return true; + } + + @Override + public int hashCode() { + int result = (int) (eventId ^ (eventId >>> 32)); + result = 31 * result + (calculationId != null ? calculationId.hashCode() : 0); + return result; + } +} 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..18c9a317 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataImpl.java @@ -0,0 +1,47 @@ +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 16:54 + */ +class CalculatorEventDataImpl implements CalculatorEventData { + + @NotNull + private CalculatorEventDataId calculatorEventDataId; + + CalculatorEventDataImpl(@NotNull CalculatorEventDataId calculatorEventDataId) { + this.calculatorEventDataId = calculatorEventDataId; + } + + @Override + public long getEventId() { + return calculatorEventDataId.getEventId(); + } + + @Override + @Nullable + public Long getCalculationId() { + return calculatorEventDataId.getCalculationId(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof CalculatorEventDataImpl)) return false; + + CalculatorEventDataImpl that = (CalculatorEventDataImpl) o; + + if (!calculatorEventDataId.equals(that.calculatorEventDataId)) return false; + + return true; + } + + @Override + public int hashCode() { + return calculatorEventDataId.hashCode(); + } +} 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..7df859e9 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventType.java @@ -0,0 +1,9 @@ +package org.solovyev.android.calculator; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 16:40 + */ +public enum CalculatorEventType { +} 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..c98d1712 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorImpl.java @@ -0,0 +1,168 @@ +package org.solovyev.android.calculator; + +import jscl.AbstractJsclArithmeticException; +import jscl.NumeralBaseException; +import jscl.math.Generic; +import jscl.text.ParseInterruptedException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.calculator.jscl.JsclOperation; +import org.solovyev.android.calculator.text.TextProcessor; +import org.solovyev.common.msg.MessageRegistry; + +import java.util.List; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicLong; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 16:42 + */ +public class CalculatorImpl implements Calculator { + + @NotNull + private final CalculatorEventContainer calculatorEventContainer = new ListCalculatorEventContainer(); + + @NotNull + private static final Calculator instance = new CalculatorImpl(); + + @NotNull + private final AtomicLong counter = new AtomicLong(0); + + @NotNull + private final Object lock = new Object(); + + @NotNull + private final TextProcessor preprocessor = ToJsclTextProcessor.getInstance(); + + @NotNull + private final Executor threadPoolExecutor = Executors.newFixedThreadPool(10); + + private CalculatorImpl() { + } + + @NotNull + public static Calculator getInstance() { + return instance; + } + + @NotNull + private CalculatorEventDataId nextCalculatorEventDataId() { + long eventId = counter.incrementAndGet(); + return CalculatorEventDataIdImpl.newInstance(eventId, eventId); + } + + /* + ********************************************************************** + * + * CALCULATION + * + ********************************************************************** + */ + + public void evaluate(@NotNull JsclOperation operation, + @NotNull String expression) { + evaluate(operation, expression, null); + } + + public void evaluate(@NotNull final JsclOperation operation, + @NotNull final String expression, + @Nullable final MessageRegistry mr) { + + final CalculatorEventDataId eventDataId = nextCalculatorEventDataId(); + + threadPoolExecutor.execute(new Runnable() { + @Override + public void run() { + CalculatorImpl.this.evaluate(eventDataId, operation, expression, mr); + } + }); + } + + private void evaluate(@NotNull CalculatorEventDataId eventDataId, + @NotNull JsclOperation operation, + @NotNull String expression, + @Nullable MessageRegistry mr) { + synchronized (lock) { + PreparedExpression preparedExpression = null; + + try { + preparedExpression = preprocessor.process(expression); + + final String jsclExpression = preparedExpression.toString(); + try { + + final Generic genericResult = operation.evaluateGeneric(jsclExpression); + + // NOTE: toString() method must be called here as ArithmeticOperationException may occur in it (just to avoid later check!) + genericResult.toString(); + + //return new Result(operation.getFromProcessor().process(genericResult), operation, genericResult); + } catch (AbstractJsclArithmeticException e) { + handleException(eventDataId, operation, expression, mr, preparedExpression, null, new CalculatorEvalException(e, e, jsclExpression)); + } + + } catch (ArithmeticException e) { + //final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_1, MessageType.error, CalculatorApplication.getInstance(), e.getMessage()); + handleException(operation, expression, mr, preparedExpression, new CalculatorParseException(jsclExpression, androidMessage)); + } catch (StackOverflowError e) { + //final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_2, MessageType.error, CalculatorApplication.getInstance()); + handleException(eventDataId, operation, expression, mr, preparedExpression, new CalculatorParseException(e), null); + } catch (jscl.text.ParseException e) { + //System.out.println(e.getMessage()); + handleException(eventDataId, operation, expression, mr, preparedExpression, new CalculatorParseException(e), null); + } catch (ParseInterruptedException e) { + // do nothing - we ourselves interrupt the calculations + } catch (CalculatorParseException e) { + handleException(eventDataId, operation, expression, mr, preparedExpression, e, null); + } + } + } + + private void handleException(@NotNull CalculatorEventDataId eventDataId, + @NotNull JsclOperation operation, + @NotNull String expression, + @Nullable MessageRegistry mr, + @Nullable PreparedExpression preparedExpression, + @Nullable CalculatorParseException parseException, + @Nullable CalculatorEvalException evalException) { + if (operation == JsclOperation.numeric && (preparedExpression != null && preparedExpression.isExistsUndefinedVar() || (evalException != null && evalException.getCause() instanceof NumeralBaseException))) { + evaluate(eventDataId, JsclOperation.simplify, expression, mr); + } + + if (parseException != null) { + throw parseException; + } else { + throw evalException; + } + } + + /* + ********************************************************************** + * + * EVENTS + * + ********************************************************************** + */ + + @Override + public void addCalculatorEventListener(@NotNull CalculatorEventListener calculatorEventListener) { + calculatorEventContainer.addCalculatorEventListener(calculatorEventListener); + } + + @Override + public void removeCalculatorEventListener(@NotNull CalculatorEventListener calculatorEventListener) { + calculatorEventContainer.removeCalculatorEventListener(calculatorEventListener); + } + + @Override + public void fireCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) { + calculatorEventContainer.fireCalculatorEvent(calculatorEventData, calculatorEventType, data); + } + + @Override + public void fireCalculatorEvents(@NotNull List calculatorEvents) { + calculatorEventContainer.fireCalculatorEvents(calculatorEvents); + } +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/ICalculatorDisplay.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/JCalculatorDisplay.java similarity index 90% rename from calculatorpp-core/src/main/java/org/solovyev/android/calculator/ICalculatorDisplay.java rename to calculatorpp-core/src/main/java/org/solovyev/android/calculator/JCalculatorDisplay.java index 96a61018..1594893a 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/ICalculatorDisplay.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/JCalculatorDisplay.java @@ -17,7 +17,7 @@ import org.solovyev.android.calculator.jscl.JsclOperation; * Date: 12/17/11 * Time: 9:45 PM */ -public interface ICalculatorDisplay extends Editor{ +public interface JCalculatorDisplay extends Editor{ boolean isValid(); 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 95% 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 630ad312..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,12 +4,13 @@ * 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; diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorDisplayHistoryState.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorDisplayHistoryState.java index b637e901..356f953a 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorDisplayHistoryState.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorDisplayHistoryState.java @@ -11,7 +11,7 @@ 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.JCalculatorDisplay; import org.solovyev.android.calculator.jscl.JsclOperation; /** @@ -47,7 +47,7 @@ public class CalculatorDisplayHistoryState implements Cloneable { } @NotNull - public static CalculatorDisplayHistoryState newInstance(@NotNull ICalculatorDisplay display) { + public static CalculatorDisplayHistoryState newInstance(@NotNull JCalculatorDisplay display) { final CalculatorDisplayHistoryState result = new CalculatorDisplayHistoryState(); result.editorState = EditorHistoryState.newInstance(display); @@ -59,7 +59,7 @@ public class CalculatorDisplayHistoryState implements Cloneable { return result; } - public void setValuesFromHistory(@NotNull ICalculatorDisplay display) { + public void setValuesFromHistory(@NotNull JCalculatorDisplay display) { this.getEditorState().setValuesFromHistory(display); display.setValid(this.isValid()); display.setErrorMessage(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-core/src/main/java/org/solovyev/android/calculator/history/CalculatorHistoryState.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorHistoryState.java index a90b457a..a7d37aaa 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorHistoryState.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorHistoryState.java @@ -9,7 +9,7 @@ import org.jetbrains.annotations.NotNull; import org.simpleframework.xml.Element; import org.simpleframework.xml.Root; import org.solovyev.android.calculator.Editor; -import org.solovyev.android.calculator.ICalculatorDisplay; +import org.solovyev.android.calculator.JCalculatorDisplay; /** * User: serso @@ -38,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 JCalculatorDisplay display) { final EditorHistoryState editorHistoryState = EditorHistoryState.newInstance(editor); final CalculatorDisplayHistoryState displayHistoryState = CalculatorDisplayHistoryState.newInstance(display); return new CalculatorHistoryState(editorHistoryState, displayHistoryState); @@ -94,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 JCalculatorDisplay display) { this.getEditorState().setValuesFromHistory(editor); this.getDisplayState().setValuesFromHistory(display); } 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/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/CalculatorDisplay.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorDisplay.java index 54fc4e6f..c0e4e4ce 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorDisplay.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorDisplay.java @@ -19,7 +19,7 @@ import org.jetbrains.annotations.Nullable; import org.solovyev.android.calculator.jscl.JsclOperation; import org.solovyev.android.calculator.model.CalculatorEngine; import org.solovyev.android.calculator.text.TextProcessor; -import org.solovyev.android.calculator.model.ToJsclTextProcessor; +import org.solovyev.android.calculator.ToJsclTextProcessor; import org.solovyev.android.calculator.view.NumeralBaseConverterDialog; import org.solovyev.android.calculator.view.TextHighlighter; import org.solovyev.android.calculator.view.UnitConverterViewBuilder; @@ -37,7 +37,7 @@ import java.util.Set; * Date: 9/17/11 * Time: 10:58 PM */ -public class CalculatorDisplay extends AutoResizeTextView implements ICalculatorDisplay{ +public class CalculatorDisplay extends AutoResizeTextView implements JCalculatorDisplay { private static enum ConversionMenuItem implements AMenuItem { convert_to_bin(NumeralBase.bin), 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 6d544a8f..2329cc0a 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorModel.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorModel.java @@ -20,13 +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.history.HistoryControl; import org.solovyev.android.menu.AMenuBuilder; import org.solovyev.android.menu.MenuImpl; @@ -70,7 +69,7 @@ public enum CalculatorModel implements CursorControl, HistoryControl 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/model/CalculatorEngine.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/model/CalculatorEngine.java index fb385c9a..00d4da93 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 @@ -15,10 +15,7 @@ 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.CalculatorParseException; -import org.solovyev.android.calculator.JCalculatorEngine; -import org.solovyev.android.calculator.R; +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; 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 0e2040eb..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 @@ -36,8 +36,8 @@ 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.model.PreparedExpression; -import org.solovyev.android.calculator.model.ToJsclTextProcessor; +import org.solovyev.android.calculator.PreparedExpression; +import org.solovyev.android.calculator.ToJsclTextProcessor; import org.solovyev.common.MutableObject; import java.io.Serializable; 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 4c94fe06..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 @@ -13,7 +13,7 @@ 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.model.ToJsclTextProcessor; +import org.solovyev.android.calculator.ToJsclTextProcessor; import org.solovyev.common.MutableObject; import org.solovyev.common.text.StringUtils; 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 2ebf22da..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 @@ -9,7 +9,10 @@ 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; 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 c05f2a32..7d427a1d 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 @@ -12,7 +12,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.junit.Test; import org.solovyev.android.calculator.Editor; -import org.solovyev.android.calculator.ICalculatorDisplay; +import org.solovyev.android.calculator.JCalculatorDisplay; import org.solovyev.android.calculator.jscl.JsclOperation; import org.solovyev.common.equals.CollectionEqualizer; import org.solovyev.common.equals.EqualsTool; @@ -125,7 +125,7 @@ public class HistoryUtilsTest { HistoryHelper history = new SimpleHistoryHelper(); - ICalculatorDisplay calculatorDisplay = new TestCalculatorDisplay(); + JCalculatorDisplay calculatorDisplay = new TestCalculatorDisplay(); calculatorDisplay.setErrorMessage("error_msg1"); calculatorDisplay.setText("Error"); calculatorDisplay.setSelection(1); @@ -215,7 +215,7 @@ public class HistoryUtilsTest { } - private static class TestCalculatorDisplay implements ICalculatorDisplay { + private static class TestCalculatorDisplay implements JCalculatorDisplay { @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 187c504d..8bd96dcd 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,7 @@ 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; 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 b7046569..bd658b31 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,7 @@ 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; 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 57f2bb1d..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 @@ -12,6 +12,8 @@ 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; /** From 79e85ea255da8d8fc9ea294e7608ef7f0499314f Mon Sep 17 00:00:00 2001 From: Sergey Solovyev Date: Thu, 20 Sep 2012 22:49:39 +0400 Subject: [PATCH 5/6] Calculator display changes --- .../android/calculator/Calculator.java | 34 +- .../android/calculator/CalculatorDisplay.java | 28 + .../calculator/CalculatorDisplayImpl.java | 147 +++ .../calculator/CalculatorDisplayView.java | 16 + .../CalculatorDisplayViewState.java | 33 + .../CalculatorDisplayViewStateImpl.java | 104 ++ .../CalculatorEvaluationEventData.java | 18 + .../CalculatorEvaluationEventDataImpl.java | 58 ++ .../calculator/CalculatorEventData.java | 21 +- .../calculator/CalculatorEventDataId.java | 37 +- .../calculator/CalculatorEventDataIdImpl.java | 127 +-- .../calculator/CalculatorEventDataImpl.java | 104 +- .../calculator/CalculatorEventType.java | 52 +- .../android/calculator/CalculatorFailure.java | 21 + .../calculator/CalculatorFailureImpl.java | 34 + .../android/calculator/CalculatorImpl.java | 377 ++++---- .../android/calculator/CalculatorInput.java | 18 + .../calculator/CalculatorInputImpl.java | 35 + .../android/calculator/CalculatorLocator.java | 38 +- .../calculator/CalculatorLocatorImpl.java | 90 +- .../android/calculator/CalculatorMessage.java | 31 + .../calculator/CalculatorMessages.java | 69 ++ .../android/calculator/CalculatorOutput.java | 22 + .../calculator/CalculatorOutputImpl.java | 46 + .../calculator/JCalculatorDisplay.java | 40 - .../CalculatorDisplayHistoryState.java | 282 +++--- .../history/CalculatorHistoryState.java | 222 ++--- .../history/EditorHistoryState.java | 187 ++-- .../android/calculator/messages.properties | 8 + calculatorpp/res/layout/calc_display.xml | 2 +- ...java => AndroidCalculatorDisplayView.java} | 663 +++++++------ .../android/calculator/CalculatorModel.java | 821 ++++++++-------- .../calculator/model/CalculatorEngine.java | 841 ++++++++--------- .../calculator/history/HistoryUtilsTest.java | 642 ++++++------- .../model/CalculatorEngineTest.java | 890 +++++++++--------- .../calculator/model/NumeralBaseTest.java | 296 +++--- 36 files changed, 3595 insertions(+), 2859 deletions(-) create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplay.java create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplayImpl.java create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplayView.java create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplayViewState.java create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplayViewStateImpl.java create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEvaluationEventData.java create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEvaluationEventDataImpl.java create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorFailure.java create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorFailureImpl.java create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorInput.java create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorInputImpl.java create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorMessage.java create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorMessages.java create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorOutput.java create mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorOutputImpl.java delete mode 100644 calculatorpp-core/src/main/java/org/solovyev/android/calculator/JCalculatorDisplay.java create mode 100644 calculatorpp-core/src/main/resources/org/solovyev/android/calculator/messages.properties rename calculatorpp/src/main/java/org/solovyev/android/calculator/{CalculatorDisplay.java => AndroidCalculatorDisplayView.java} (54%) diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/Calculator.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/Calculator.java index b310b90c..ee0e756b 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/Calculator.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/Calculator.java @@ -1,9 +1,25 @@ -package org.solovyev.android.calculator; - -/** - * User: Solovyev_S - * Date: 20.09.12 - * Time: 16:38 - */ -public interface Calculator extends CalculatorEventContainer { -} +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.calculator.jscl.JsclOperation; +import org.solovyev.common.msg.MessageRegistry; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 16:38 + */ +public interface Calculator extends CalculatorEventContainer { + + @NotNull + CalculatorEventDataId createFirstEventDataId(); + + void evaluate(@NotNull JsclOperation operation, + @NotNull String expression); + + @NotNull + CalculatorEventDataId evaluate(@NotNull JsclOperation operation, + @NotNull String expression, + @Nullable MessageRegistry mr); +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplay.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplay.java new file mode 100644 index 00000000..a68d9747 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplay.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + * or visit http://se.solovyev.org + */ + +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * User: serso + * Date: 12/17/11 + * Time: 9:45 PM + */ +public interface CalculatorDisplay extends CalculatorEventListener { + + void setView(@Nullable CalculatorDisplayView view); + + @NotNull + CalculatorDisplayViewState getViewState(); + + void setViewState(@NotNull CalculatorDisplayViewState viewState); + + @NotNull + CalculatorEventData getLastEventData(); +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplayImpl.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplayImpl.java new file mode 100644 index 00000000..5e794275 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplayImpl.java @@ -0,0 +1,147 @@ +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import static org.solovyev.android.calculator.CalculatorEventType.*; + +/** + * User: serso + * Date: 9/20/12 + * Time: 8:24 PM + */ +public class CalculatorDisplayImpl implements CalculatorDisplay { + + @NotNull + private CalculatorEventData lastCalculatorEventData = CalculatorEventDataImpl.newInstance(CalculatorLocatorImpl.getInstance().getCalculator().createFirstEventDataId()); + + @Nullable + private CalculatorDisplayView view; + + @NotNull + private final Object viewLock = new Object(); + + @NotNull + private CalculatorDisplayViewState lastViewState = CalculatorDisplayViewStateImpl.newDefaultInstance(); + + @Override + public void setView(@Nullable CalculatorDisplayView view) { + synchronized (viewLock) { + this.view = view; + + if (view != null) { + this.view.setState(lastViewState); + } + } + } + + @NotNull + @Override + public CalculatorDisplayViewState getViewState() { + return this.lastViewState; + } + + @Override + public void setViewState(@NotNull CalculatorDisplayViewState viewState) { + synchronized (viewLock) { + this.lastViewState = viewState; + if (this.view != null) { + this.view.setState(viewState); + } + } + } + +/* @Override + @Nullable + public CharSequence getText() { + synchronized (viewLock) { + return view != null ? view.getText() : null; + } + } + + @Override + public void setText(@Nullable CharSequence text) { + synchronized (viewLock) { + if (view != null) { + view.setText(text); + } + } + } + + @Override + public int getSelection() { + synchronized (viewLock) { + return view != null ? view.getSelection() : 0; + } + } + + @Override + public void setSelection(int selection) { + synchronized (viewLock) { + if (view != null) { + view.setSelection(selection); + } + } + }*/ + + @Override + @NotNull + public CalculatorEventData getLastEventData() { + return lastCalculatorEventData; + } + + @Override + public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, + @NotNull CalculatorEventType calculatorEventType, + @Nullable Object data) { + if (calculatorEventType.isOfType(calculation_result, calculation_failed, calculation_cancelled)) { + + if (calculatorEventData.isAfter(lastCalculatorEventData)) { + lastCalculatorEventData = calculatorEventData; + } + + switch (calculatorEventType) { + case calculation_result: + processCalculationResult((CalculatorEvaluationEventData)calculatorEventData, (CalculatorOutput) data); + break; + case calculation_cancelled: + processCalculationCancelled((CalculatorEvaluationEventData)calculatorEventData); + break; + case calculation_failed: + processCalculationFailed((CalculatorEvaluationEventData)calculatorEventData, (CalculatorFailure) data); + break; + } + + } + } + + private void processCalculationFailed(@NotNull CalculatorEvaluationEventData calculatorEventData, @NotNull CalculatorFailure data) { + + final CalculatorEvalException calculatorEvalException = data.getCalculationEvalException(); + + final String errorMessage; + if (calculatorEvalException != null) { + errorMessage = CalculatorMessages.getBundle().getString(CalculatorMessages.syntax_error); + } else { + final CalculatorParseException calculationParseException = data.getCalculationParseException(); + if (calculationParseException != null) { + errorMessage = calculationParseException.getLocalizedMessage(); + } else { + errorMessage = CalculatorMessages.getBundle().getString(CalculatorMessages.syntax_error); + } + } + + this.setViewState(CalculatorDisplayViewStateImpl.newErrorState(calculatorEventData.getOperation(), errorMessage)); + } + + private void processCalculationCancelled(@NotNull CalculatorEvaluationEventData calculatorEventData) { + final String errorMessage = CalculatorMessages.getBundle().getString(CalculatorMessages.syntax_error); + + this.setViewState(CalculatorDisplayViewStateImpl.newErrorState(calculatorEventData.getOperation(), errorMessage)); + } + + private void processCalculationResult(@NotNull CalculatorEvaluationEventData calculatorEventData, @NotNull CalculatorOutput data) { + final String stringResult = data.getStringResult(); + this.setViewState(CalculatorDisplayViewStateImpl.newValidState(calculatorEventData.getOperation(), data.getResult(), stringResult, 0)); + } +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplayView.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplayView.java new file mode 100644 index 00000000..5ddc45df --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplayView.java @@ -0,0 +1,16 @@ +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; + +/** + * User: serso + * Date: 9/20/12 + * Time: 8:25 PM + */ +public interface CalculatorDisplayView { + + void setState(@NotNull CalculatorDisplayViewState state); + + @NotNull + CalculatorDisplayViewState getState(); +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplayViewState.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplayViewState.java new file mode 100644 index 00000000..2fdc83b3 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplayViewState.java @@ -0,0 +1,33 @@ +package org.solovyev.android.calculator; + +import jscl.math.Generic; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.calculator.jscl.JsclOperation; + +/** + * User: serso + * Date: 9/20/12 + * Time: 9:50 PM + */ +public interface CalculatorDisplayViewState { + + @NotNull + String getText(); + + int getSelection(); + + @Nullable + Generic getResult(); + + boolean isValid(); + + @Nullable + String getErrorMessage(); + + @NotNull + JsclOperation getOperation(); + + @Nullable + String getStringResult(); +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplayViewStateImpl.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplayViewStateImpl.java new file mode 100644 index 00000000..ac13a9bc --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorDisplayViewStateImpl.java @@ -0,0 +1,104 @@ +package org.solovyev.android.calculator; + +import jscl.math.Generic; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.calculator.jscl.JsclOperation; +import org.solovyev.common.text.StringUtils; + +/** + * User: serso + * Date: 9/20/12 + * Time: 9:50 PM + */ +public class CalculatorDisplayViewStateImpl implements CalculatorDisplayViewState { + + @NotNull + private JsclOperation operation = JsclOperation.numeric; + + @Nullable + private Generic result; + + @Nullable + private String stringResult = ""; + + private boolean valid = true; + + @Nullable + private String errorMessage; + + private int selection = 0; + + private CalculatorDisplayViewStateImpl() { + } + + @NotNull + public static CalculatorDisplayViewState newDefaultInstance() { + return new CalculatorDisplayViewStateImpl(); + } + + @NotNull + public static CalculatorDisplayViewState newErrorState(@NotNull JsclOperation operation, + @NotNull String errorMessage) { + final CalculatorDisplayViewStateImpl calculatorDisplayState = new CalculatorDisplayViewStateImpl(); + calculatorDisplayState.valid = false; + calculatorDisplayState.errorMessage = errorMessage; + calculatorDisplayState.operation = operation; + return calculatorDisplayState; + } + + @NotNull + public static CalculatorDisplayViewState newValidState(@NotNull JsclOperation operation, + @Nullable Generic result, + @NotNull String stringResult, + int selection) { + final CalculatorDisplayViewStateImpl calculatorDisplayState = new CalculatorDisplayViewStateImpl(); + calculatorDisplayState.valid = true; + calculatorDisplayState.result = result; + calculatorDisplayState.stringResult = stringResult; + calculatorDisplayState.operation = operation; + calculatorDisplayState.selection = selection; + + return calculatorDisplayState; + } + + @NotNull + @Override + public String getText() { + return StringUtils.getNotEmpty(isValid() ? stringResult : errorMessage, ""); + } + + @Override + public int getSelection() { + return selection; + } + + @Nullable + @Override + public Generic getResult() { + return this.result; + } + + @Override + public boolean isValid() { + return this.valid; + } + + @Nullable + @Override + public String getErrorMessage() { + return this.errorMessage; + } + + @Override + @Nullable + public String getStringResult() { + return stringResult; + } + + @NotNull + @Override + public JsclOperation getOperation() { + return this.operation; + } +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEvaluationEventData.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEvaluationEventData.java new file mode 100644 index 00000000..45c74178 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEvaluationEventData.java @@ -0,0 +1,18 @@ +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; +import org.solovyev.android.calculator.jscl.JsclOperation; + +/** + * User: serso + * Date: 9/20/12 + * Time: 10:00 PM + */ +public interface CalculatorEvaluationEventData extends CalculatorEventData{ + + @NotNull + JsclOperation getOperation(); + + @NotNull + String getExpression(); +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEvaluationEventDataImpl.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEvaluationEventDataImpl.java new file mode 100644 index 00000000..677d6eca --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEvaluationEventDataImpl.java @@ -0,0 +1,58 @@ +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.calculator.jscl.JsclOperation; + +/** + * User: serso + * Date: 9/20/12 + * Time: 10:01 PM + */ +public class CalculatorEvaluationEventDataImpl implements CalculatorEvaluationEventData { + + @NotNull + private final CalculatorEventData calculatorEventData; + + @NotNull + private final JsclOperation operation; + + @NotNull + private final String expression; + + public CalculatorEvaluationEventDataImpl(@NotNull CalculatorEventData calculatorEventData, + @NotNull JsclOperation operation, + @NotNull String expression) { + this.calculatorEventData = calculatorEventData; + this.operation = operation; + this.expression = expression; + } + + @NotNull + @Override + public JsclOperation getOperation() { + return this.operation; + } + + @NotNull + @Override + public String getExpression() { + return this.expression; + } + + @Override + public long getEventId() { + return calculatorEventData.getEventId(); + } + + @Override + @Nullable + public Long getCalculationId() { + return calculatorEventData.getCalculationId(); + } + + @Override + public boolean isAfter(@NotNull CalculatorEventDataId calculatorEventDataId) { + return calculatorEventData.isAfter(calculatorEventDataId); + } +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventData.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventData.java index 5d3b348b..567808a7 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventData.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventData.java @@ -1,11 +1,10 @@ -package org.solovyev.android.calculator; - -import org.jetbrains.annotations.Nullable; - -/** - * User: Solovyev_S - * Date: 20.09.12 - * Time: 16:51 - */ -public interface CalculatorEventData extends CalculatorEventDataId { -} +package org.solovyev.android.calculator; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 16:51 + */ +public interface CalculatorEventData extends CalculatorEventDataId { + +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataId.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataId.java index 58d8dc11..61684be3 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataId.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataId.java @@ -1,17 +1,20 @@ -package org.solovyev.android.calculator; - -import org.jetbrains.annotations.Nullable; - -/** - * User: Solovyev_S - * Date: 20.09.12 - * Time: 18:18 - */ -public interface CalculatorEventDataId { - - // the higher id => the later event - long getEventId(); - - @Nullable - Long getCalculationId(); -} +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 18:18 + */ +public interface CalculatorEventDataId { + + // the higher id => the later event + long getEventId(); + + @Nullable + Long getCalculationId(); + + boolean isAfter(@NotNull CalculatorEventDataId calculatorEventDataId); +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataIdImpl.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataIdImpl.java index 1767719f..6992bef6 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataIdImpl.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataIdImpl.java @@ -1,61 +1,66 @@ -package org.solovyev.android.calculator; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * User: Solovyev_S - * Date: 20.09.12 - * Time: 18:18 - */ -public class CalculatorEventDataIdImpl implements CalculatorEventDataId { - - private final long eventId; - - @Nullable - private final Long calculationId; - - private CalculatorEventDataIdImpl(long id, - @Nullable Long calculationId) { - this.eventId = id; - this.calculationId = calculationId; - } - - @NotNull - public static CalculatorEventDataId newInstance(long id, - @Nullable Long calculationId) { - return new CalculatorEventDataIdImpl(id, calculationId); - } - - @Override - public long getEventId() { - return this.eventId; - } - - @Nullable - @Override - public Long getCalculationId() { - return this.calculationId; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof CalculatorEventDataIdImpl)) return false; - - CalculatorEventDataIdImpl that = (CalculatorEventDataIdImpl) o; - - if (eventId != that.eventId) return false; - if (calculationId != null ? !calculationId.equals(that.calculationId) : that.calculationId != null) - return false; - - return true; - } - - @Override - public int hashCode() { - int result = (int) (eventId ^ (eventId >>> 32)); - result = 31 * result + (calculationId != null ? calculationId.hashCode() : 0); - return result; - } -} +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 18:18 + */ +class CalculatorEventDataIdImpl implements CalculatorEventDataId { + + private final long eventId; + + @Nullable + private final Long calculationId; + + private CalculatorEventDataIdImpl(long id, + @Nullable Long calculationId) { + this.eventId = id; + this.calculationId = calculationId; + } + + @NotNull + static CalculatorEventDataId newInstance(long id, + @Nullable Long calculationId) { + return new CalculatorEventDataIdImpl(id, calculationId); + } + + @Override + public long getEventId() { + return this.eventId; + } + + @Nullable + @Override + public Long getCalculationId() { + return this.calculationId; + } + + @Override + public boolean isAfter(@NotNull CalculatorEventDataId calculatorEventDataId) { + return this.eventId > calculatorEventDataId.getEventId(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof CalculatorEventDataIdImpl)) return false; + + CalculatorEventDataIdImpl that = (CalculatorEventDataIdImpl) o; + + if (eventId != that.eventId) return false; + if (calculationId != null ? !calculationId.equals(that.calculationId) : that.calculationId != null) + return false; + + return true; + } + + @Override + public int hashCode() { + int result = (int) (eventId ^ (eventId >>> 32)); + result = 31 * result + (calculationId != null ? calculationId.hashCode() : 0); + return result; + } +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataImpl.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataImpl.java index 18c9a317..ad9d19b8 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataImpl.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataImpl.java @@ -1,47 +1,57 @@ -package org.solovyev.android.calculator; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * User: Solovyev_S - * Date: 20.09.12 - * Time: 16:54 - */ -class CalculatorEventDataImpl implements CalculatorEventData { - - @NotNull - private CalculatorEventDataId calculatorEventDataId; - - CalculatorEventDataImpl(@NotNull CalculatorEventDataId calculatorEventDataId) { - this.calculatorEventDataId = calculatorEventDataId; - } - - @Override - public long getEventId() { - return calculatorEventDataId.getEventId(); - } - - @Override - @Nullable - public Long getCalculationId() { - return calculatorEventDataId.getCalculationId(); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof CalculatorEventDataImpl)) return false; - - CalculatorEventDataImpl that = (CalculatorEventDataImpl) o; - - if (!calculatorEventDataId.equals(that.calculatorEventDataId)) return false; - - return true; - } - - @Override - public int hashCode() { - return calculatorEventDataId.hashCode(); - } -} +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 16:54 + */ +class CalculatorEventDataImpl implements CalculatorEventData { + + @NotNull + private CalculatorEventDataId calculatorEventDataId; + + private CalculatorEventDataImpl(@NotNull CalculatorEventDataId calculatorEventDataId) { + this.calculatorEventDataId = calculatorEventDataId; + } + + @NotNull + public static CalculatorEventData newInstance(@NotNull CalculatorEventDataId calculatorEventDataId) { + return new CalculatorEventDataImpl(calculatorEventDataId); + } + + @Override + public long getEventId() { + return calculatorEventDataId.getEventId(); + } + + @Override + @Nullable + public Long getCalculationId() { + return calculatorEventDataId.getCalculationId(); + } + + @Override + public boolean isAfter(@NotNull CalculatorEventDataId calculatorEventDataId) { + return this.calculatorEventDataId.isAfter(calculatorEventDataId); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof CalculatorEventDataImpl)) return false; + + CalculatorEventDataImpl that = (CalculatorEventDataImpl) o; + + if (!calculatorEventDataId.equals(that.calculatorEventDataId)) return false; + + return true; + } + + @Override + public int hashCode() { + return calculatorEventDataId.hashCode(); + } +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventType.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventType.java index 7df859e9..c06da282 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventType.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventType.java @@ -1,9 +1,43 @@ -package org.solovyev.android.calculator; - -/** - * User: Solovyev_S - * Date: 20.09.12 - * Time: 16:40 - */ -public enum CalculatorEventType { -} +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 16:40 + */ +public enum CalculatorEventType { + + /* + ********************************************************************** + * + * org.solovyev.android.calculator.CalculatorEvaluationEventData + * + ********************************************************************** + */ + + // @NotNull org.solovyev.android.calculator.CalculatorInput + calculation_started, + + // @NotNull org.solovyev.android.calculator.CalculatorOutput + calculation_result, + + calculation_cancelled, + + calculation_finished, + + // @NotNull org.solovyev.android.calculator.CalculatorFailure + calculation_failed; + + public boolean isOfType(@NotNull CalculatorEventType... types) { + for (CalculatorEventType type : types) { + if ( this == type ) { + return true; + } + } + + return false; + } + +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorFailure.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorFailure.java new file mode 100644 index 00000000..5d07bb40 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorFailure.java @@ -0,0 +1,21 @@ +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * User: serso + * Date: 9/20/12 + * Time: 7:33 PM + */ +public interface CalculatorFailure { + + @NotNull + Exception getException(); + + @Nullable + CalculatorParseException getCalculationParseException(); + + @Nullable + CalculatorEvalException getCalculationEvalException(); +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorFailureImpl.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorFailureImpl.java new file mode 100644 index 00000000..866146b3 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorFailureImpl.java @@ -0,0 +1,34 @@ +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; + +/** + * User: serso + * Date: 9/20/12 + * Time: 7:34 PM + */ +public class CalculatorFailureImpl implements CalculatorFailure { + + @NotNull + private Exception exception; + + public CalculatorFailureImpl(@NotNull Exception exception) { + this.exception = exception; + } + + @NotNull + @Override + public Exception getException() { + return this.exception; + } + + @Override + public CalculatorParseException getCalculationParseException() { + return exception instanceof CalculatorParseException ? (CalculatorParseException)exception : null; + } + + @Override + public CalculatorEvalException getCalculationEvalException() { + return exception instanceof CalculatorEvalException ? (CalculatorEvalException)exception : null; + } +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorImpl.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorImpl.java index c98d1712..fab2b16a 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorImpl.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorImpl.java @@ -1,168 +1,209 @@ -package org.solovyev.android.calculator; - -import jscl.AbstractJsclArithmeticException; -import jscl.NumeralBaseException; -import jscl.math.Generic; -import jscl.text.ParseInterruptedException; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.solovyev.android.calculator.jscl.JsclOperation; -import org.solovyev.android.calculator.text.TextProcessor; -import org.solovyev.common.msg.MessageRegistry; - -import java.util.List; -import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicLong; - -/** - * User: Solovyev_S - * Date: 20.09.12 - * Time: 16:42 - */ -public class CalculatorImpl implements Calculator { - - @NotNull - private final CalculatorEventContainer calculatorEventContainer = new ListCalculatorEventContainer(); - - @NotNull - private static final Calculator instance = new CalculatorImpl(); - - @NotNull - private final AtomicLong counter = new AtomicLong(0); - - @NotNull - private final Object lock = new Object(); - - @NotNull - private final TextProcessor preprocessor = ToJsclTextProcessor.getInstance(); - - @NotNull - private final Executor threadPoolExecutor = Executors.newFixedThreadPool(10); - - private CalculatorImpl() { - } - - @NotNull - public static Calculator getInstance() { - return instance; - } - - @NotNull - private CalculatorEventDataId nextCalculatorEventDataId() { - long eventId = counter.incrementAndGet(); - return CalculatorEventDataIdImpl.newInstance(eventId, eventId); - } - - /* - ********************************************************************** - * - * CALCULATION - * - ********************************************************************** - */ - - public void evaluate(@NotNull JsclOperation operation, - @NotNull String expression) { - evaluate(operation, expression, null); - } - - public void evaluate(@NotNull final JsclOperation operation, - @NotNull final String expression, - @Nullable final MessageRegistry mr) { - - final CalculatorEventDataId eventDataId = nextCalculatorEventDataId(); - - threadPoolExecutor.execute(new Runnable() { - @Override - public void run() { - CalculatorImpl.this.evaluate(eventDataId, operation, expression, mr); - } - }); - } - - private void evaluate(@NotNull CalculatorEventDataId eventDataId, - @NotNull JsclOperation operation, - @NotNull String expression, - @Nullable MessageRegistry mr) { - synchronized (lock) { - PreparedExpression preparedExpression = null; - - try { - preparedExpression = preprocessor.process(expression); - - final String jsclExpression = preparedExpression.toString(); - try { - - final Generic genericResult = operation.evaluateGeneric(jsclExpression); - - // NOTE: toString() method must be called here as ArithmeticOperationException may occur in it (just to avoid later check!) - genericResult.toString(); - - //return new Result(operation.getFromProcessor().process(genericResult), operation, genericResult); - } catch (AbstractJsclArithmeticException e) { - handleException(eventDataId, operation, expression, mr, preparedExpression, null, new CalculatorEvalException(e, e, jsclExpression)); - } - - } catch (ArithmeticException e) { - //final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_1, MessageType.error, CalculatorApplication.getInstance(), e.getMessage()); - handleException(operation, expression, mr, preparedExpression, new CalculatorParseException(jsclExpression, androidMessage)); - } catch (StackOverflowError e) { - //final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_2, MessageType.error, CalculatorApplication.getInstance()); - handleException(eventDataId, operation, expression, mr, preparedExpression, new CalculatorParseException(e), null); - } catch (jscl.text.ParseException e) { - //System.out.println(e.getMessage()); - handleException(eventDataId, operation, expression, mr, preparedExpression, new CalculatorParseException(e), null); - } catch (ParseInterruptedException e) { - // do nothing - we ourselves interrupt the calculations - } catch (CalculatorParseException e) { - handleException(eventDataId, operation, expression, mr, preparedExpression, e, null); - } - } - } - - private void handleException(@NotNull CalculatorEventDataId eventDataId, - @NotNull JsclOperation operation, - @NotNull String expression, - @Nullable MessageRegistry mr, - @Nullable PreparedExpression preparedExpression, - @Nullable CalculatorParseException parseException, - @Nullable CalculatorEvalException evalException) { - if (operation == JsclOperation.numeric && (preparedExpression != null && preparedExpression.isExistsUndefinedVar() || (evalException != null && evalException.getCause() instanceof NumeralBaseException))) { - evaluate(eventDataId, JsclOperation.simplify, expression, mr); - } - - if (parseException != null) { - throw parseException; - } else { - throw evalException; - } - } - - /* - ********************************************************************** - * - * EVENTS - * - ********************************************************************** - */ - - @Override - public void addCalculatorEventListener(@NotNull CalculatorEventListener calculatorEventListener) { - calculatorEventContainer.addCalculatorEventListener(calculatorEventListener); - } - - @Override - public void removeCalculatorEventListener(@NotNull CalculatorEventListener calculatorEventListener) { - calculatorEventContainer.removeCalculatorEventListener(calculatorEventListener); - } - - @Override - public void fireCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) { - calculatorEventContainer.fireCalculatorEvent(calculatorEventData, calculatorEventType, data); - } - - @Override - public void fireCalculatorEvents(@NotNull List calculatorEvents) { - calculatorEventContainer.fireCalculatorEvents(calculatorEvents); - } -} +package org.solovyev.android.calculator; + +import jscl.AbstractJsclArithmeticException; +import jscl.NumeralBaseException; +import jscl.math.Generic; +import jscl.text.ParseInterruptedException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.calculator.jscl.JsclOperation; +import org.solovyev.android.calculator.text.TextProcessor; +import org.solovyev.common.msg.MessageRegistry; +import org.solovyev.common.msg.MessageType; + +import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicLong; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 16:42 + */ +public class CalculatorImpl implements Calculator { + + private static final long FIRST_ID = 0; + + @NotNull + private final CalculatorEventContainer calculatorEventContainer = new ListCalculatorEventContainer(); + + @NotNull + private final AtomicLong counter = new AtomicLong(FIRST_ID); + + @NotNull + private final Object lock = new Object(); + + @NotNull + private final TextProcessor preprocessor = ToJsclTextProcessor.getInstance(); + + @NotNull + private final Executor threadPoolExecutor = Executors.newFixedThreadPool(10); + + public CalculatorImpl() { + } + + @NotNull + private CalculatorEventDataId nextCalculatorEventDataId() { + long eventId = counter.incrementAndGet(); + return CalculatorEventDataIdImpl.newInstance(eventId, eventId); + } + + @NotNull + private CalculatorEventDataId nextEventDataId(@NotNull Long calculationId) { + long eventId = counter.incrementAndGet(); + return CalculatorEventDataIdImpl.newInstance(eventId, calculationId); + } + + /* + ********************************************************************** + * + * CALCULATION + * + ********************************************************************** + */ + + @NotNull + @Override + public CalculatorEventDataId createFirstEventDataId() { + return CalculatorEventDataIdImpl.newInstance(FIRST_ID, FIRST_ID); + } + + @Override + public void evaluate(@NotNull JsclOperation operation, + @NotNull String expression) { + evaluate(operation, expression, null); + } + + @Override + @NotNull + public CalculatorEventDataId evaluate(@NotNull final JsclOperation operation, + @NotNull final String expression, + @Nullable final MessageRegistry mr) { + + final CalculatorEventDataId eventDataId = nextCalculatorEventDataId(); + + threadPoolExecutor.execute(new Runnable() { + @Override + public void run() { + CalculatorImpl.this.evaluate(eventDataId.getCalculationId(), operation, expression, mr); + } + }); + + return eventDataId; + } + + private void evaluate(@NotNull Long calculationId, + @NotNull JsclOperation operation, + @NotNull String expression, + @Nullable MessageRegistry mr) { + synchronized (lock) { + + PreparedExpression preparedExpression = null; + + fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_started, new CalculatorInputImpl(expression, operation)); + + try { + preparedExpression = preprocessor.process(expression); + + final String jsclExpression = preparedExpression.toString(); + + try { + + final Generic result = operation.evaluateGeneric(jsclExpression); + + // NOTE: toString() method must be called here as ArithmeticOperationException may occur in it (just to avoid later check!) + result.toString(); + + final CalculatorOutputImpl data = new CalculatorOutputImpl(operation.getFromProcessor().process(result), operation, result); + fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_result, data); + + } catch (AbstractJsclArithmeticException e) { + handleException(calculationId, operation, expression, mr, new CalculatorEvalException(e, e, jsclExpression)); + } + + } catch (ArithmeticException e) { + handleException(calculationId, operation, expression, mr, preparedExpression, new CalculatorParseException(expression, new CalculatorMessage(CalculatorMessages.msg_001, MessageType.error, e.getMessage()))); + } catch (StackOverflowError e) { + handleException(calculationId, operation, expression, mr, preparedExpression, new CalculatorParseException(expression, new CalculatorMessage(CalculatorMessages.msg_002, MessageType.error))); + } catch (jscl.text.ParseException e) { + handleException(calculationId, operation, expression, mr, preparedExpression, new CalculatorParseException(e)); + } catch (ParseInterruptedException e) { + + // do nothing - we ourselves interrupt the calculations + fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_cancelled, null); + + } catch (CalculatorParseException e) { + handleException(calculationId, operation, expression, mr, preparedExpression, e); + } finally { + fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_finished, null); + } + } + } + + @NotNull + private CalculatorEventData newCalculationEventData(@NotNull JsclOperation operation, + @NotNull String expression, + @NotNull Long calculationId) { + return new CalculatorEvaluationEventDataImpl(CalculatorEventDataImpl.newInstance(nextEventDataId(calculationId)), operation, expression); + } + + private void handleException(@NotNull Long calculationId, + @NotNull JsclOperation operation, + @NotNull String expression, + @Nullable MessageRegistry mr, + @Nullable PreparedExpression preparedExpression, + @NotNull CalculatorParseException parseException) { + + if (operation == JsclOperation.numeric + && preparedExpression != null + && preparedExpression.isExistsUndefinedVar()) { + + evaluate(calculationId, JsclOperation.simplify, expression, mr); + + } + + fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_failed, new CalculatorFailureImpl(parseException)); + } + + private void handleException(@NotNull Long calculationId, + @NotNull JsclOperation operation, + @NotNull String expression, + @Nullable MessageRegistry mr, + @NotNull CalculatorEvalException evalException) { + + if (operation == JsclOperation.numeric && evalException.getCause() instanceof NumeralBaseException) { + evaluate(calculationId, JsclOperation.simplify, expression, mr); + } + + fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_failed, new CalculatorFailureImpl(evalException)); + } + + /* + ********************************************************************** + * + * EVENTS + * + ********************************************************************** + */ + + @Override + public void addCalculatorEventListener(@NotNull CalculatorEventListener calculatorEventListener) { + calculatorEventContainer.addCalculatorEventListener(calculatorEventListener); + } + + @Override + public void removeCalculatorEventListener(@NotNull CalculatorEventListener calculatorEventListener) { + calculatorEventContainer.removeCalculatorEventListener(calculatorEventListener); + } + + @Override + public void fireCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) { + calculatorEventContainer.fireCalculatorEvent(calculatorEventData, calculatorEventType, data); + } + + @Override + public void fireCalculatorEvents(@NotNull List calculatorEvents) { + calculatorEventContainer.fireCalculatorEvents(calculatorEvents); + } +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorInput.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorInput.java new file mode 100644 index 00000000..1f0c98ab --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorInput.java @@ -0,0 +1,18 @@ +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; +import org.solovyev.android.calculator.jscl.JsclOperation; + +/** + * User: serso + * Date: 9/20/12 + * Time: 7:25 PM + */ +public interface CalculatorInput { + + @NotNull + String getExpression(); + + @NotNull + JsclOperation getOperation(); +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorInputImpl.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorInputImpl.java new file mode 100644 index 00000000..2ad4608c --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorInputImpl.java @@ -0,0 +1,35 @@ +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; +import org.solovyev.android.calculator.jscl.JsclOperation; + +/** + * User: serso + * Date: 9/20/12 + * Time: 7:26 PM + */ +public class CalculatorInputImpl implements CalculatorInput { + + @NotNull + private String expression; + + @NotNull + private JsclOperation operation; + + public CalculatorInputImpl(@NotNull String expression, @NotNull JsclOperation operation) { + this.expression = expression; + this.operation = operation; + } + + @Override + @NotNull + public String getExpression() { + return expression; + } + + @Override + @NotNull + public JsclOperation getOperation() { + return operation; + } +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorLocator.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorLocator.java index 5b74bde7..3b04e8be 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorLocator.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorLocator.java @@ -1,16 +1,22 @@ -package org.solovyev.android.calculator; - -import org.jetbrains.annotations.NotNull; - -/** - * User: Solovyev_S - * Date: 20.09.12 - * Time: 12:45 - */ -public interface CalculatorLocator { - - @NotNull - JCalculatorEngine getCalculatorEngine(); - - void setCalculatorEngine(@NotNull JCalculatorEngine calculatorEngine); -} +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 12:45 + */ +public interface CalculatorLocator { + + @NotNull + JCalculatorEngine getCalculatorEngine(); + + @NotNull + Calculator getCalculator(); + + @NotNull + CalculatorDisplay getCalculatorDisplay(); + + void setCalculatorEngine(@NotNull JCalculatorEngine calculatorEngine); +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorLocatorImpl.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorLocatorImpl.java index 6cee9b7c..989e1445 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorLocatorImpl.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorLocatorImpl.java @@ -1,36 +1,54 @@ -package org.solovyev.android.calculator; - -import org.jetbrains.annotations.NotNull; - -/** - * User: Solovyev_S - * Date: 20.09.12 - * Time: 12:45 - */ -public class CalculatorLocatorImpl implements CalculatorLocator { - - @NotNull - private JCalculatorEngine calculatorEngine; - - @NotNull - private static final CalculatorLocator instance = new CalculatorLocatorImpl(); - - private CalculatorLocatorImpl() { - } - - @NotNull - public static CalculatorLocator getInstance() { - return instance; - } - - @NotNull - @Override - public JCalculatorEngine getCalculatorEngine() { - return calculatorEngine; - } - - @Override - public void setCalculatorEngine(@NotNull JCalculatorEngine calculatorEngine) { - this.calculatorEngine = calculatorEngine; - } -} +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; + +/** + * User: Solovyev_S + * Date: 20.09.12 + * Time: 12:45 + */ +public class CalculatorLocatorImpl implements CalculatorLocator { + + @NotNull + private JCalculatorEngine calculatorEngine; + + @NotNull + private CalculatorDisplay calculatorDisplay = new CalculatorDisplayImpl(); + + @NotNull + private Calculator calculator = new CalculatorImpl(); + + @NotNull + private static final CalculatorLocator instance = new CalculatorLocatorImpl(); + + private CalculatorLocatorImpl() { + } + + @NotNull + public static CalculatorLocator getInstance() { + return instance; + } + + @NotNull + @Override + public JCalculatorEngine getCalculatorEngine() { + return calculatorEngine; + } + + @NotNull + @Override + public Calculator getCalculator() { + return this.calculator; + } + + @Override + public void setCalculatorEngine(@NotNull JCalculatorEngine calculatorEngine) { + this.calculatorEngine = calculatorEngine; + } + + @Override + @NotNull + public CalculatorDisplay getCalculatorDisplay() { + return calculatorDisplay; + } +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorMessage.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorMessage.java new file mode 100644 index 00000000..ab7e1763 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorMessage.java @@ -0,0 +1,31 @@ +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; +import org.solovyev.common.msg.AbstractMessage; +import org.solovyev.common.msg.MessageType; + +import java.util.List; +import java.util.Locale; +import java.util.ResourceBundle; + +/** + * User: serso + * Date: 9/20/12 + * Time: 8:06 PM + */ +public class CalculatorMessage extends AbstractMessage { + + protected CalculatorMessage(@NotNull String messageCode, @NotNull MessageType messageType, @org.jetbrains.annotations.Nullable Object... parameters) { + super(messageCode, messageType, parameters); + } + + protected CalculatorMessage(@NotNull String messageCode, @NotNull MessageType messageType, @NotNull List parameters) { + super(messageCode, messageType, parameters); + } + + @Override + protected String getMessagePattern(@NotNull Locale locale) { + final ResourceBundle rb = CalculatorMessages.getBundle(locale); + return rb.getString(getMessageCode()); + } +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorMessages.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorMessages.java new file mode 100644 index 00000000..aa21657b --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorMessages.java @@ -0,0 +1,69 @@ +package org.solovyev.android.calculator; + +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.ResourceBundle; + +/** + * User: serso + * Date: 9/20/12 + * Time: 8:10 PM + */ +public final class CalculatorMessages { + + @NotNull + private static final Map bundlesByLocale = new HashMap(); + + private CalculatorMessages() { + throw new AssertionError(); + } + + @NotNull + public static ResourceBundle getBundle() { + return getBundle(Locale.getDefault()); + } + + @NotNull + public static ResourceBundle getBundle(@NotNull Locale locale) { + synchronized (bundlesByLocale) { + ResourceBundle result = bundlesByLocale.get(locale); + if (result == null) { + result = ResourceBundle.getBundle("org/solovyev/android/calculator/messages", locale); + bundlesByLocale.put(locale, result); + } + + return result; + } + } + + /* Arithmetic error occurred: {0} */ + @NotNull + public static final String msg_001 = "msg_1"; + + /* Too complex expression*/ + @NotNull + public static final String msg_002 = "msg_2"; + + /* Too long execution time - check the expression*/ + @NotNull + public static final String msg_003 = "msg_3"; + + /* Evaluation was cancelled*/ + @NotNull + public static final String msg_004 = "msg_4"; + + /* No parameters are specified for function: {0}*/ + @NotNull + public static final String msg_005 = "msg_5"; + + /* Infinite loop is detected in expression*/ + @NotNull + public static final String msg_006 = "msg_6"; + + /* Error */ + @NotNull + public static final String syntax_error = "syntax_error"; +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorOutput.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorOutput.java new file mode 100644 index 00000000..bac56104 --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorOutput.java @@ -0,0 +1,22 @@ +package org.solovyev.android.calculator; + +import jscl.math.Generic; +import org.jetbrains.annotations.NotNull; +import org.solovyev.android.calculator.jscl.JsclOperation; + +/** + * User: serso + * Date: 9/20/12 + * Time: 7:29 PM + */ +public interface CalculatorOutput { + + @NotNull + String getStringResult(); + + @NotNull + JsclOperation getOperation(); + + @NotNull + Generic getResult(); +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorOutputImpl.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorOutputImpl.java new file mode 100644 index 00000000..0ca7e1fe --- /dev/null +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorOutputImpl.java @@ -0,0 +1,46 @@ +package org.solovyev.android.calculator; + +import jscl.math.Generic; +import org.jetbrains.annotations.NotNull; +import org.solovyev.android.calculator.jscl.JsclOperation; + +/** +* User: serso +* Date: 9/20/12 +* Time: 7:28 PM +*/ +public class CalculatorOutputImpl implements CalculatorOutput { + + @NotNull + private Generic result; + + @NotNull + private String stringResult; + + @NotNull + private JsclOperation operation; + + public CalculatorOutputImpl(@NotNull String stringResult, @NotNull JsclOperation operation, @NotNull Generic result) { + this.stringResult = stringResult; + this.operation = operation; + this.result = result; + } + + @Override + @NotNull + public String getStringResult() { + return stringResult; + } + + @Override + @NotNull + public JsclOperation getOperation() { + return operation; + } + + @Override + @NotNull + public Generic getResult() { + return result; + } +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/JCalculatorDisplay.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/JCalculatorDisplay.java deleted file mode 100644 index 1594893a..00000000 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/JCalculatorDisplay.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - * or visit http://se.solovyev.org - */ - -package org.solovyev.android.calculator; - -import jscl.math.Generic; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.solovyev.android.calculator.Editor; -import org.solovyev.android.calculator.jscl.JsclOperation; - -/** - * User: serso - * Date: 12/17/11 - * Time: 9:45 PM - */ -public interface JCalculatorDisplay extends Editor{ - - boolean isValid(); - - void setValid(boolean valid); - - @Nullable - String getErrorMessage(); - - void setErrorMessage(@Nullable String errorMessage); - - void setJsclOperation(@NotNull JsclOperation jsclOperation); - - @NotNull - JsclOperation getJsclOperation(); - - void setGenericResult(@Nullable Generic genericResult); - - @Nullable - Generic getGenericResult(); -} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorDisplayHistoryState.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorDisplayHistoryState.java index 356f953a..c8c37c41 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorDisplayHistoryState.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorDisplayHistoryState.java @@ -1,138 +1,144 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - */ - -package org.solovyev.android.calculator.history; - -import jscl.math.Generic; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; -import org.simpleframework.xml.Transient; -import org.solovyev.android.calculator.JCalculatorDisplay; -import org.solovyev.android.calculator.jscl.JsclOperation; - -/** - * User: serso - * Date: 9/17/11 - * Time: 11:05 PM - */ - -@Root -public class CalculatorDisplayHistoryState implements Cloneable { - - @Transient - private boolean valid = true; - - @Transient - @Nullable - private String errorMessage = null; - - @Element - @NotNull - private EditorHistoryState editorState; - - @Element - @NotNull - private JsclOperation jsclOperation; - - @Transient - @Nullable - private Generic genericResult; - - private CalculatorDisplayHistoryState() { - // for xml - } - - @NotNull - public static CalculatorDisplayHistoryState newInstance(@NotNull JCalculatorDisplay display) { - final CalculatorDisplayHistoryState result = new CalculatorDisplayHistoryState(); - - result.editorState = EditorHistoryState.newInstance(display); - result.valid = display.isValid(); - result.jsclOperation = display.getJsclOperation(); - result.genericResult = display.getGenericResult(); - result.errorMessage = display.getErrorMessage(); - - return result; - } - - public void setValuesFromHistory(@NotNull JCalculatorDisplay display) { - this.getEditorState().setValuesFromHistory(display); - display.setValid(this.isValid()); - display.setErrorMessage(this.getErrorMessage()); - display.setJsclOperation(this.getJsclOperation()); - display.setGenericResult(this.getGenericResult()); - } - - - public boolean isValid() { - return valid; - } - - @NotNull - public EditorHistoryState getEditorState() { - return editorState; - } - - @NotNull - public JsclOperation getJsclOperation() { - return jsclOperation; - } - - @Nullable - public String getErrorMessage() { - return errorMessage; - } - - @Nullable - public Generic getGenericResult() { - return genericResult; - } - - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - CalculatorDisplayHistoryState that = (CalculatorDisplayHistoryState) o; - - if (!editorState.equals(that.editorState)) return false; - if (jsclOperation != that.jsclOperation) return false; - - return true; - } - - @Override - public int hashCode() { - int result = editorState.hashCode(); - result = 31 * result + jsclOperation.hashCode(); - return result; - } - - @Override - public String toString() { - return "CalculatorDisplayHistoryState{" + - "valid=" + valid + - ", errorMessage='" + errorMessage + '\'' + - ", editorHistoryState=" + editorState + - ", jsclOperation=" + jsclOperation + - '}'; - } - - @Override - protected CalculatorDisplayHistoryState clone() { - try { - final CalculatorDisplayHistoryState clone = (CalculatorDisplayHistoryState) super.clone(); - - clone.editorState = this.editorState.clone(); - - return clone; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + */ + +package org.solovyev.android.calculator.history; + +import jscl.math.Generic; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.simpleframework.xml.Element; +import org.simpleframework.xml.Root; +import org.simpleframework.xml.Transient; +import org.solovyev.android.calculator.CalculatorDisplay; +import org.solovyev.android.calculator.CalculatorDisplayViewState; +import org.solovyev.android.calculator.CalculatorDisplayViewStateImpl; +import org.solovyev.android.calculator.jscl.JsclOperation; +import org.solovyev.common.text.StringUtils; + +/** + * User: serso + * Date: 9/17/11 + * Time: 11:05 PM + */ + +@Root +public class CalculatorDisplayHistoryState implements Cloneable { + + @Transient + private boolean valid = true; + + @Transient + @Nullable + private String errorMessage = null; + + @Element + @NotNull + private EditorHistoryState editorState; + + @Element + @NotNull + private JsclOperation jsclOperation; + + @Transient + @Nullable + private Generic genericResult; + + private CalculatorDisplayHistoryState() { + // for xml + } + + @NotNull + public static CalculatorDisplayHistoryState newInstance(@NotNull CalculatorDisplay display) { + final CalculatorDisplayHistoryState result = new CalculatorDisplayHistoryState(); + + result.editorState = EditorHistoryState.newInstance(display.getViewState()); + + final CalculatorDisplayViewState displayViewState = display.getViewState(); + + result.valid = displayViewState.isValid(); + result.jsclOperation = displayViewState.getOperation(); + result.genericResult = displayViewState.getResult(); + result.errorMessage = displayViewState.getErrorMessage(); + + return result; + } + + public void setValuesFromHistory(@NotNull CalculatorDisplay display) { + if ( this.isValid() ) { + display.setViewState(CalculatorDisplayViewStateImpl.newValidState(this.getJsclOperation(), this.getGenericResult(), StringUtils.getNotEmpty(this.getEditorState().getText(), ""), this.getEditorState().getCursorPosition())); + } else { + display.setViewState(CalculatorDisplayViewStateImpl.newErrorState(this.getJsclOperation(), StringUtils.getNotEmpty(this.getErrorMessage(), ""))); + } + } + + + public boolean isValid() { + return valid; + } + + @NotNull + public EditorHistoryState getEditorState() { + return editorState; + } + + @NotNull + public JsclOperation getJsclOperation() { + return jsclOperation; + } + + @Nullable + public String getErrorMessage() { + return errorMessage; + } + + @Nullable + public Generic getGenericResult() { + return genericResult; + } + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + CalculatorDisplayHistoryState that = (CalculatorDisplayHistoryState) o; + + if (!editorState.equals(that.editorState)) return false; + if (jsclOperation != that.jsclOperation) return false; + + return true; + } + + @Override + public int hashCode() { + int result = editorState.hashCode(); + result = 31 * result + jsclOperation.hashCode(); + return result; + } + + @Override + public String toString() { + return "CalculatorDisplayHistoryState{" + + "valid=" + valid + + ", errorMessage='" + errorMessage + '\'' + + ", editorHistoryState=" + editorState + + ", jsclOperation=" + jsclOperation + + '}'; + } + + @Override + protected CalculatorDisplayHistoryState clone() { + try { + final CalculatorDisplayHistoryState clone = (CalculatorDisplayHistoryState) super.clone(); + + clone.editorState = this.editorState.clone(); + + return clone; + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorHistoryState.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorHistoryState.java index a7d37aaa..f866596a 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorHistoryState.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/CalculatorHistoryState.java @@ -1,111 +1,111 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - */ - -package org.solovyev.android.calculator.history; - -import org.jetbrains.annotations.NotNull; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; -import org.solovyev.android.calculator.Editor; -import org.solovyev.android.calculator.JCalculatorDisplay; - -/** - * User: serso - * Date: 9/11/11 - * Time: 12:16 AM - */ - -@Root -public class CalculatorHistoryState extends AbstractHistoryState { - - @Element - @NotNull - private EditorHistoryState editorState; - - @Element - @NotNull - private CalculatorDisplayHistoryState displayState; - - private CalculatorHistoryState() { - // for xml - } - - private CalculatorHistoryState(@NotNull EditorHistoryState editorState, - @NotNull CalculatorDisplayHistoryState displayState) { - this.editorState = editorState; - this.displayState = displayState; - } - - public static CalculatorHistoryState newInstance(@NotNull Editor editor, @NotNull JCalculatorDisplay display) { - final EditorHistoryState editorHistoryState = EditorHistoryState.newInstance(editor); - final CalculatorDisplayHistoryState displayHistoryState = CalculatorDisplayHistoryState.newInstance(display); - return new CalculatorHistoryState(editorHistoryState, displayHistoryState); - } - - @NotNull - public EditorHistoryState getEditorState() { - return editorState; - } - - public void setEditorState(@NotNull EditorHistoryState editorState) { - this.editorState = editorState; - } - - @NotNull - public CalculatorDisplayHistoryState getDisplayState() { - return displayState; - } - - public void setDisplayState(@NotNull CalculatorDisplayHistoryState displayState) { - this.displayState = displayState; - } - - @Override - public String toString() { - return "CalculatorHistoryState{" + - "editorState=" + editorState + - ", displayState=" + displayState + - '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - CalculatorHistoryState that = (CalculatorHistoryState) o; - - if (this.isSaved() != that.isSaved()) return false; - if (this.getId() != that.getId()) return false; - if (!displayState.equals(that.displayState)) return false; - if (!editorState.equals(that.editorState)) return false; - - return true; - } - - @Override - public int hashCode() { - int result = Boolean.valueOf(isSaved()).hashCode(); - result = 31 * result + getId(); - result = 31 * result + editorState.hashCode(); - result = 31 * result + displayState.hashCode(); - return result; - } - - public void setValuesFromHistory(@NotNull Editor editor, @NotNull JCalculatorDisplay display) { - this.getEditorState().setValuesFromHistory(editor); - this.getDisplayState().setValuesFromHistory(display); - } - - @Override - protected CalculatorHistoryState clone() { - final CalculatorHistoryState clone = (CalculatorHistoryState)super.clone(); - - clone.editorState = this.editorState.clone(); - clone.displayState = this.displayState.clone(); - - return clone; - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + */ + +package org.solovyev.android.calculator.history; + +import org.jetbrains.annotations.NotNull; +import org.simpleframework.xml.Element; +import org.simpleframework.xml.Root; +import org.solovyev.android.calculator.CalculatorDisplay; +import org.solovyev.android.calculator.Editor; + +/** + * User: serso + * Date: 9/11/11 + * Time: 12:16 AM + */ + +@Root +public class CalculatorHistoryState extends AbstractHistoryState { + + @Element + @NotNull + private EditorHistoryState editorState; + + @Element + @NotNull + private CalculatorDisplayHistoryState displayState; + + private CalculatorHistoryState() { + // for xml + } + + private CalculatorHistoryState(@NotNull EditorHistoryState editorState, + @NotNull CalculatorDisplayHistoryState displayState) { + this.editorState = editorState; + this.displayState = displayState; + } + + public static CalculatorHistoryState newInstance(@NotNull Editor editor, @NotNull CalculatorDisplay display) { + final EditorHistoryState editorHistoryState = EditorHistoryState.newInstance(editor); + final CalculatorDisplayHistoryState displayHistoryState = CalculatorDisplayHistoryState.newInstance(display); + return new CalculatorHistoryState(editorHistoryState, displayHistoryState); + } + + @NotNull + public EditorHistoryState getEditorState() { + return editorState; + } + + public void setEditorState(@NotNull EditorHistoryState editorState) { + this.editorState = editorState; + } + + @NotNull + public CalculatorDisplayHistoryState getDisplayState() { + return displayState; + } + + public void setDisplayState(@NotNull CalculatorDisplayHistoryState displayState) { + this.displayState = displayState; + } + + @Override + public String toString() { + return "CalculatorHistoryState{" + + "editorState=" + editorState + + ", displayState=" + displayState + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + CalculatorHistoryState that = (CalculatorHistoryState) o; + + if (this.isSaved() != that.isSaved()) return false; + if (this.getId() != that.getId()) return false; + if (!displayState.equals(that.displayState)) return false; + if (!editorState.equals(that.editorState)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = Boolean.valueOf(isSaved()).hashCode(); + result = 31 * result + getId(); + result = 31 * result + editorState.hashCode(); + result = 31 * result + displayState.hashCode(); + return result; + } + + public void setValuesFromHistory(@NotNull Editor editor, @NotNull CalculatorDisplay display) { + this.getEditorState().setValuesFromHistory(editor); + this.getDisplayState().setValuesFromHistory(display); + } + + @Override + protected CalculatorHistoryState clone() { + final CalculatorHistoryState clone = (CalculatorHistoryState)super.clone(); + + clone.editorState = this.editorState.clone(); + clone.displayState = this.displayState.clone(); + + return clone; + } +} diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/EditorHistoryState.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/EditorHistoryState.java index ea2de8ab..2ffa241b 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/EditorHistoryState.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/history/EditorHistoryState.java @@ -1,88 +1,99 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - */ - -package org.solovyev.android.calculator.history; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; -import org.solovyev.android.calculator.Editor; - -@Root -public class EditorHistoryState implements Cloneable{ - - @Element - private int cursorPosition; - - @Element(required = false) - @Nullable - private String text; - - private EditorHistoryState() { - // for xml - } - - @NotNull - public static EditorHistoryState newInstance(@NotNull Editor editor) { - final EditorHistoryState result = new EditorHistoryState(); - - result.text = String.valueOf(editor.getText()); - result.cursorPosition = editor.getSelection(); - - return result; - } - - public void setValuesFromHistory(@NotNull Editor editor) { - editor.setText(this.getText()); - editor.setSelection(this.getCursorPosition()); - } - - @Nullable - public String getText() { - return text; - } - - public int getCursorPosition() { - return cursorPosition; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof EditorHistoryState)) return false; - - EditorHistoryState that = (EditorHistoryState) o; - - if (cursorPosition != that.cursorPosition) return false; - if (text != null ? !text.equals(that.text) : that.text != null) return false; - - return true; - } - - @Override - public int hashCode() { - int result = cursorPosition; - result = 31 * result + (text != null ? text.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "EditorHistoryState{" + - "cursorPosition=" + cursorPosition + - ", text='" + text + '\'' + - '}'; - } - - @Override - protected EditorHistoryState clone() { - try { - return (EditorHistoryState)super.clone(); - } catch (CloneNotSupportedException e) { - throw new UnsupportedOperationException(e); - } - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + */ + +package org.solovyev.android.calculator.history; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.simpleframework.xml.Element; +import org.simpleframework.xml.Root; +import org.solovyev.android.calculator.CalculatorDisplayViewState; +import org.solovyev.android.calculator.Editor; + +@Root +public class EditorHistoryState implements Cloneable{ + + @Element + private int cursorPosition; + + @Element(required = false) + @Nullable + private String text; + + private EditorHistoryState() { + // for xml + } + + @NotNull + public static EditorHistoryState newInstance(@NotNull Editor editor) { + final EditorHistoryState result = new EditorHistoryState(); + + result.text = String.valueOf(editor.getText()); + result.cursorPosition = editor.getSelection(); + + return result; + } + + @NotNull + public static EditorHistoryState newInstance(@NotNull CalculatorDisplayViewState viewState) { + final EditorHistoryState result = new EditorHistoryState(); + + result.text = viewState.getText(); + result.cursorPosition = viewState.getSelection(); + + return result; + } + + public void setValuesFromHistory(@NotNull Editor editor) { + editor.setText(this.getText()); + editor.setSelection(this.getCursorPosition()); + } + + @Nullable + public String getText() { + return text; + } + + public int getCursorPosition() { + return cursorPosition; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof EditorHistoryState)) return false; + + EditorHistoryState that = (EditorHistoryState) o; + + if (cursorPosition != that.cursorPosition) return false; + if (text != null ? !text.equals(that.text) : that.text != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = cursorPosition; + result = 31 * result + (text != null ? text.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "EditorHistoryState{" + + "cursorPosition=" + cursorPosition + + ", text='" + text + '\'' + + '}'; + } + + @Override + protected EditorHistoryState clone() { + try { + return (EditorHistoryState)super.clone(); + } catch (CloneNotSupportedException e) { + throw new UnsupportedOperationException(e); + } + } +} diff --git a/calculatorpp-core/src/main/resources/org/solovyev/android/calculator/messages.properties b/calculatorpp-core/src/main/resources/org/solovyev/android/calculator/messages.properties new file mode 100644 index 00000000..e6534912 --- /dev/null +++ b/calculatorpp-core/src/main/resources/org/solovyev/android/calculator/messages.properties @@ -0,0 +1,8 @@ +msg_1=Arithmetic error occurred: {0} +msg_2=Too complex expression +msg_3=Too long execution time - check the expression +msg_4=Evaluation was cancelled +msg_5=No parameters are specified for function: {0} +msg_6=Infinite loop is detected in expression + +syntax_error=Error \ No newline at end of file diff --git a/calculatorpp/res/layout/calc_display.xml b/calculatorpp/res/layout/calc_display.xml index c4e6a164..c44067dd 100644 --- a/calculatorpp/res/layout/calc_display.xml +++ b/calculatorpp/res/layout/calc_display.xml @@ -6,7 +6,7 @@ ~ or visit http://se.solovyev.org --> - { - convert_to_bin(NumeralBase.bin), - convert_to_dec(NumeralBase.dec), - convert_to_hex(NumeralBase.hex); - - @NotNull - private final NumeralBase toNumeralBase; - - private ConversionMenuItem(@NotNull NumeralBase toNumeralBase) { - this.toNumeralBase = toNumeralBase; - } - - protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { - boolean result = false; - - if (operation == JsclOperation.numeric) { - if (generic.getConstants().isEmpty()) { - try { - convert(generic); - - // conversion possible => return true - result = true; - - } catch (UnitConverterViewBuilder.ConversionException e) { - // conversion is not possible => return false - } - } - } - - return result; - } - - @Override - public void onClick(@NotNull CalculatorDisplay data, @NotNull Context context) { - final NumeralBase fromNumeralBase = CalculatorEngine.instance.getEngine().getNumeralBase(); - - String to; - try { - to = convert(data.getGenericResult()); - - // add prefix - if (fromNumeralBase != toNumeralBase) { - to = toNumeralBase.getJsclPrefix() + to; - } - } catch (UnitConverterViewBuilder.ConversionException e) { - to = context.getString(R.string.c_error); - } - - data.setText(to); - data.redraw(); - } - - @NotNull - private String convert(@NotNull Generic generic) throws UnitConverterViewBuilder.ConversionException { - final NumeralBase fromNumeralBase = CalculatorEngine.instance.getEngine().getNumeralBase(); - - if (fromNumeralBase != toNumeralBase) { - String from = generic.toString(); - if (!StringUtils.isEmpty(from)) { - try { - from = ToJsclTextProcessor.getInstance().process(from).getExpression(); - } catch (CalculatorParseException e) { - // ok, problems while processing occurred - } - } - - return UnitConverterViewBuilder.doConversion(AndroidNumeralBase.getConverter(), from, AndroidNumeralBase.valueOf(fromNumeralBase), AndroidNumeralBase.valueOf(toNumeralBase)); - } else { - return generic.toString(); - } - } - } - - public static enum MenuItem implements LabeledMenuItem { - - copy(R.string.c_copy) { - @Override - public void onClick(@NotNull CalculatorDisplay data, @NotNull Context context) { - CalculatorModel.copyResult(context, data); - } - }, - - convert_to_bin(R.string.convert_to_bin) { - @Override - public void onClick(@NotNull CalculatorDisplay data, @NotNull Context context) { - ConversionMenuItem.convert_to_bin.onClick(data, context); - } - - @Override - protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { - return ConversionMenuItem.convert_to_bin.isItemVisibleFor(generic, operation); - } - }, - - convert_to_dec(R.string.convert_to_dec) { - @Override - public void onClick(@NotNull CalculatorDisplay data, @NotNull Context context) { - ConversionMenuItem.convert_to_dec.onClick(data, context); - } - - @Override - protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { - return ConversionMenuItem.convert_to_dec.isItemVisibleFor(generic, operation); - } - }, - - convert_to_hex(R.string.convert_to_hex) { - @Override - public void onClick(@NotNull CalculatorDisplay data, @NotNull Context context) { - ConversionMenuItem.convert_to_hex.onClick(data, context); - } - - @Override - protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { - return ConversionMenuItem.convert_to_hex.isItemVisibleFor(generic, operation); - } - }, - - convert(R.string.c_convert) { - @Override - public void onClick(@NotNull CalculatorDisplay data, @NotNull Context context) { - new NumeralBaseConverterDialog(data.getGenericResult().toString()).show(context); - } - - @Override - protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { - return operation == JsclOperation.numeric && generic.getConstants().isEmpty(); - } - }, - - plot(R.string.c_plot) { - @Override - public void onClick(@NotNull CalculatorDisplay data, @NotNull Context context) { - final Generic generic = data.getGenericResult(); - assert generic != null; - - final Constant constant = CollectionsUtils.getFirstCollectionElement(getNotSystemConstants(generic)); - assert constant != null; - CalculatorActivityLauncher.plotGraph(context, generic, constant); - } - - @Override - protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { - boolean result = false; - - if (operation == JsclOperation.simplify) { - if (getNotSystemConstants(generic).size() == 1) { - result = true; - } - } - - return result; - } - - @NotNull - private Set getNotSystemConstants(@NotNull Generic generic) { - final Set notSystemConstants = new HashSet(); - - for (Constant constant : generic.getConstants()) { - IConstant var = CalculatorEngine.instance.getVarsRegistry().get(constant.getName()); - if (var != null && !var.isSystem() && !var.isDefined()) { - notSystemConstants.add(constant); - } - } - - return notSystemConstants; - } - }; - - private final int captionId; - - MenuItem(int captionId) { - this.captionId = captionId; - } - - public final boolean isItemVisible(@NotNull CalculatorDisplay display) { - //noinspection ConstantConditions - return display.isValid() && display.getGenericResult() != null && isItemVisibleFor(display.getGenericResult(), display.getJsclOperation()); - } - - protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { - return true; - } - - @NotNull - @Override - public String getCaption(@NotNull Context context) { - return context.getString(captionId); - } - } - - private boolean valid = true; - - @Nullable - private String errorMessage; - - @NotNull - private JsclOperation jsclOperation = JsclOperation.numeric; - - @NotNull - private final static TextProcessor textHighlighter = new TextHighlighter(Color.WHITE, false, CalculatorEngine.instance.getEngine()); - - @Nullable - private Generic genericResult; - - public CalculatorDisplay(Context context) { - super(context); - } - - public CalculatorDisplay(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public CalculatorDisplay(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - public boolean isValid() { - return valid; - } - - @Override - public void setValid(boolean valid) { - this.valid = valid; - if (valid) { - errorMessage = null; - setTextColor(getResources().getColor(R.color.default_text_color)); - } else { - setTextColor(getResources().getColor(R.color.display_error_text_color)); - } - } - - @Override - @Nullable - public String getErrorMessage() { - return errorMessage; - } - - @Override - public void setErrorMessage(@Nullable String errorMessage) { - this.errorMessage = errorMessage; - } - - @Override - public void setJsclOperation(@NotNull JsclOperation jsclOperation) { - this.jsclOperation = jsclOperation; - } - - @Override - @NotNull - public JsclOperation getJsclOperation() { - return jsclOperation; - } - - @Override - public void setText(CharSequence text, BufferType type) { - super.setText(text, type); - - setValid(true); - } - - public synchronized void redraw() { - if (isValid()) { - String text = getText().toString(); - - Log.d(this.getClass().getName(), text); - - try { - TextHighlighter.Result result = textHighlighter.process(text); - text = result.toString(); - } catch (CalculatorParseException e) { - Log.e(this.getClass().getName(), e.getMessage(), e); - } - - Log.d(this.getClass().getName(), text); - super.setText(Html.fromHtml(text), BufferType.EDITABLE); - } - - // todo serso: think where to move it (keep in mind org.solovyev.android.view.AutoResizeTextView.resetTextSize()) - setAddEllipsis(false); - setMinTextSize(10); - resizeText(); - } - - @Override - public void setGenericResult(@Nullable Generic genericResult) { - this.genericResult = genericResult; - } - - @Override - @Nullable - public Generic getGenericResult() { - return genericResult; - } - - @Override - public int getSelection() { - return this.getSelectionStart(); - } - - @Override - public void setSelection(int selection) { - // not supported by TextView - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + */ + +package org.solovyev.android.calculator; + +import android.content.Context; +import android.graphics.Color; +import android.text.Html; +import android.util.AttributeSet; +import android.util.Log; +import jscl.NumeralBase; +import jscl.math.Generic; +import jscl.math.function.Constant; +import jscl.math.function.IConstant; +import org.jetbrains.annotations.NotNull; +import org.solovyev.android.calculator.jscl.JsclOperation; +import org.solovyev.android.calculator.model.CalculatorEngine; +import org.solovyev.android.calculator.text.TextProcessor; +import org.solovyev.android.calculator.view.NumeralBaseConverterDialog; +import org.solovyev.android.calculator.view.TextHighlighter; +import org.solovyev.android.calculator.view.UnitConverterViewBuilder; +import org.solovyev.android.menu.AMenuItem; +import org.solovyev.android.menu.LabeledMenuItem; +import org.solovyev.android.view.AutoResizeTextView; +import org.solovyev.common.collections.CollectionsUtils; +import org.solovyev.common.text.StringUtils; + +import java.util.HashSet; +import java.util.Set; + +/** + * User: serso + * Date: 9/17/11 + * Time: 10:58 PM + */ +public class AndroidCalculatorDisplayView extends AutoResizeTextView implements CalculatorDisplayView { + + private static enum ConversionMenuItem implements AMenuItem { + convert_to_bin(NumeralBase.bin), + convert_to_dec(NumeralBase.dec), + convert_to_hex(NumeralBase.hex); + + @NotNull + private final NumeralBase toNumeralBase; + + private ConversionMenuItem(@NotNull NumeralBase toNumeralBase) { + this.toNumeralBase = toNumeralBase; + } + + protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { + boolean result = false; + + if (operation == JsclOperation.numeric) { + if (generic.getConstants().isEmpty()) { + try { + convert(generic); + + // conversion possible => return true + result = true; + + } catch (UnitConverterViewBuilder.ConversionException e) { + // conversion is not possible => return false + } + } + } + + return result; + } + + @Override + public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) { + final NumeralBase fromNumeralBase = CalculatorEngine.instance.getEngine().getNumeralBase(); + + final Generic lastResult = CalculatorLocatorImpl.getInstance().getCalculatorDisplay().getViewState().getResult(); + + if (lastResult != null) { + String to; + try { + to = convert(lastResult); + + // add prefix + if (fromNumeralBase != toNumeralBase) { + to = toNumeralBase.getJsclPrefix() + to; + } + } catch (UnitConverterViewBuilder.ConversionException e) { + to = context.getString(R.string.c_error); + } + + data.setText(to); + //data.redraw(); + } + } + + @NotNull + private String convert(@NotNull Generic generic) throws UnitConverterViewBuilder.ConversionException { + final NumeralBase fromNumeralBase = CalculatorEngine.instance.getEngine().getNumeralBase(); + + if (fromNumeralBase != toNumeralBase) { + String from = generic.toString(); + if (!StringUtils.isEmpty(from)) { + try { + from = ToJsclTextProcessor.getInstance().process(from).getExpression(); + } catch (CalculatorParseException e) { + // ok, problems while processing occurred + } + } + + return UnitConverterViewBuilder.doConversion(AndroidNumeralBase.getConverter(), from, AndroidNumeralBase.valueOf(fromNumeralBase), AndroidNumeralBase.valueOf(toNumeralBase)); + } else { + return generic.toString(); + } + } + } + + public static enum MenuItem implements LabeledMenuItem { + + copy(R.string.c_copy) { + @Override + public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) { + CalculatorModel.copyResult(context, data); + } + }, + + convert_to_bin(R.string.convert_to_bin) { + @Override + public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) { + ConversionMenuItem.convert_to_bin.onClick(data, context); + } + + @Override + protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { + return ConversionMenuItem.convert_to_bin.isItemVisibleFor(generic, operation); + } + }, + + convert_to_dec(R.string.convert_to_dec) { + @Override + public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) { + ConversionMenuItem.convert_to_dec.onClick(data, context); + } + + @Override + protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { + return ConversionMenuItem.convert_to_dec.isItemVisibleFor(generic, operation); + } + }, + + convert_to_hex(R.string.convert_to_hex) { + @Override + public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) { + ConversionMenuItem.convert_to_hex.onClick(data, context); + } + + @Override + protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { + return ConversionMenuItem.convert_to_hex.isItemVisibleFor(generic, operation); + } + }, + + convert(R.string.c_convert) { + @Override + public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) { + final Generic result = data.getState().getResult(); + if (result != null) { + new NumeralBaseConverterDialog(result.toString()).show(context); + } + } + + @Override + protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { + return operation == JsclOperation.numeric && generic.getConstants().isEmpty(); + } + }, + + plot(R.string.c_plot) { + @Override + public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) { + final Generic generic = data.getState().getResult(); + assert generic != null; + + final Constant constant = CollectionsUtils.getFirstCollectionElement(getNotSystemConstants(generic)); + assert constant != null; + CalculatorActivityLauncher.plotGraph(context, generic, constant); + } + + @Override + protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { + boolean result = false; + + if (operation == JsclOperation.simplify) { + if (getNotSystemConstants(generic).size() == 1) { + result = true; + } + } + + return result; + } + + @NotNull + private Set getNotSystemConstants(@NotNull Generic generic) { + final Set notSystemConstants = new HashSet(); + + for (Constant constant : generic.getConstants()) { + IConstant var = CalculatorEngine.instance.getVarsRegistry().get(constant.getName()); + if (var != null && !var.isSystem() && !var.isDefined()) { + notSystemConstants.add(constant); + } + } + + return notSystemConstants; + } + }; + + private final int captionId; + + MenuItem(int captionId) { + this.captionId = captionId; + } + + public final boolean isItemVisible(@NotNull CalculatorDisplayViewState displayViewState) { + //noinspection ConstantConditions + return displayViewState.isValid() && displayViewState.getResult() != null && isItemVisibleFor(displayViewState.getResult(), displayViewState.getOperation()); + } + + protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { + return true; + } + + @NotNull + @Override + public String getCaption(@NotNull Context context) { + return context.getString(captionId); + } + } + + @NotNull + private CalculatorDisplayViewState state = CalculatorDisplayViewStateImpl.newDefaultInstance(); + + @NotNull + private final static TextProcessor textHighlighter = new TextHighlighter(Color.WHITE, false, CalculatorEngine.instance.getEngine()); + + public AndroidCalculatorDisplayView(Context context) { + super(context); + } + + public AndroidCalculatorDisplayView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public AndroidCalculatorDisplayView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public boolean isValid() { + return this.state.isValid(); + } + + + @Override + public void setState(@NotNull CalculatorDisplayViewState state) { + this.state = state; + if ( state.isValid() ) { + setTextColor(getResources().getColor(R.color.default_text_color)); + setText(state.getStringResult()); + } else { + setTextColor(getResources().getColor(R.color.display_error_text_color)); + setText(state.getErrorMessage()); + } + } + + @NotNull + @Override + public CalculatorDisplayViewState getState() { + return this.state; + } + + @Override + public void setText(CharSequence text, BufferType type) { + super.setText(text, type); + } + + public synchronized void redraw() { + if (isValid()) { + String text = getText().toString(); + + Log.d(this.getClass().getName(), text); + + try { + TextHighlighter.Result result = textHighlighter.process(text); + text = result.toString(); + } catch (CalculatorParseException e) { + Log.e(this.getClass().getName(), e.getMessage(), e); + } + + Log.d(this.getClass().getName(), text); + super.setText(Html.fromHtml(text), BufferType.EDITABLE); + } + + // todo serso: think where to move it (keep in mind org.solovyev.android.view.AutoResizeTextView.resetTextSize()) + setAddEllipsis(false); + setMinTextSize(10); + resizeText(); + } + + @Override + public int getSelection() { + return this.getSelectionStart(); + } + + @Override + public void setSelection(int selection) { + // not supported by TextView + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorModel.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorModel.java index 2329cc0a..d3268689 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorModel.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorModel.java @@ -1,409 +1,412 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - */ - -package org.solovyev.android.calculator; - -import android.app.Activity; -import android.app.AlertDialog; -import android.content.Context; -import android.content.SharedPreferences; -import android.os.Handler; -import android.text.ClipboardManager; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.EditText; -import android.widget.TextView; -import android.widget.Toast; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.solovyev.android.CursorControl; -import org.solovyev.android.calculator.history.AndroidCalculatorHistoryImpl; -import org.solovyev.android.calculator.history.CalculatorHistoryState; -import org.solovyev.android.calculator.history.TextViewEditorAdapter; -import org.solovyev.android.calculator.jscl.JsclOperation; -import org.solovyev.android.calculator.math.MathType; -import org.solovyev.android.calculator.model.CalculatorEngine; -import org.solovyev.android.history.HistoryControl; -import org.solovyev.android.menu.AMenuBuilder; -import org.solovyev.android.menu.MenuImpl; -import org.solovyev.common.MutableObject; -import org.solovyev.common.history.HistoryAction; -import org.solovyev.common.msg.Message; -import org.solovyev.common.text.StringUtils; - -import java.util.ArrayList; -import java.util.List; - -/** - * User: serso - * Date: 9/12/11 - * Time: 11:15 PM - */ -public enum CalculatorModel implements CursorControl, HistoryControl, CalculatorEngineControl { - - instance; - - // millis to wait before evaluation after user edit action - public static final int EVAL_DELAY_MILLIS = 0; - - @NotNull - private CalculatorEditor editor; - - @NotNull - private CalculatorDisplay display; - - @NotNull - private CalculatorEngine calculatorEngine; - - public CalculatorModel init(@NotNull final Activity activity, @NotNull SharedPreferences preferences, @NotNull CalculatorEngine calculator) { - Log.d(this.getClass().getName(), "CalculatorModel initialization with activity: " + activity); - this.calculatorEngine = calculator; - - this.editor = (CalculatorEditor) activity.findViewById(R.id.calculatorEditor); - this.editor.init(preferences); - preferences.registerOnSharedPreferenceChangeListener(editor); - - this.display = (CalculatorDisplay) activity.findViewById(R.id.calculatorDisplay); - this.display.setOnClickListener(new CalculatorDisplayOnClickListener(activity)); - - final CalculatorHistoryState lastState = AndroidCalculatorHistoryImpl.instance.getLastHistoryState(); - if (lastState == null) { - saveHistoryState(); - } else { - setCurrentHistoryState(lastState); - } - - - return this; - } - - private static void showEvaluationError(@NotNull Activity activity, @NotNull final String errorMessage) { - final LayoutInflater layoutInflater = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE); - - final View errorMessageView = layoutInflater.inflate(R.layout.display_error_message, null); - ((TextView) errorMessageView.findViewById(R.id.error_message_text_view)).setText(errorMessage); - - final AlertDialog.Builder builder = new AlertDialog.Builder(activity) - .setPositiveButton(R.string.c_cancel, null) - .setView(errorMessageView); - - builder.create().show(); - } - - public void copyResult(@NotNull Context context) { - copyResult(context, display); - } - - public static void copyResult(@NotNull Context context, @NotNull final CalculatorDisplay display) { - if (display.isValid()) { - final CharSequence text = display.getText(); - if (!StringUtils.isEmpty(text)) { - final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE); - clipboard.setText(text.toString()); - Toast.makeText(context, context.getText(R.string.c_result_copied), Toast.LENGTH_SHORT).show(); - } - } - } - - private void saveHistoryState() { - AndroidCalculatorHistoryImpl.instance.addState(getCurrentHistoryState()); - } - - public void setCursorOnStart() { - editor.setSelection(0); - } - - public void setCursorOnEnd() { - editor.setSelection(editor.getText().length()); - } - - public void moveCursorLeft() { - if (editor.getSelectionStart() > 0) { - editor.setSelection(editor.getSelectionStart() - 1); - } - } - - public void moveCursorRight() { - if (editor.getSelectionStart() < editor.getText().length()) { - editor.setSelection(editor.getSelectionStart() + 1); - } - } - - public void doTextOperation(@NotNull TextOperation operation) { - doTextOperation(operation, true); - } - - public void doTextOperation(@NotNull TextOperation operation, boolean delayEvaluate) { - doTextOperation(operation, delayEvaluate, JsclOperation.numeric, false); - } - - public void doTextOperation(@NotNull TextOperation operation, boolean delayEvaluate, @NotNull JsclOperation jsclOperation, boolean forceEval) { - final String editorStateBefore = this.editor.getText().toString(); - - Log.d(CalculatorModel.class.getName(), "Editor state changed before '" + editorStateBefore + "'"); - operation.doOperation(this.editor); - //Log.d(CalculatorModel.class.getName(), "Doing text operation" + StringUtils.fromStackTrace(Thread.currentThread().getStackTrace())); - - final String editorStateAfter = this.editor.getText().toString(); - if (forceEval ||!editorStateBefore.equals(editorStateAfter)) { - - editor.redraw(); - - evaluate(delayEvaluate, editorStateAfter, jsclOperation, null); - } - } - - @NotNull - private final static MutableObject pendingOperation = new MutableObject(); - - private void evaluate(boolean delayEvaluate, - @NotNull final String expression, - @NotNull final JsclOperation operation, - @Nullable CalculatorHistoryState historyState) { - - final CalculatorHistoryState localHistoryState; - if (historyState == null) { - //this.display.setText(""); - localHistoryState = getCurrentHistoryState(); - } else { - this.display.setText(historyState.getDisplayState().getEditorState().getText()); - localHistoryState = historyState; - } - - pendingOperation.setObject(new Runnable() { - @Override - public void run() { - // allow only one runner at one time - synchronized (pendingOperation) { - //lock all operations with history - if (pendingOperation.getObject() == this) { - // actually nothing shall be logged while text operations are done - evaluate(expression, operation, this); - - if (pendingOperation.getObject() == this) { - // todo serso: of course there is small probability that someone will set pendingOperation after if statement but before .setObject(null) - pendingOperation.setObject(null); - localHistoryState.setDisplayState(getCurrentHistoryState().getDisplayState()); - } - } - } - } - }); - - if (delayEvaluate) { - if (historyState == null) { - AndroidCalculatorHistoryImpl.instance.addState(localHistoryState); - } - // todo serso: this is not correct - operation is processing still in the same thread - new Handler().postDelayed(pendingOperation.getObject(), EVAL_DELAY_MILLIS); - } else { - pendingOperation.getObject().run(); - if (historyState == null) { - AndroidCalculatorHistoryImpl.instance.addState(localHistoryState); - } - } - } - - @Override - public void evaluate() { - evaluate(false, this.editor.getText().toString(), JsclOperation.numeric, null); - } - - public void evaluate(@NotNull JsclOperation operation) { - evaluate(false, this.editor.getText().toString(), operation, null); - } - - @Override - public void simplify() { - evaluate(false, this.editor.getText().toString(), JsclOperation.simplify, null); - } - - private void evaluate(@Nullable final String expression, - @NotNull JsclOperation operation, - @NotNull Runnable currentRunner) { - - if (!StringUtils.isEmpty(expression)) { - try { - Log.d(CalculatorModel.class.getName(), "Trying to evaluate '" + operation + "': " + expression /*+ StringUtils.fromStackTrace(Thread.currentThread().getStackTrace())*/); - final CalculatorEngine.Result result = calculatorEngine.evaluate(operation, expression); - - // todo serso: second condition might replaced with expression.equals(this.editor.getText().toString()) ONLY if expression will be formatted with text highlighter - if (currentRunner == pendingOperation.getObject() && this.editor.getText().length() > 0) { - display.setText(result.getResult()); - } else { - display.setText(""); - } - display.setJsclOperation(result.getUserOperation()); - display.setGenericResult(result.getGenericResult()); - } catch (CalculatorParseException e) { - handleEvaluationException(expression, display, operation, e); - } catch (CalculatorEvalException e) { - handleEvaluationException(expression, display, operation, e); - } - } else { - this.display.setText(""); - this.display.setJsclOperation(operation); - this.display.setGenericResult(null); - } - - - - this.display.redraw(); - } - - private void handleEvaluationException(@NotNull String expression, - @NotNull CalculatorDisplay localDisplay, - @NotNull JsclOperation operation, - @NotNull Message e) { - Log.d(CalculatorModel.class.getName(), "Evaluation failed for : " + expression + ". Error message: " + e); - if ( StringUtils.isEmpty(localDisplay.getText()) ) { - // if previous display state was empty -> show error - localDisplay.setText(R.string.c_syntax_error); - } else { - // show previous result instead of error caption (actually previous result will be greyed) - } - localDisplay.setJsclOperation(operation); - localDisplay.setGenericResult(null); - localDisplay.setValid(false); - localDisplay.setErrorMessage(e.getLocalizedMessage()); - } - - public void clear() { - if (!StringUtils.isEmpty(editor.getText()) || !StringUtils.isEmpty(display.getText())) { - editor.getText().clear(); - display.setText(""); - saveHistoryState(); - } - } - - public void processDigitButtonAction(@Nullable final String text) { - processDigitButtonAction(text, true); - } - - public void processDigitButtonAction(@Nullable final String text, boolean delayEvaluate) { - - if (!StringUtils.isEmpty(text)) { - doTextOperation(new CalculatorModel.TextOperation() { - - @Override - public void doOperation(@NotNull EditText editor) { - int cursorPositionOffset = 0; - final StringBuilder textToBeInserted = new StringBuilder(text); - - final MathType.Result mathType = MathType.getType(text, 0, false); - switch (mathType.getMathType()) { - case function: - textToBeInserted.append("()"); - cursorPositionOffset = -1; - break; - case operator: - textToBeInserted.append("()"); - cursorPositionOffset = -1; - break; - case comma: - textToBeInserted.append(" "); - break; - } - - if (cursorPositionOffset == 0) { - if (MathType.openGroupSymbols.contains(text)) { - cursorPositionOffset = -1; - } - } - - editor.getText().insert(editor.getSelectionStart(), textToBeInserted.toString()); - editor.setSelection(editor.getSelectionStart() + cursorPositionOffset, editor.getSelectionEnd() + cursorPositionOffset); - } - }, delayEvaluate); - } - } - - public static interface TextOperation { - - void doOperation(@NotNull EditText editor); - - } - - @Override - public void doHistoryAction(@NotNull HistoryAction historyAction) { - synchronized (AndroidCalculatorHistoryImpl.instance) { - if (AndroidCalculatorHistoryImpl.instance.isActionAvailable(historyAction)) { - final CalculatorHistoryState newState = AndroidCalculatorHistoryImpl.instance.doAction(historyAction, getCurrentHistoryState()); - if (newState != null) { - setCurrentHistoryState(newState); - } - } - } - } - - @Override - public void setCurrentHistoryState(@NotNull CalculatorHistoryState editorHistoryState) { - synchronized (AndroidCalculatorHistoryImpl.instance) { - Log.d(this.getClass().getName(), "Saved history found: " + editorHistoryState); - - editorHistoryState.setValuesFromHistory(new TextViewEditorAdapter(this.editor), this.display); - - final String expression = this.editor.getText().toString(); - if ( !StringUtils.isEmpty(expression) ) { - if ( StringUtils.isEmpty(this.display.getText().toString()) ) { - evaluate(false, expression, this.display.getJsclOperation(), editorHistoryState); - } - } - - editor.redraw(); - display.redraw(); - } - } - - @Override - @NotNull - public CalculatorHistoryState getCurrentHistoryState() { - synchronized (AndroidCalculatorHistoryImpl.instance) { - return CalculatorHistoryState.newInstance(new TextViewEditorAdapter(this.editor), this.display); - } - } - - @NotNull - public CalculatorDisplay getDisplay() { - return display; - } - - private static class CalculatorDisplayOnClickListener implements View.OnClickListener { - - @NotNull - private final Activity activity; - - public CalculatorDisplayOnClickListener(@NotNull Activity activity) { - this.activity = activity; - } - - @Override - public void onClick(View v) { - if (v instanceof CalculatorDisplay) { - final CalculatorDisplay cd = (CalculatorDisplay) v; - - if (cd.isValid()) { - final List filteredMenuItems = new ArrayList(CalculatorDisplay.MenuItem.values().length); - for (CalculatorDisplay.MenuItem menuItem : CalculatorDisplay.MenuItem.values()) { - if (menuItem.isItemVisible(cd)) { - filteredMenuItems.add(menuItem); - } - } - - if (!filteredMenuItems.isEmpty()) { - AMenuBuilder.newInstance(activity, MenuImpl.newInstance(filteredMenuItems)).create(cd).show(); - } - - } else { - final String errorMessage = cd.getErrorMessage(); - if (errorMessage != null) { - showEvaluationError(activity, errorMessage); - } - } - } - } - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + */ + +package org.solovyev.android.calculator; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Handler; +import android.text.ClipboardManager; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.CursorControl; +import org.solovyev.android.calculator.history.AndroidCalculatorHistoryImpl; +import org.solovyev.android.calculator.history.CalculatorHistoryState; +import org.solovyev.android.calculator.history.TextViewEditorAdapter; +import org.solovyev.android.calculator.jscl.JsclOperation; +import org.solovyev.android.calculator.math.MathType; +import org.solovyev.android.calculator.model.CalculatorEngine; +import org.solovyev.android.history.HistoryControl; +import org.solovyev.android.menu.AMenuBuilder; +import org.solovyev.android.menu.MenuImpl; +import org.solovyev.common.MutableObject; +import org.solovyev.common.history.HistoryAction; +import org.solovyev.common.msg.Message; +import org.solovyev.common.text.StringUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * User: serso + * Date: 9/12/11 + * Time: 11:15 PM + */ +public enum CalculatorModel implements CursorControl, HistoryControl, CalculatorEngineControl { + + instance; + + // millis to wait before evaluation after user edit action + public static final int EVAL_DELAY_MILLIS = 0; + + @NotNull + private CalculatorEditor editor; + + @NotNull + private AndroidCalculatorDisplayView display; + + @NotNull + private CalculatorEngine calculatorEngine; + + public CalculatorModel init(@NotNull final Activity activity, @NotNull SharedPreferences preferences, @NotNull CalculatorEngine calculator) { + Log.d(this.getClass().getName(), "CalculatorModel initialization with activity: " + activity); + this.calculatorEngine = calculator; + + this.editor = (CalculatorEditor) activity.findViewById(R.id.calculatorEditor); + this.editor.init(preferences); + preferences.registerOnSharedPreferenceChangeListener(editor); + + this.display = (AndroidCalculatorDisplayView) activity.findViewById(R.id.calculatorDisplay); + this.display.setOnClickListener(new CalculatorDisplayOnClickListener(activity)); + + final CalculatorHistoryState lastState = AndroidCalculatorHistoryImpl.instance.getLastHistoryState(); + if (lastState == null) { + saveHistoryState(); + } else { + setCurrentHistoryState(lastState); + } + + + return this; + } + + private static void showEvaluationError(@NotNull Activity activity, @NotNull final String errorMessage) { + final LayoutInflater layoutInflater = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE); + + final View errorMessageView = layoutInflater.inflate(R.layout.display_error_message, null); + ((TextView) errorMessageView.findViewById(R.id.error_message_text_view)).setText(errorMessage); + + final AlertDialog.Builder builder = new AlertDialog.Builder(activity) + .setPositiveButton(R.string.c_cancel, null) + .setView(errorMessageView); + + builder.create().show(); + } + + public void copyResult(@NotNull Context context) { + copyResult(context, display); + } + + public static void copyResult(@NotNull Context context, @NotNull final CalculatorDisplayView display) { + final CalculatorDisplayViewState displayViewState = display.getState(); + if (displayViewState.isValid()) { + final CharSequence text = display.getText(); + if (!StringUtils.isEmpty(text)) { + final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE); + clipboard.setText(text.toString()); + Toast.makeText(context, context.getText(R.string.c_result_copied), Toast.LENGTH_SHORT).show(); + } + } + } + + private void saveHistoryState() { + AndroidCalculatorHistoryImpl.instance.addState(getCurrentHistoryState()); + } + + public void setCursorOnStart() { + editor.setSelection(0); + } + + public void setCursorOnEnd() { + editor.setSelection(editor.getText().length()); + } + + public void moveCursorLeft() { + if (editor.getSelectionStart() > 0) { + editor.setSelection(editor.getSelectionStart() - 1); + } + } + + public void moveCursorRight() { + if (editor.getSelectionStart() < editor.getText().length()) { + editor.setSelection(editor.getSelectionStart() + 1); + } + } + + public void doTextOperation(@NotNull TextOperation operation) { + doTextOperation(operation, true); + } + + public void doTextOperation(@NotNull TextOperation operation, boolean delayEvaluate) { + doTextOperation(operation, delayEvaluate, JsclOperation.numeric, false); + } + + public void doTextOperation(@NotNull TextOperation operation, boolean delayEvaluate, @NotNull JsclOperation jsclOperation, boolean forceEval) { + final String editorStateBefore = this.editor.getText().toString(); + + Log.d(CalculatorModel.class.getName(), "Editor state changed before '" + editorStateBefore + "'"); + operation.doOperation(this.editor); + //Log.d(CalculatorModel.class.getName(), "Doing text operation" + StringUtils.fromStackTrace(Thread.currentThread().getStackTrace())); + + final String editorStateAfter = this.editor.getText().toString(); + if (forceEval ||!editorStateBefore.equals(editorStateAfter)) { + + editor.redraw(); + + evaluate(delayEvaluate, editorStateAfter, jsclOperation, null); + } + } + + @NotNull + private final static MutableObject pendingOperation = new MutableObject(); + + private void evaluate(boolean delayEvaluate, + @NotNull final String expression, + @NotNull final JsclOperation operation, + @Nullable CalculatorHistoryState historyState) { + + final CalculatorHistoryState localHistoryState; + if (historyState == null) { + //this.display.setText(""); + localHistoryState = getCurrentHistoryState(); + } else { + this.display.setText(historyState.getDisplayState().getEditorState().getText()); + localHistoryState = historyState; + } + + pendingOperation.setObject(new Runnable() { + @Override + public void run() { + // allow only one runner at one time + synchronized (pendingOperation) { + //lock all operations with history + if (pendingOperation.getObject() == this) { + // actually nothing shall be logged while text operations are done + evaluate(expression, operation, this); + + if (pendingOperation.getObject() == this) { + // todo serso: of course there is small probability that someone will set pendingOperation after if statement but before .setObject(null) + pendingOperation.setObject(null); + localHistoryState.setDisplayState(getCurrentHistoryState().getDisplayState()); + } + } + } + } + }); + + if (delayEvaluate) { + if (historyState == null) { + AndroidCalculatorHistoryImpl.instance.addState(localHistoryState); + } + // todo serso: this is not correct - operation is processing still in the same thread + new Handler().postDelayed(pendingOperation.getObject(), EVAL_DELAY_MILLIS); + } else { + pendingOperation.getObject().run(); + if (historyState == null) { + AndroidCalculatorHistoryImpl.instance.addState(localHistoryState); + } + } + } + + @Override + public void evaluate() { + evaluate(false, this.editor.getText().toString(), JsclOperation.numeric, null); + } + + public void evaluate(@NotNull JsclOperation operation) { + evaluate(false, this.editor.getText().toString(), operation, null); + } + + @Override + public void simplify() { + evaluate(false, this.editor.getText().toString(), JsclOperation.simplify, null); + } + + private void evaluate(@Nullable final String expression, + @NotNull JsclOperation operation, + @NotNull Runnable currentRunner) { + + if (!StringUtils.isEmpty(expression)) { + try { + Log.d(CalculatorModel.class.getName(), "Trying to evaluate '" + operation + "': " + expression /*+ StringUtils.fromStackTrace(Thread.currentThread().getStackTrace())*/); + final CalculatorOutput result = calculatorEngine.evaluate(operation, expression); + + // todo serso: second condition might replaced with expression.equals(this.editor.getText().toString()) ONLY if expression will be formatted with text highlighter + if (currentRunner == pendingOperation.getObject() && this.editor.getText().length() > 0) { + display.setText(result.getStringResult()); + } else { + display.setText(""); + } + display.setJsclOperation(result.getOperation()); + display.setGenericResult(result.getResult()); + } catch (CalculatorParseException e) { + handleEvaluationException(expression, display, operation, e); + } catch (CalculatorEvalException e) { + handleEvaluationException(expression, display, operation, e); + } + } else { + this.display.setText(""); + this.display.setJsclOperation(operation); + this.display.setGenericResult(null); + } + + + + this.display.redraw(); + } + + private void handleEvaluationException(@NotNull String expression, + @NotNull AndroidCalculatorDisplayView localDisplay, + @NotNull JsclOperation operation, + @NotNull Message e) { + Log.d(CalculatorModel.class.getName(), "Evaluation failed for : " + expression + ". Error message: " + e); + if ( StringUtils.isEmpty(localDisplay.getText()) ) { + // if previous display state was empty -> show error + localDisplay.setText(R.string.c_syntax_error); + } else { + // show previous result instead of error caption (actually previous result will be greyed) + } + localDisplay.setJsclOperation(operation); + localDisplay.setGenericResult(null); + localDisplay.setValid(false); + localDisplay.setErrorMessage(e.getLocalizedMessage()); + } + + public void clear() { + if (!StringUtils.isEmpty(editor.getText()) || !StringUtils.isEmpty(display.getText())) { + editor.getText().clear(); + display.setText(""); + saveHistoryState(); + } + } + + public void processDigitButtonAction(@Nullable final String text) { + processDigitButtonAction(text, true); + } + + public void processDigitButtonAction(@Nullable final String text, boolean delayEvaluate) { + + if (!StringUtils.isEmpty(text)) { + doTextOperation(new CalculatorModel.TextOperation() { + + @Override + public void doOperation(@NotNull EditText editor) { + int cursorPositionOffset = 0; + final StringBuilder textToBeInserted = new StringBuilder(text); + + final MathType.Result mathType = MathType.getType(text, 0, false); + switch (mathType.getMathType()) { + case function: + textToBeInserted.append("()"); + cursorPositionOffset = -1; + break; + case operator: + textToBeInserted.append("()"); + cursorPositionOffset = -1; + break; + case comma: + textToBeInserted.append(" "); + break; + } + + if (cursorPositionOffset == 0) { + if (MathType.openGroupSymbols.contains(text)) { + cursorPositionOffset = -1; + } + } + + editor.getText().insert(editor.getSelectionStart(), textToBeInserted.toString()); + editor.setSelection(editor.getSelectionStart() + cursorPositionOffset, editor.getSelectionEnd() + cursorPositionOffset); + } + }, delayEvaluate); + } + } + + public static interface TextOperation { + + void doOperation(@NotNull EditText editor); + + } + + @Override + public void doHistoryAction(@NotNull HistoryAction historyAction) { + synchronized (AndroidCalculatorHistoryImpl.instance) { + if (AndroidCalculatorHistoryImpl.instance.isActionAvailable(historyAction)) { + final CalculatorHistoryState newState = AndroidCalculatorHistoryImpl.instance.doAction(historyAction, getCurrentHistoryState()); + if (newState != null) { + setCurrentHistoryState(newState); + } + } + } + } + + @Override + public void setCurrentHistoryState(@NotNull CalculatorHistoryState editorHistoryState) { + synchronized (AndroidCalculatorHistoryImpl.instance) { + Log.d(this.getClass().getName(), "Saved history found: " + editorHistoryState); + + editorHistoryState.setValuesFromHistory(new TextViewEditorAdapter(this.editor), this.display); + + final String expression = this.editor.getText().toString(); + if ( !StringUtils.isEmpty(expression) ) { + if ( StringUtils.isEmpty(this.display.getText().toString()) ) { + evaluate(false, expression, this.display.getJsclOperation(), editorHistoryState); + } + } + + editor.redraw(); + display.redraw(); + } + } + + @Override + @NotNull + public CalculatorHistoryState getCurrentHistoryState() { + synchronized (AndroidCalculatorHistoryImpl.instance) { + return CalculatorHistoryState.newInstance(new TextViewEditorAdapter(this.editor), this.display); + } + } + + @NotNull + public AndroidCalculatorDisplayView getDisplay() { + return display; + } + + private static class CalculatorDisplayOnClickListener implements View.OnClickListener { + + @NotNull + private final Activity activity; + + public CalculatorDisplayOnClickListener(@NotNull Activity activity) { + this.activity = activity; + } + + @Override + public void onClick(View v) { + if (v instanceof CalculatorDisplayView) { + final CalculatorDisplay cd = CalculatorLocatorImpl.getInstance().getCalculatorDisplay(); + + final CalculatorDisplayViewState displayViewState = cd.getViewState(); + + if (displayViewState.isValid()) { + final List filteredMenuItems = new ArrayList(AndroidCalculatorDisplayView.MenuItem.values().length); + for (AndroidCalculatorDisplayView.MenuItem menuItem : AndroidCalculatorDisplayView.MenuItem.values()) { + if (menuItem.isItemVisible(displayViewState)) { + filteredMenuItems.add(menuItem); + } + } + + if (!filteredMenuItems.isEmpty()) { + AMenuBuilder.newInstance(activity, MenuImpl.newInstance(filteredMenuItems)).create(cd).show(); + } + + } else { + final String errorMessage = displayViewState.getErrorMessage(); + if (errorMessage != null) { + showEvaluationError(activity, errorMessage); + } + } + } + } + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/CalculatorEngine.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/model/CalculatorEngine.java index 00d4da93..4dfa07f0 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/model/CalculatorEngine.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/model/CalculatorEngine.java @@ -1,437 +1,404 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - */ - -package org.solovyev.android.calculator.model; - -import android.content.Context; -import android.content.SharedPreferences; -import jscl.*; -import jscl.math.Generic; -import jscl.math.function.Function; -import jscl.math.function.IConstant; -import jscl.math.operator.Operator; -import jscl.text.ParseInterruptedException; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.solovyev.android.calculator.*; -import org.solovyev.android.calculator.jscl.JsclOperation; -import org.solovyev.android.calculator.text.TextProcessor; -import org.solovyev.android.msg.AndroidMessage; -import org.solovyev.android.prefs.BooleanPreference; -import org.solovyev.android.prefs.Preference; -import org.solovyev.android.prefs.StringPreference; -import org.solovyev.common.MutableObject; -import org.solovyev.common.msg.MessageRegistry; -import org.solovyev.common.msg.MessageType; -import org.solovyev.common.text.EnumMapper; -import org.solovyev.common.text.NumberMapper; -import org.solovyev.common.text.StringUtils; - -import java.text.DecimalFormatSymbols; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -/** - * User: serso - * Date: 9/12/11 - * Time: 11:38 PM - */ - -public enum CalculatorEngine implements JCalculatorEngine { - - instance; - - private static final String GROUPING_SEPARATOR_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_calc_grouping_separator"; - - private static final String MULTIPLICATION_SIGN_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_calc_multiplication_sign"; - private static final String MULTIPLICATION_SIGN_DEFAULT = "×"; - - private static final String MAX_CALCULATION_TIME_P_KEY = "calculation.max_calculation_time"; - private static final String MAX_CALCULATION_TIME_DEFAULT = "5"; - - private static final String SCIENCE_NOTATION_P_KEY = "calculation.output.science_notation"; - private static final boolean SCIENCE_NOTATION_DEFAULT = false; - - private static final String ROUND_RESULT_P_KEY = "org.solovyev.android.calculator.CalculatorModel_round_result"; - private static final boolean ROUND_RESULT_DEFAULT = true; - - private static final String RESULT_PRECISION_P_KEY = "org.solovyev.android.calculator.CalculatorModel_result_precision"; - private static final String RESULT_PRECISION_DEFAULT = "5"; - - private static final String NUMERAL_BASES_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_numeral_bases"; - private static final String NUMERAL_BASES_DEFAULT = "dec"; - - private static final String ANGLE_UNITS_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_angle_units"; - private static final String ANGLE_UNITS_DEFAULT = "deg"; - - public static class Preferences { - public static final Preference groupingSeparator = StringPreference.newInstance(GROUPING_SEPARATOR_P_KEY, JsclMathEngine.GROUPING_SEPARATOR_DEFAULT); - public static final Preference multiplicationSign = StringPreference.newInstance(MULTIPLICATION_SIGN_P_KEY, MULTIPLICATION_SIGN_DEFAULT); - public static final Preference precision = StringPreference.newInstance(RESULT_PRECISION_P_KEY, RESULT_PRECISION_DEFAULT, new NumberMapper(Integer.class)); - public static final Preference roundResult = new BooleanPreference(ROUND_RESULT_P_KEY, ROUND_RESULT_DEFAULT); - public static final Preference numeralBase = StringPreference.newInstance(NUMERAL_BASES_P_KEY, NUMERAL_BASES_DEFAULT, EnumMapper.newInstance(NumeralBase.class)); - public static final Preference angleUnit = StringPreference.newInstance(ANGLE_UNITS_P_KEY, ANGLE_UNITS_DEFAULT, EnumMapper.newInstance(AngleUnit.class)); - public static final Preference scienceNotation = new BooleanPreference(SCIENCE_NOTATION_P_KEY, SCIENCE_NOTATION_DEFAULT); - public static final Preference maxCalculationTime = StringPreference.newInstance(MAX_CALCULATION_TIME_P_KEY, MAX_CALCULATION_TIME_DEFAULT, new NumberMapper(Integer.class)); - - private static final List preferenceKeys = new ArrayList(); - - static { - preferenceKeys.add(groupingSeparator.getKey()); - preferenceKeys.add(multiplicationSign.getKey()); - preferenceKeys.add(precision.getKey()); - preferenceKeys.add(roundResult.getKey()); - preferenceKeys.add(numeralBase.getKey()); - preferenceKeys.add(angleUnit.getKey()); - preferenceKeys.add(scienceNotation.getKey()); - preferenceKeys.add(maxCalculationTime.getKey()); - } - - @NotNull - public static List getPreferenceKeys() { - return Collections.unmodifiableList(preferenceKeys); - } - } - - @NotNull - private final Object lock = new Object(); - - @NotNull - private MathEngine engine = JsclMathEngine.instance; - - @NotNull - public final TextProcessor preprocessor = ToJsclTextProcessor.getInstance(); - - @NotNull - private final AndroidMathRegistry varsRegistry = new AndroidVarsRegistryImpl(engine.getConstantsRegistry()); - - @NotNull - private final AndroidMathRegistry functionsRegistry = new AndroidFunctionsMathRegistry(engine.getFunctionsRegistry()); - - @NotNull - private final AndroidMathRegistry operatorsRegistry = new AndroidOperatorsMathRegistry(engine.getOperatorsRegistry()); - - private final AndroidMathRegistry postfixFunctionsRegistry = new AndroidPostfixFunctionsRegistry(engine.getPostfixFunctionsRegistry()); - - @Nullable - private ThreadKiller threadKiller = new AndroidThreadKiller(); - - // calculation thread timeout in seconds, after timeout thread would be interrupted - private int timeout = Integer.valueOf(MAX_CALCULATION_TIME_DEFAULT); - - @NotNull - private String multiplicationSign = MULTIPLICATION_SIGN_DEFAULT; - - CalculatorEngine() { - this.engine.setRoundResult(true); - this.engine.setUseGroupingSeparator(true); - } - - @Override - @NotNull - public String getMultiplicationSign() { - return multiplicationSign; - } - - public void setMultiplicationSign(@NotNull String multiplicationSign) { - this.multiplicationSign = multiplicationSign; - } - - public static class Result { - - @NotNull - private Generic genericResult; - - @NotNull - private String result; - - @NotNull - private JsclOperation userOperation; - - public Result(@NotNull String result, @NotNull JsclOperation userOperation, @NotNull Generic genericResult) { - this.result = result; - this.userOperation = userOperation; - this.genericResult = genericResult; - } - - @NotNull - public String getResult() { - return result; - } - - @NotNull - public JsclOperation getUserOperation() { - return userOperation; - } - - @NotNull - public Generic getGenericResult() { - return genericResult; - } - } - - public Result evaluate(@NotNull JsclOperation operation, - @NotNull String expression) throws CalculatorParseException, CalculatorEvalException { - return evaluate(operation, expression, null); - } - - public Result evaluate(@NotNull final JsclOperation operation, - @NotNull String expression, - @Nullable MessageRegistry mr) throws CalculatorParseException, CalculatorEvalException { - synchronized (lock) { - final StringBuilder sb = new StringBuilder(); - - final PreparedExpression preparedExpression = preprocessor.process(expression); - sb.append(preparedExpression); - - //Log.d(CalculatorEngine.class.getName(), "Preprocessed expression: " + preparedExpression); - /*if (operation == JsclOperation.numeric && preparedExpression.isExistsUndefinedVar()) { - operation = JsclOperation.simplify; - - if (mr != null) { - final String undefinedVars = CollectionsUtils.formatValue(preparedExpression.getUndefinedVars(), ", ", new Formatter() { - @Override - public String formatValue(@Nullable Var var) throws IllegalArgumentException { - return var != null ? var.getName() : ""; - } - }); - - mr.addMessage(new AndroidMessage(R.string.c_simplify_instead_of_numeric, MessageType.info, undefinedVars)); - } - }*/ - - final String jsclExpression = sb.toString(); - - final MutableObject calculationResult = new MutableObject(null); - final MutableObject parseException = new MutableObject(null); - final MutableObject evalException = new MutableObject(null); - final MutableObject calculationThread = new MutableObject(null); - - final CountDownLatch latch = new CountDownLatch(1); - - new Thread(new Runnable() { - @Override - public void run() { - final Thread thread = Thread.currentThread(); - try { - //Log.d(CalculatorEngine.class.getName(), "Calculation thread started work: " + thread.getName()); - //System.out.println(jsclExpression); - calculationThread.setObject(thread); - final Generic genericResult = operation.evaluateGeneric(jsclExpression); - - // NOTE: toString() method must be called here as ArithmeticOperationException may occur in it (just to avoid later check!) - genericResult.toString(); - - calculationResult.setObject(genericResult); - } catch (AbstractJsclArithmeticException e) { - evalException.setObject(new CalculatorEvalException(e, e, jsclExpression)); - } catch (ArithmeticException e) { - //System.out.println(e.getMessage()); - final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_1, MessageType.error, CalculatorApplication.getInstance(), e.getMessage()); - parseException.setObject(new CalculatorParseException(jsclExpression, androidMessage)); - } catch (StackOverflowError e) { - //System.out.println(StringUtils.fromStackTrace(e.getStackTrace())); - final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_2, MessageType.error, CalculatorApplication.getInstance()); - parseException.setObject(new CalculatorParseException(jsclExpression, androidMessage)); - } catch (jscl.text.ParseException e) { - //System.out.println(e.getMessage()); - parseException.setObject(new CalculatorParseException(e)); - } catch (ParseInterruptedException e) { - //System.out.println(e.getMessage()); - // do nothing - we ourselves interrupt the calculations - } finally { - //Log.d(CalculatorEngine.class.getName(), "Calculation thread ended work: " + thread.getName()); - calculationThread.setObject(null); - latch.countDown(); - } - } - }).start(); - - try { - //Log.d(CalculatorEngine.class.getName(), "Main thread is waiting: " + Thread.currentThread().getName()); - latch.await(timeout, TimeUnit.SECONDS); - //Log.d(CalculatorEngine.class.getName(), "Main thread got up: " + Thread.currentThread().getName()); - - final CalculatorParseException parseExceptionObject = parseException.getObject(); - final CalculatorEvalException evalExceptionObject = evalException.getObject(); - final Object calculationResultLocal = calculationResult.getObject(); - final Thread calculationThreadLocal = calculationThread.getObject(); - - if (calculationThreadLocal != null) { - if (threadKiller != null) { - threadKiller.killThread(calculationThreadLocal); - } - //calculationThreadLocal.stop(); - } - - if (parseExceptionObject != null || evalExceptionObject != null) { - if (operation == JsclOperation.numeric && - (preparedExpression.isExistsUndefinedVar() || (evalExceptionObject != null && evalExceptionObject.getCause() instanceof NumeralBaseException))) { - return evaluate(JsclOperation.simplify, expression, mr); - } - - if (parseExceptionObject != null) { - throw parseExceptionObject; - } else { - throw evalExceptionObject; - } - } - - if (calculationResultLocal == null) { - final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_3, MessageType.error, CalculatorApplication.getInstance()); - throw new CalculatorParseException(jsclExpression, androidMessage); - } - - } catch (InterruptedException e) { - final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_4, MessageType.error, CalculatorApplication.getInstance()); - throw new CalculatorParseException(jsclExpression, androidMessage); - } - - final Generic genericResult = calculationResult.getObject(); - - return new Result(operation.getFromProcessor().process(genericResult), operation, genericResult); - } - } - - public void setPrecision(int precision) { - this.getEngine().setPrecision(precision); - } - - public void setRoundResult(boolean roundResult) { - this.getEngine().setRoundResult(roundResult); - } - - public void init(@Nullable Context context, @Nullable SharedPreferences preferences) { - synchronized (lock) { - reset(context, preferences); - } - } - - public void reset(@Nullable Context context, @Nullable SharedPreferences preferences) { - synchronized (lock) { - softReset(context, preferences); - - varsRegistry.load(context, preferences); - functionsRegistry.load(context, preferences); - operatorsRegistry.load(context, preferences); - postfixFunctionsRegistry.load(context, preferences); - } - } - - public void softReset(@Nullable Context context, @Nullable SharedPreferences preferences) { - synchronized (lock) { - if (preferences != null) { - this.setPrecision(Preferences.precision.getPreference(preferences)); - this.setRoundResult(Preferences.roundResult.getPreference(preferences)); - this.setAngleUnits(getAngleUnitsFromPrefs(preferences)); - this.setNumeralBase(getNumeralBaseFromPrefs(preferences)); - this.setMultiplicationSign(Preferences.multiplicationSign.getPreference(preferences)); - this.setScienceNotation(Preferences.scienceNotation.getPreference(preferences)); - this.setTimeout(Preferences.maxCalculationTime.getPreference(preferences)); - - final String groupingSeparator = Preferences.groupingSeparator.getPreference(preferences); - if (StringUtils.isEmpty(groupingSeparator)) { - this.getEngine().setUseGroupingSeparator(false); - } else { - this.getEngine().setUseGroupingSeparator(true); - this.getEngine().setGroupingSeparator(groupingSeparator.charAt(0)); - } - } - } - } - - - @NotNull - public NumeralBase getNumeralBaseFromPrefs(@NotNull SharedPreferences preferences) { - return Preferences.numeralBase.getPreference(preferences); - } - - @NotNull - public AngleUnit getAngleUnitsFromPrefs(@NotNull SharedPreferences preferences) { - return Preferences.angleUnit.getPreference(preferences); - } - - //for tests only - void setDecimalGroupSymbols(@NotNull DecimalFormatSymbols decimalGroupSymbols) { - synchronized (lock) { - this.getEngine().setDecimalGroupSymbols(decimalGroupSymbols); - } - } - - @Override - @NotNull - public AndroidMathRegistry getVarsRegistry() { - return varsRegistry; - } - - @Override - @NotNull - public AndroidMathRegistry getFunctionsRegistry() { - return functionsRegistry; - } - - @Override - @NotNull - public AndroidMathRegistry getOperatorsRegistry() { - return operatorsRegistry; - } - - @Override - @NotNull - public AndroidMathRegistry getPostfixFunctionsRegistry() { - return postfixFunctionsRegistry; - } - - @Override - @NotNull - public MathEngine getEngine() { - return engine; - } - - // package protected for tests - void setTimeout(int timeout) { - this.timeout = timeout; - } - - public void setAngleUnits(@NotNull AngleUnit angleUnits) { - getEngine().setAngleUnits(angleUnits); - } - - public void setScienceNotation(boolean scienceNotation) { - getEngine().setScienceNotation(scienceNotation); - } - - public void setNumeralBase(@NotNull NumeralBase numeralBase) { - getEngine().setNumeralBase(numeralBase); - } - - // for tests only - void setThreadKiller(@Nullable ThreadKiller threadKiller) { - this.threadKiller = threadKiller; - } - - private static interface ThreadKiller { - void killThread(@NotNull Thread thread); - } - - private static class AndroidThreadKiller implements ThreadKiller { - @Override - public void killThread(@NotNull Thread thread) { - thread.setPriority(Thread.MIN_PRIORITY); - thread.interrupt(); - } - } - - public static class ThreadKillerImpl implements ThreadKiller { - @Override - public void killThread(@NotNull Thread thread) { - thread.setPriority(Thread.MIN_PRIORITY); - thread.stop(); - } - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + */ + +package org.solovyev.android.calculator.model; + +import android.content.Context; +import android.content.SharedPreferences; +import jscl.*; +import jscl.math.Generic; +import jscl.math.function.Function; +import jscl.math.function.IConstant; +import jscl.math.operator.Operator; +import jscl.text.ParseInterruptedException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.calculator.*; +import org.solovyev.android.calculator.jscl.JsclOperation; +import org.solovyev.android.calculator.text.TextProcessor; +import org.solovyev.android.msg.AndroidMessage; +import org.solovyev.android.prefs.BooleanPreference; +import org.solovyev.android.prefs.Preference; +import org.solovyev.android.prefs.StringPreference; +import org.solovyev.common.MutableObject; +import org.solovyev.common.msg.MessageRegistry; +import org.solovyev.common.msg.MessageType; +import org.solovyev.common.text.EnumMapper; +import org.solovyev.common.text.NumberMapper; +import org.solovyev.common.text.StringUtils; + +import java.text.DecimalFormatSymbols; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * User: serso + * Date: 9/12/11 + * Time: 11:38 PM + */ + +public enum CalculatorEngine implements JCalculatorEngine { + + instance; + + private static final String GROUPING_SEPARATOR_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_calc_grouping_separator"; + + private static final String MULTIPLICATION_SIGN_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_calc_multiplication_sign"; + private static final String MULTIPLICATION_SIGN_DEFAULT = "×"; + + private static final String MAX_CALCULATION_TIME_P_KEY = "calculation.max_calculation_time"; + private static final String MAX_CALCULATION_TIME_DEFAULT = "5"; + + private static final String SCIENCE_NOTATION_P_KEY = "calculation.output.science_notation"; + private static final boolean SCIENCE_NOTATION_DEFAULT = false; + + private static final String ROUND_RESULT_P_KEY = "org.solovyev.android.calculator.CalculatorModel_round_result"; + private static final boolean ROUND_RESULT_DEFAULT = true; + + private static final String RESULT_PRECISION_P_KEY = "org.solovyev.android.calculator.CalculatorModel_result_precision"; + private static final String RESULT_PRECISION_DEFAULT = "5"; + + private static final String NUMERAL_BASES_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_numeral_bases"; + private static final String NUMERAL_BASES_DEFAULT = "dec"; + + private static final String ANGLE_UNITS_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_angle_units"; + private static final String ANGLE_UNITS_DEFAULT = "deg"; + + public static class Preferences { + public static final Preference groupingSeparator = StringPreference.newInstance(GROUPING_SEPARATOR_P_KEY, JsclMathEngine.GROUPING_SEPARATOR_DEFAULT); + public static final Preference multiplicationSign = StringPreference.newInstance(MULTIPLICATION_SIGN_P_KEY, MULTIPLICATION_SIGN_DEFAULT); + public static final Preference precision = StringPreference.newInstance(RESULT_PRECISION_P_KEY, RESULT_PRECISION_DEFAULT, new NumberMapper(Integer.class)); + public static final Preference roundResult = new BooleanPreference(ROUND_RESULT_P_KEY, ROUND_RESULT_DEFAULT); + public static final Preference numeralBase = StringPreference.newInstance(NUMERAL_BASES_P_KEY, NUMERAL_BASES_DEFAULT, EnumMapper.newInstance(NumeralBase.class)); + public static final Preference angleUnit = StringPreference.newInstance(ANGLE_UNITS_P_KEY, ANGLE_UNITS_DEFAULT, EnumMapper.newInstance(AngleUnit.class)); + public static final Preference scienceNotation = new BooleanPreference(SCIENCE_NOTATION_P_KEY, SCIENCE_NOTATION_DEFAULT); + public static final Preference maxCalculationTime = StringPreference.newInstance(MAX_CALCULATION_TIME_P_KEY, MAX_CALCULATION_TIME_DEFAULT, new NumberMapper(Integer.class)); + + private static final List preferenceKeys = new ArrayList(); + + static { + preferenceKeys.add(groupingSeparator.getKey()); + preferenceKeys.add(multiplicationSign.getKey()); + preferenceKeys.add(precision.getKey()); + preferenceKeys.add(roundResult.getKey()); + preferenceKeys.add(numeralBase.getKey()); + preferenceKeys.add(angleUnit.getKey()); + preferenceKeys.add(scienceNotation.getKey()); + preferenceKeys.add(maxCalculationTime.getKey()); + } + + @NotNull + public static List getPreferenceKeys() { + return Collections.unmodifiableList(preferenceKeys); + } + } + + @NotNull + private final Object lock = new Object(); + + @NotNull + private MathEngine engine = JsclMathEngine.instance; + + @NotNull + public final TextProcessor preprocessor = ToJsclTextProcessor.getInstance(); + + @NotNull + private final AndroidMathRegistry varsRegistry = new AndroidVarsRegistryImpl(engine.getConstantsRegistry()); + + @NotNull + private final AndroidMathRegistry functionsRegistry = new AndroidFunctionsMathRegistry(engine.getFunctionsRegistry()); + + @NotNull + private final AndroidMathRegistry operatorsRegistry = new AndroidOperatorsMathRegistry(engine.getOperatorsRegistry()); + + private final AndroidMathRegistry postfixFunctionsRegistry = new AndroidPostfixFunctionsRegistry(engine.getPostfixFunctionsRegistry()); + + @Nullable + private ThreadKiller threadKiller = new AndroidThreadKiller(); + + // calculation thread timeout in seconds, after timeout thread would be interrupted + private int timeout = Integer.valueOf(MAX_CALCULATION_TIME_DEFAULT); + + @NotNull + private String multiplicationSign = MULTIPLICATION_SIGN_DEFAULT; + + CalculatorEngine() { + this.engine.setRoundResult(true); + this.engine.setUseGroupingSeparator(true); + } + + @Override + @NotNull + public String getMultiplicationSign() { + return multiplicationSign; + } + + public void setMultiplicationSign(@NotNull String multiplicationSign) { + this.multiplicationSign = multiplicationSign; + } + + public CalculatorOutput evaluate(@NotNull JsclOperation operation, + @NotNull String expression) throws CalculatorParseException, CalculatorEvalException { + return evaluate(operation, expression, null); + } + + public CalculatorOutput evaluate(@NotNull final JsclOperation operation, + @NotNull String expression, + @Nullable MessageRegistry mr) throws CalculatorParseException, CalculatorEvalException { + synchronized (lock) { + final StringBuilder sb = new StringBuilder(); + + final PreparedExpression preparedExpression = preprocessor.process(expression); + sb.append(preparedExpression); + + //Log.d(CalculatorEngine.class.getName(), "Preprocessed expression: " + preparedExpression); + /*if (operation == JsclOperation.numeric && preparedExpression.isExistsUndefinedVar()) { + operation = JsclOperation.simplify; + + if (mr != null) { + final String undefinedVars = CollectionsUtils.formatValue(preparedExpression.getUndefinedVars(), ", ", new Formatter() { + @Override + public String formatValue(@Nullable Var var) throws IllegalArgumentException { + return var != null ? var.getName() : ""; + } + }); + + mr.addMessage(new AndroidMessage(R.string.c_simplify_instead_of_numeric, MessageType.info, undefinedVars)); + } + }*/ + + final String jsclExpression = sb.toString(); + + final MutableObject calculationResult = new MutableObject(null); + final MutableObject parseException = new MutableObject(null); + final MutableObject evalException = new MutableObject(null); + final MutableObject calculationThread = new MutableObject(null); + + final CountDownLatch latch = new CountDownLatch(1); + + new Thread(new Runnable() { + @Override + public void run() { + final Thread thread = Thread.currentThread(); + try { + //Log.d(CalculatorEngine.class.getName(), "Calculation thread started work: " + thread.getName()); + //System.out.println(jsclExpression); + calculationThread.setObject(thread); + final Generic genericResult = operation.evaluateGeneric(jsclExpression); + + // NOTE: toString() method must be called here as ArithmeticOperationException may occur in it (just to avoid later check!) + genericResult.toString(); + + calculationResult.setObject(genericResult); + } catch (AbstractJsclArithmeticException e) { + evalException.setObject(new CalculatorEvalException(e, e, jsclExpression)); + } catch (ArithmeticException e) { + //System.out.println(e.getMessage()); + final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_1, MessageType.error, CalculatorApplication.getInstance(), e.getMessage()); + parseException.setObject(new CalculatorParseException(jsclExpression, androidMessage)); + } catch (StackOverflowError e) { + //System.out.println(StringUtils.fromStackTrace(e.getStackTrace())); + final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_2, MessageType.error, CalculatorApplication.getInstance()); + parseException.setObject(new CalculatorParseException(jsclExpression, androidMessage)); + } catch (jscl.text.ParseException e) { + //System.out.println(e.getMessage()); + parseException.setObject(new CalculatorParseException(e)); + } catch (ParseInterruptedException e) { + //System.out.println(e.getMessage()); + // do nothing - we ourselves interrupt the calculations + } finally { + //Log.d(CalculatorEngine.class.getName(), "Calculation thread ended work: " + thread.getName()); + calculationThread.setObject(null); + latch.countDown(); + } + } + }).start(); + + try { + //Log.d(CalculatorEngine.class.getName(), "Main thread is waiting: " + Thread.currentThread().getName()); + latch.await(timeout, TimeUnit.SECONDS); + //Log.d(CalculatorEngine.class.getName(), "Main thread got up: " + Thread.currentThread().getName()); + + final CalculatorParseException parseExceptionObject = parseException.getObject(); + final CalculatorEvalException evalExceptionObject = evalException.getObject(); + final Object calculationResultLocal = calculationResult.getObject(); + final Thread calculationThreadLocal = calculationThread.getObject(); + + if (calculationThreadLocal != null) { + if (threadKiller != null) { + threadKiller.killThread(calculationThreadLocal); + } + //calculationThreadLocal.stop(); + } + + if (parseExceptionObject != null || evalExceptionObject != null) { + if (operation == JsclOperation.numeric && + (preparedExpression.isExistsUndefinedVar() || (evalExceptionObject != null && evalExceptionObject.getCause() instanceof NumeralBaseException))) { + return evaluate(JsclOperation.simplify, expression, mr); + } + + if (parseExceptionObject != null) { + throw parseExceptionObject; + } else { + throw evalExceptionObject; + } + } + + if (calculationResultLocal == null) { + final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_3, MessageType.error, CalculatorApplication.getInstance()); + throw new CalculatorParseException(jsclExpression, androidMessage); + } + + } catch (InterruptedException e) { + final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_4, MessageType.error, CalculatorApplication.getInstance()); + throw new CalculatorParseException(jsclExpression, androidMessage); + } + + final Generic genericResult = calculationResult.getObject(); + + return new CalculatorOutputImpl(operation.getFromProcessor().process(genericResult), operation, genericResult); + } + } + + public void setPrecision(int precision) { + this.getEngine().setPrecision(precision); + } + + public void setRoundResult(boolean roundResult) { + this.getEngine().setRoundResult(roundResult); + } + + public void init(@Nullable Context context, @Nullable SharedPreferences preferences) { + synchronized (lock) { + reset(context, preferences); + } + } + + public void reset(@Nullable Context context, @Nullable SharedPreferences preferences) { + synchronized (lock) { + softReset(context, preferences); + + varsRegistry.load(context, preferences); + functionsRegistry.load(context, preferences); + operatorsRegistry.load(context, preferences); + postfixFunctionsRegistry.load(context, preferences); + } + } + + public void softReset(@Nullable Context context, @Nullable SharedPreferences preferences) { + synchronized (lock) { + if (preferences != null) { + this.setPrecision(Preferences.precision.getPreference(preferences)); + this.setRoundResult(Preferences.roundResult.getPreference(preferences)); + this.setAngleUnits(getAngleUnitsFromPrefs(preferences)); + this.setNumeralBase(getNumeralBaseFromPrefs(preferences)); + this.setMultiplicationSign(Preferences.multiplicationSign.getPreference(preferences)); + this.setScienceNotation(Preferences.scienceNotation.getPreference(preferences)); + this.setTimeout(Preferences.maxCalculationTime.getPreference(preferences)); + + final String groupingSeparator = Preferences.groupingSeparator.getPreference(preferences); + if (StringUtils.isEmpty(groupingSeparator)) { + this.getEngine().setUseGroupingSeparator(false); + } else { + this.getEngine().setUseGroupingSeparator(true); + this.getEngine().setGroupingSeparator(groupingSeparator.charAt(0)); + } + } + } + } + + + @NotNull + public NumeralBase getNumeralBaseFromPrefs(@NotNull SharedPreferences preferences) { + return Preferences.numeralBase.getPreference(preferences); + } + + @NotNull + public AngleUnit getAngleUnitsFromPrefs(@NotNull SharedPreferences preferences) { + return Preferences.angleUnit.getPreference(preferences); + } + + //for tests only + void setDecimalGroupSymbols(@NotNull DecimalFormatSymbols decimalGroupSymbols) { + synchronized (lock) { + this.getEngine().setDecimalGroupSymbols(decimalGroupSymbols); + } + } + + @Override + @NotNull + public AndroidMathRegistry getVarsRegistry() { + return varsRegistry; + } + + @Override + @NotNull + public AndroidMathRegistry getFunctionsRegistry() { + return functionsRegistry; + } + + @Override + @NotNull + public AndroidMathRegistry getOperatorsRegistry() { + return operatorsRegistry; + } + + @Override + @NotNull + public AndroidMathRegistry getPostfixFunctionsRegistry() { + return postfixFunctionsRegistry; + } + + @Override + @NotNull + public MathEngine getEngine() { + return engine; + } + + // package protected for tests + void setTimeout(int timeout) { + this.timeout = timeout; + } + + public void setAngleUnits(@NotNull AngleUnit angleUnits) { + getEngine().setAngleUnits(angleUnits); + } + + public void setScienceNotation(boolean scienceNotation) { + getEngine().setScienceNotation(scienceNotation); + } + + public void setNumeralBase(@NotNull NumeralBase numeralBase) { + getEngine().setNumeralBase(numeralBase); + } + + // for tests only + void setThreadKiller(@Nullable ThreadKiller threadKiller) { + this.threadKiller = threadKiller; + } + + private static interface ThreadKiller { + void killThread(@NotNull Thread thread); + } + + private static class AndroidThreadKiller implements ThreadKiller { + @Override + public void killThread(@NotNull Thread thread) { + thread.setPriority(Thread.MIN_PRIORITY); + thread.interrupt(); + } + } + + public static class ThreadKillerImpl implements ThreadKiller { + @Override + public void killThread(@NotNull Thread thread) { + thread.setPriority(Thread.MIN_PRIORITY); + thread.stop(); + } + } +} diff --git a/calculatorpp/src/test/java/org/solovyev/android/calculator/history/HistoryUtilsTest.java b/calculatorpp/src/test/java/org/solovyev/android/calculator/history/HistoryUtilsTest.java index 7d427a1d..4247f1b8 100644 --- a/calculatorpp/src/test/java/org/solovyev/android/calculator/history/HistoryUtilsTest.java +++ b/calculatorpp/src/test/java/org/solovyev/android/calculator/history/HistoryUtilsTest.java @@ -1,321 +1,321 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - * or visit http://se.solovyev.org - */ - -package org.solovyev.android.calculator.history; - -import jscl.math.Generic; -import junit.framework.Assert; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.junit.Test; -import org.solovyev.android.calculator.Editor; -import org.solovyev.android.calculator.JCalculatorDisplay; -import org.solovyev.android.calculator.jscl.JsclOperation; -import org.solovyev.common.equals.CollectionEqualizer; -import org.solovyev.common.equals.EqualsTool; -import org.solovyev.common.history.HistoryHelper; -import org.solovyev.common.history.SimpleHistoryHelper; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -/** - * User: serso - * Date: 12/17/11 - * Time: 10:01 PM - */ -public class HistoryUtilsTest { - - @Test - public void testFromXml() throws Exception { - - } - - private static final String emptyHistory = "\n" + - " \n" + - ""; - - private static final String toXml1 = "\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " 3\n" + - " 1+1\n" + - " \n" + - " \n" + - " \n" + - " 1\n" + - " Error\n" + - " \n" + - " simplify\n" + - " \n" + - " \n" + - " \n" + - ""; - - private static final String toXml2 = "\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " 3\n" + - " 1+1\n" + - " \n" + - " \n" + - " \n" + - " 1\n" + - " Error\n" + - " \n" + - " simplify\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " 2\n" + - " 5/6\n" + - " \n" + - " \n" + - " \n" + - " 3\n" + - " 5/6\n" + - " \n" + - " numeric\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " 1\n" + - " null\n" + - " \n" + - " \n" + - " \n" + - " 1\n" + - " Error\n" + - " \n" + - " elementary\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " 0\n" + - " 4+5/35sin(41)+dfdsfsdfs\n" + - " \n" + - " \n" + - " \n" + - " 1\n" + - " 4+5/35sin(41)+dfdsfsdfs\n" + - " \n" + - " numeric\n" + - " \n" + - " \n" + - " \n" + - ""; - - @Test - public void testToXml() throws Exception { - final Date date = new Date(100000000); - - HistoryHelper history = new SimpleHistoryHelper(); - - JCalculatorDisplay calculatorDisplay = new TestCalculatorDisplay(); - calculatorDisplay.setErrorMessage("error_msg1"); - calculatorDisplay.setText("Error"); - calculatorDisplay.setSelection(1); - calculatorDisplay.setJsclOperation(JsclOperation.simplify); - - Editor calculatorEditor = new TestEditor(); - calculatorEditor.setSelection(3); - calculatorEditor.setText("1+1"); - - CalculatorHistoryState state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay); - state.setTime(date.getTime()); - history.addState(state); - - Assert.assertEquals(emptyHistory, HistoryUtils.toXml(history.getStates())); - - - state.setSaved(true); - - Assert.assertEquals(toXml1, HistoryUtils.toXml(history.getStates())); - - calculatorDisplay = new TestCalculatorDisplay(); - calculatorDisplay.setErrorMessage(null); - calculatorDisplay.setText("5/6"); - calculatorDisplay.setSelection(3); - calculatorDisplay.setJsclOperation(JsclOperation.numeric); - - calculatorEditor = new TestEditor(); - calculatorEditor.setSelection(2); - calculatorEditor.setText("5/6"); - - state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay); - state.setSaved(true); - state.setTime(date.getTime()); - history.addState(state); - - calculatorDisplay = new TestCalculatorDisplay(); - calculatorDisplay.setErrorMessage("error_msg2"); - calculatorDisplay.setText("Error"); - calculatorDisplay.setSelection(1); - calculatorDisplay.setJsclOperation(JsclOperation.elementary); - - calculatorEditor = new TestEditor(); - calculatorEditor.setSelection(1); - calculatorEditor.setText(null); - - state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay); - state.setSaved(true); - state.setTime(date.getTime()); - history.addState(state); - - calculatorDisplay = new TestCalculatorDisplay(); - calculatorDisplay.setErrorMessage(null); - calculatorDisplay.setText("4+5/35sin(41)+dfdsfsdfs"); - calculatorDisplay.setSelection(1); - calculatorDisplay.setJsclOperation(JsclOperation.numeric); - - calculatorEditor = new TestEditor(); - calculatorEditor.setSelection(0); - calculatorEditor.setText("4+5/35sin(41)+dfdsfsdfs"); - - state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay); - state.setSaved(true); - state.setTime(date.getTime()); - history.addState(state); - - String xml = HistoryUtils.toXml(history.getStates()); - Assert.assertEquals(toXml2, xml); - - final List fromXml = new ArrayList(); - final HistoryHelper historyFromXml = new SimpleHistoryHelper(); - HistoryUtils.fromXml(xml, fromXml); - for (CalculatorHistoryState historyState : fromXml) { - historyFromXml.addState(historyState); - } - - Assert.assertEquals(history.getStates().size(), historyFromXml.getStates().size()); - - for (CalculatorHistoryState historyState : history.getStates()) { - historyState.setId(0); - historyState.setSaved(true); - } - for (CalculatorHistoryState historyState : historyFromXml.getStates()) { - historyState.setId(0); - historyState.setSaved(true); - } - Assert.assertTrue(EqualsTool.areEqual(history.getStates(), historyFromXml.getStates(), new CollectionEqualizer(null))); - } - - - private static class TestCalculatorDisplay implements JCalculatorDisplay { - - @NotNull - private final TestEditor testEditor = new TestEditor(); - - private boolean valid; - - private String errorMessage; - - private JsclOperation operation; - - private Generic genericResult; - - @Override - public boolean isValid() { - return this.valid; - } - - @Override - public void setValid(boolean valid) { - this.valid = valid; - } - - @Override - public String getErrorMessage() { - return this.errorMessage; - } - - @Override - public void setErrorMessage(@Nullable String errorMessage) { - this.errorMessage = errorMessage; - } - - @Override - public void setJsclOperation(@NotNull JsclOperation jsclOperation) { - this.operation = jsclOperation; - } - - @NotNull - @Override - public JsclOperation getJsclOperation() { - return this.operation; - } - - @Override - public void setGenericResult(@Nullable Generic genericResult) { - this.genericResult = genericResult; - } - - @Override - public Generic getGenericResult() { - return this.genericResult; - } - - @Override - public CharSequence getText() { - return this.testEditor.getText(); - } - - @Override - public void setText(@Nullable CharSequence text) { - this.testEditor.setText(text); - } - - @Override - public int getSelection() { - return this.testEditor.getSelection(); - } - - @Override - public void setSelection(int selection) { - this.testEditor.setSelection(selection); - } - } - - private static class TestEditor implements Editor { - - @Nullable - private CharSequence text; - - private int selection; - - @Nullable - @Override - public CharSequence getText() { - return this.text; - } - - @Override - public void setText(@Nullable CharSequence text) { - this.text = text; - } - - @Override - public int getSelection() { - return this.selection; - } - - @Override - public void setSelection(int selection) { - this.selection = selection; - } - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + * or visit http://se.solovyev.org + */ + +package org.solovyev.android.calculator.history; + +import jscl.math.Generic; +import junit.framework.Assert; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.junit.Test; +import org.solovyev.android.calculator.CalculatorDisplay; +import org.solovyev.android.calculator.Editor; +import org.solovyev.android.calculator.jscl.JsclOperation; +import org.solovyev.common.equals.CollectionEqualizer; +import org.solovyev.common.equals.EqualsTool; +import org.solovyev.common.history.HistoryHelper; +import org.solovyev.common.history.SimpleHistoryHelper; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * User: serso + * Date: 12/17/11 + * Time: 10:01 PM + */ +public class HistoryUtilsTest { + + @Test + public void testFromXml() throws Exception { + + } + + private static final String emptyHistory = "\n" + + " \n" + + ""; + + private static final String toXml1 = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " 3\n" + + " 1+1\n" + + " \n" + + " \n" + + " \n" + + " 1\n" + + " Error\n" + + " \n" + + " simplify\n" + + " \n" + + " \n" + + " \n" + + ""; + + private static final String toXml2 = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " 3\n" + + " 1+1\n" + + " \n" + + " \n" + + " \n" + + " 1\n" + + " Error\n" + + " \n" + + " simplify\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " 2\n" + + " 5/6\n" + + " \n" + + " \n" + + " \n" + + " 3\n" + + " 5/6\n" + + " \n" + + " numeric\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " 1\n" + + " null\n" + + " \n" + + " \n" + + " \n" + + " 1\n" + + " Error\n" + + " \n" + + " elementary\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " 0\n" + + " 4+5/35sin(41)+dfdsfsdfs\n" + + " \n" + + " \n" + + " \n" + + " 1\n" + + " 4+5/35sin(41)+dfdsfsdfs\n" + + " \n" + + " numeric\n" + + " \n" + + " \n" + + " \n" + + ""; + + @Test + public void testToXml() throws Exception { + final Date date = new Date(100000000); + + HistoryHelper history = new SimpleHistoryHelper(); + + CalculatorDisplay calculatorDisplay = new TestCalculatorDisplay(); + calculatorDisplay.setErrorMessage("error_msg1"); + calculatorDisplay.setText("Error"); + calculatorDisplay.setSelection(1); + calculatorDisplay.setJsclOperation(JsclOperation.simplify); + + Editor calculatorEditor = new TestEditor(); + calculatorEditor.setSelection(3); + calculatorEditor.setText("1+1"); + + CalculatorHistoryState state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay); + state.setTime(date.getTime()); + history.addState(state); + + Assert.assertEquals(emptyHistory, HistoryUtils.toXml(history.getStates())); + + + state.setSaved(true); + + Assert.assertEquals(toXml1, HistoryUtils.toXml(history.getStates())); + + calculatorDisplay = new TestCalculatorDisplay(); + calculatorDisplay.setErrorMessage(null); + calculatorDisplay.setText("5/6"); + calculatorDisplay.setSelection(3); + calculatorDisplay.setJsclOperation(JsclOperation.numeric); + + calculatorEditor = new TestEditor(); + calculatorEditor.setSelection(2); + calculatorEditor.setText("5/6"); + + state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay); + state.setSaved(true); + state.setTime(date.getTime()); + history.addState(state); + + calculatorDisplay = new TestCalculatorDisplay(); + calculatorDisplay.setErrorMessage("error_msg2"); + calculatorDisplay.setText("Error"); + calculatorDisplay.setSelection(1); + calculatorDisplay.setJsclOperation(JsclOperation.elementary); + + calculatorEditor = new TestEditor(); + calculatorEditor.setSelection(1); + calculatorEditor.setText(null); + + state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay); + state.setSaved(true); + state.setTime(date.getTime()); + history.addState(state); + + calculatorDisplay = new TestCalculatorDisplay(); + calculatorDisplay.setErrorMessage(null); + calculatorDisplay.setText("4+5/35sin(41)+dfdsfsdfs"); + calculatorDisplay.setSelection(1); + calculatorDisplay.setJsclOperation(JsclOperation.numeric); + + calculatorEditor = new TestEditor(); + calculatorEditor.setSelection(0); + calculatorEditor.setText("4+5/35sin(41)+dfdsfsdfs"); + + state = CalculatorHistoryState.newInstance(calculatorEditor, calculatorDisplay); + state.setSaved(true); + state.setTime(date.getTime()); + history.addState(state); + + String xml = HistoryUtils.toXml(history.getStates()); + Assert.assertEquals(toXml2, xml); + + final List fromXml = new ArrayList(); + final HistoryHelper historyFromXml = new SimpleHistoryHelper(); + HistoryUtils.fromXml(xml, fromXml); + for (CalculatorHistoryState historyState : fromXml) { + historyFromXml.addState(historyState); + } + + Assert.assertEquals(history.getStates().size(), historyFromXml.getStates().size()); + + for (CalculatorHistoryState historyState : history.getStates()) { + historyState.setId(0); + historyState.setSaved(true); + } + for (CalculatorHistoryState historyState : historyFromXml.getStates()) { + historyState.setId(0); + historyState.setSaved(true); + } + Assert.assertTrue(EqualsTool.areEqual(history.getStates(), historyFromXml.getStates(), new CollectionEqualizer(null))); + } + + + private static class TestCalculatorDisplay implements CalculatorDisplay { + + @NotNull + private final TestEditor testEditor = new TestEditor(); + + private boolean valid; + + private String errorMessage; + + private JsclOperation operation; + + private Generic genericResult; + + @Override + public boolean isValid() { + return this.valid; + } + + @Override + public void setValid(boolean valid) { + this.valid = valid; + } + + @Override + public String getErrorMessage() { + return this.errorMessage; + } + + @Override + public void setErrorMessage(@Nullable String errorMessage) { + this.errorMessage = errorMessage; + } + + @Override + public void setJsclOperation(@NotNull JsclOperation jsclOperation) { + this.operation = jsclOperation; + } + + @NotNull + @Override + public JsclOperation getJsclOperation() { + return this.operation; + } + + @Override + public void setGenericResult(@Nullable Generic genericResult) { + this.genericResult = genericResult; + } + + @Override + public Generic getGenericResult() { + return this.genericResult; + } + + @Override + public CharSequence getText() { + return this.testEditor.getText(); + } + + @Override + public void setText(@Nullable CharSequence text) { + this.testEditor.setText(text); + } + + @Override + public int getSelection() { + return this.testEditor.getSelection(); + } + + @Override + public void setSelection(int selection) { + this.testEditor.setSelection(selection); + } + } + + private static class TestEditor implements Editor { + + @Nullable + private CharSequence text; + + private int selection; + + @Nullable + @Override + public CharSequence getText() { + return this.text; + } + + @Override + public void setText(@Nullable CharSequence text) { + this.text = text; + } + + @Override + public int getSelection() { + return this.selection; + } + + @Override + public void setSelection(int selection) { + this.selection = selection; + } + } +} diff --git a/calculatorpp/src/test/java/org/solovyev/android/calculator/model/CalculatorEngineTest.java b/calculatorpp/src/test/java/org/solovyev/android/calculator/model/CalculatorEngineTest.java index 8bd96dcd..375969ef 100644 --- a/calculatorpp/src/test/java/org/solovyev/android/calculator/model/CalculatorEngineTest.java +++ b/calculatorpp/src/test/java/org/solovyev/android/calculator/model/CalculatorEngineTest.java @@ -1,445 +1,445 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - */ - -package org.solovyev.android.calculator.model; - -import jscl.AngleUnit; -import jscl.JsclMathEngine; -import jscl.NumeralBase; -import jscl.math.Expression; -import jscl.math.Generic; -import jscl.math.function.Constant; -import jscl.math.function.CustomFunction; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; -import org.solovyev.android.calculator.CalculatorEvalException; -import org.solovyev.android.calculator.CalculatorParseException; -import org.solovyev.android.calculator.jscl.JsclOperation; - -import java.text.DecimalFormatSymbols; -import java.util.Locale; - -import static junit.framework.Assert.fail; - -/** - * User: serso - * Date: 9/17/11 - * Time: 9:47 PM - */ - -public class CalculatorEngineTest { - - @BeforeClass - public static void setUp() throws Exception { - CalculatorEngine.instance.init(null, null); - CalculatorEngine.instance.setPrecision(3); - CalculatorEngine.instance.setThreadKiller(new CalculatorEngine.ThreadKillerImpl()); - } - - @Test - public void testDegrees() throws Exception { - final CalculatorEngine cm = CalculatorEngine.instance; - - final AngleUnit defaultAngleUnit = cm.getEngine().getAngleUnits(); - try { - cm.getEngine().setAngleUnits(AngleUnit.rad); - cm.setPrecision(3); - try { - Assert.assertEquals("0.017", cm.evaluate(JsclOperation.numeric, "°")); - fail(); - } catch (CalculatorParseException e) { - - } - - Assert.assertEquals("0.017", cm.evaluate(JsclOperation.numeric, "1°").getResult()); - Assert.assertEquals("0.349", cm.evaluate(JsclOperation.numeric, "20.0°").getResult()); - Assert.assertEquals("0.5", cm.evaluate(JsclOperation.numeric, "sin(30°)").getResult()); - Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "asin(sin(30°))").getResult()); - Assert.assertEquals("∂(cos(t), t, t, 1°)", cm.evaluate(JsclOperation.numeric, "∂(cos(t),t,t,1°)").getResult()); - - Assert.assertEquals("∂(cos(t), t, t, 1°)", cm.evaluate(JsclOperation.simplify, "∂(cos(t),t,t,1°)").getResult()); - } finally { - cm.getEngine().setAngleUnits(defaultAngleUnit); - } - } - - @Test - public void testLongExecution() throws Exception { - final CalculatorEngine cm = CalculatorEngine.instance; - - try { - cm.evaluate(JsclOperation.numeric, "3^10^10^10"); - Assert.fail(); - } catch (CalculatorParseException e) { - if (e.getMessageCode().equals(Messages.msg_3)) { - - } else { - System.out.print(e.getCause().getMessage()); - Assert.fail(); - } - } - - try { - cm.evaluate(JsclOperation.numeric, "9999999!"); - Assert.fail(); - } catch (CalculatorParseException e) { - if (e.getMessageCode().equals(Messages.msg_3)) { - - } else { - System.out.print(e.getCause().getMessage()); - Assert.fail(); - } - } - - /*final long start = System.currentTimeMillis(); - try { - cm.evaluate(JsclOperation.numeric, "3^10^10^10"); - Assert.fail(); - } catch (ParseException e) { - if (e.getMessage().startsWith("Too long calculation")) { - final long end = System.currentTimeMillis(); - Assert.assertTrue(end - start < 1000); - } else { - Assert.fail(); - } - }*/ - - } - - @Test - public void testEvaluate() throws Exception { - final CalculatorEngine cm = CalculatorEngine.instance; - - Assert.assertEquals("cos(t)+10%", cm.evaluate(JsclOperation.simplify, "cos(t)+10%").getResult()); - - final Generic expression = cm.getEngine().simplifyGeneric("cos(t)+10%"); - expression.substitute(new Constant("t"), Expression.valueOf(100d)); - - Assert.assertEquals("it", cm.evaluate(JsclOperation.simplify, "it").getResult()); - Assert.assertEquals("10%", cm.evaluate(JsclOperation.simplify, "10%").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(0, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq(1, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq( 1, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.simplify, "eq( 1, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "lg(10)").getResult()); - Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "2+2").getResult()); - final AngleUnit defaultAngleUnit = cm.getEngine().getAngleUnits(); - try { - cm.getEngine().setAngleUnits(AngleUnit.rad); - Assert.assertEquals("-0.757", cm.evaluate(JsclOperation.numeric, "sin(4)").getResult()); - Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "asin(0.5)").getResult()); - Assert.assertEquals("-0.396", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)").getResult()); - Assert.assertEquals("-0.56", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)√(2)").getResult()); - Assert.assertEquals("-0.56", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)√(2)").getResult()); - } finally { - cm.getEngine().setAngleUnits(defaultAngleUnit); - } - Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "e^2").getResult()); - Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "exp(1)^2").getResult()); - Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "exp(2)").getResult()); - Assert.assertEquals("2+i", cm.evaluate(JsclOperation.numeric, "2*1+√(-1)").getResult()); - try { - cm.getEngine().setAngleUnits(AngleUnit.rad); - Assert.assertEquals("0.921+Πi", cm.evaluate(JsclOperation.numeric, "ln(5cosh(38π√(2cos(2))))").getResult()); - Assert.assertEquals("-3.41+3.41i", cm.evaluate(JsclOperation.numeric, "(5tan(2i)+2i)/(1-i)").getResult()); - } finally { - cm.getEngine().setAngleUnits(defaultAngleUnit); - } - Assert.assertEquals("7.389i", cm.evaluate(JsclOperation.numeric, "iexp(2)").getResult()); - Assert.assertEquals("2+7.389i", cm.evaluate(JsclOperation.numeric, "2+iexp(2)").getResult()); - Assert.assertEquals("2+7.389i", cm.evaluate(JsclOperation.numeric, "2+√(-1)exp(2)").getResult()); - Assert.assertEquals("2-2.5i", cm.evaluate(JsclOperation.numeric, "2-2.5i").getResult()); - Assert.assertEquals("-2-2.5i", cm.evaluate(JsclOperation.numeric, "-2-2.5i").getResult()); - Assert.assertEquals("-2+2.5i", cm.evaluate(JsclOperation.numeric, "-2+2.5i").getResult()); - Assert.assertEquals("-2+2.1i", cm.evaluate(JsclOperation.numeric, "-2+2.1i").getResult()); - Assert.assertEquals("-0.1-0.2i", cm.evaluate(JsclOperation.numeric, "(1-i)/(2+6i)").getResult()); - - junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "4!").getResult()); - junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "(2+2)!").getResult()); - junit.framework.Assert.assertEquals("120", cm.evaluate(JsclOperation.numeric, "(2+2+1)!").getResult()); - junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "(2.0+2.0)!").getResult()); - junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "4.0!").getResult()); - junit.framework.Assert.assertEquals("720", cm.evaluate(JsclOperation.numeric, "(3!)!").getResult()); - junit.framework.Assert.assertEquals("36", Expression.valueOf("3!^2").numeric().toString()); - junit.framework.Assert.assertEquals("3", Expression.valueOf("cubic(27)").numeric().toString()); - try { - junit.framework.Assert.assertEquals("√(-1)!", cm.evaluate(JsclOperation.numeric, "i!").getResult()); - fail(); - } catch (CalculatorParseException e) { - } - - junit.framework.Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "(π/π)!").getResult()); - - try { - junit.framework.Assert.assertEquals("i", cm.evaluate(JsclOperation.numeric, "(-1)i!").getResult()); - fail(); - } catch (CalculatorParseException e) { - - } - junit.framework.Assert.assertEquals("24i", cm.evaluate(JsclOperation.numeric, "4!i").getResult()); - - CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("si", 5d)); - - try { - cm.getEngine().setAngleUnits(AngleUnit.rad); - Assert.assertEquals("0.451", cm.evaluate(JsclOperation.numeric, "acos(0.8999999999999811)").getResult()); - Assert.assertEquals("-0.959", cm.evaluate(JsclOperation.numeric, "sin(5)").getResult()); - Assert.assertEquals("-4.795", cm.evaluate(JsclOperation.numeric, "sin(5)si").getResult()); - Assert.assertEquals("-23.973", cm.evaluate(JsclOperation.numeric, "sisin(5)si").getResult()); - Assert.assertEquals("-23.973", cm.evaluate(JsclOperation.numeric, "si*sin(5)si").getResult()); - Assert.assertEquals("-3.309", cm.evaluate(JsclOperation.numeric, "sisin(5si)si").getResult()); - } finally { - cm.getEngine().setAngleUnits(defaultAngleUnit); - } - - CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("s", 1d)); - Assert.assertEquals("5", cm.evaluate(JsclOperation.numeric, "si").getResult()); - - CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("k", 3.5d)); - CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("k1", 4d)); - Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "k11").getResult()); - - CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t", (String) null)); - Assert.assertEquals("11t", cm.evaluate(JsclOperation.numeric, "t11").getResult()); - Assert.assertEquals("11et", cm.evaluate(JsclOperation.numeric, "t11e").getResult()); - Assert.assertEquals("∞", cm.evaluate(JsclOperation.numeric, "∞").getResult()); - Assert.assertEquals("∞", cm.evaluate(JsclOperation.numeric, "Infinity").getResult()); - Assert.assertEquals("11∞t", cm.evaluate(JsclOperation.numeric, "t11∞").getResult()); - Assert.assertEquals("-t+t^3", cm.evaluate(JsclOperation.numeric, "t(t-1)(t+1)").getResult()); - - Assert.assertEquals("100", cm.evaluate(JsclOperation.numeric, "0.1E3").getResult()); - Assert.assertEquals("3.957", cm.evaluate(JsclOperation.numeric, "ln(8)lg(8)+ln(8)").getResult()); - - Assert.assertEquals("0.933", cm.evaluate(JsclOperation.numeric, "0x:E/0x:F").getResult()); - - try { - cm.getEngine().setNumeralBase(NumeralBase.hex); - Assert.assertEquals("E/F", cm.evaluate(JsclOperation.numeric, "0x:E/0x:F").getResult()); - Assert.assertEquals("E/F", cm.evaluate(JsclOperation.simplify, "0x:E/0x:F").getResult()); - Assert.assertEquals("E/F", cm.evaluate(JsclOperation.numeric, "E/F").getResult()); - Assert.assertEquals("E/F", cm.evaluate(JsclOperation.simplify, "E/F").getResult()); - } finally { - cm.getEngine().setNumeralBase(NumeralBase.dec); - } - - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "((((((0))))))").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((0))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))").getResult()); - - - /* Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "30°").getResult()); - Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "(10+20)°").getResult()); - Assert.assertEquals("1.047", cm.evaluate(JsclOperation.numeric, "(10+20)°*2").getResult()); - try { - Assert.assertEquals("0.278", cm.evaluate(JsclOperation.numeric, "30°^2").getResult()); - junit.framework.Assert.fail(); - } catch (ParseException e) { - if ( !e.getMessage().equals("Power operation after postfix function is currently unsupported!") ) { - junit.framework.Assert.fail(); - } - }*/ - -/* try { - cm.setTimeout(5000); - Assert.assertEquals("2", cm.evaluate(JsclOperation.numeric, "2!").getResult()); - } finally { - cm.setTimeout(3000); - }*/ - - CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t", (String) null)); - Assert.assertEquals("2t", cm.evaluate(JsclOperation.simplify, "∂(t^2,t)").getResult()); - Assert.assertEquals("2t", cm.evaluate(JsclOperation.numeric, "∂(t^2,t)").getResult()); - CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t", "2")); - Assert.assertEquals("2t", cm.evaluate(JsclOperation.simplify, "∂(t^2,t)").getResult()); - Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "∂(t^2,t)").getResult()); - - Assert.assertEquals("-x+x*ln(x)", cm.getEngine().simplify("∫(ln(x), x)")); - Assert.assertEquals("-(x-x*ln(x))/(ln(2)+ln(5))", cm.getEngine().simplify("∫(log(10, x), x)")); - - Assert.assertEquals("∫((ln(2)+ln(5))/ln(x), x)", cm.getEngine().simplify("∫(ln(10)/ln(x), x)")); - Assert.assertEquals("∫(ln(10)/ln(x), x)", Expression.valueOf("∫(log(x, 10), x)").expand().toString()); - Assert.assertEquals("∫((ln(2)+ln(5))/ln(x), x)", cm.getEngine().simplify("∫(log(x, 10), x)")); - } - - @Test - public void testFormatting() throws Exception { - final CalculatorEngine ce = CalculatorEngine.instance; - - Assert.assertEquals("12 345", ce.evaluate(JsclOperation.simplify, "12345").getResult()); - - } - - @Test - public void testI() throws CalculatorParseException, CalculatorEvalException { - final CalculatorEngine cm = CalculatorEngine.instance; - - Assert.assertEquals("-i", cm.evaluate(JsclOperation.numeric, "i^3").getResult()); - for (int i = 0; i < 1000; i++) { - double real = (Math.random()-0.5) * 1000; - double imag = (Math.random()-0.5) * 1000; - int exp = (int)(Math.random() * 10); - - final StringBuilder sb = new StringBuilder(); - sb.append(real); - if ( imag > 0 ) { - sb.append("+"); - } - sb.append(imag); - sb.append("^").append(exp); - try { - cm.evaluate(JsclOperation.numeric, sb.toString()).getResult(); - } catch (Throwable e) { - fail(sb.toString()); - } - } - } - - @Test - public void testEmptyFunction() throws Exception { - final CalculatorEngine cm = CalculatorEngine.instance; - try { - cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(acos(acos(acos(acos(acos(acos(acos(acos(cos(cos(cos(cos(cosh(acos(cos(cos(cos(cos(cos(acos(acos(acos(acos(acos(acos(acos(acos(cos(cos(cos(cos(cosh(acos(cos())))))))))))))))))))))))))))))))))))))"); - Assert.fail(); - } catch (CalculatorParseException e) { - } - Assert.assertEquals("0.34+1.382i", cm.evaluate(JsclOperation.numeric, "ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(100)))))))))))))))").getResult()); - try { - cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos())))))))))))))))))))))))))))))))))))"); - Assert.fail(); - } catch (CalculatorParseException e) { - } - - final AngleUnit defaultAngleUnit = cm.getEngine().getAngleUnits(); - try { - cm.getEngine().setAngleUnits(AngleUnit.rad); - Assert.assertEquals("0.739", cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(1))))))))))))))))))))))))))))))))))))").getResult()); - } finally { - cm.getEngine().setAngleUnits(defaultAngleUnit); - } - - CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("si", 5d)); - Assert.assertEquals("5", cm.evaluate(JsclOperation.numeric, "si").getResult()); - - try { - cm.evaluate(JsclOperation.numeric, "sin"); - Assert.fail(); - } catch (CalculatorParseException e) { - } - } - - @Test - public void testRounding() throws Exception { - final CalculatorEngine cm = CalculatorEngine.instance; - - try { - DecimalFormatSymbols decimalGroupSymbols = new DecimalFormatSymbols(Locale.getDefault()); - decimalGroupSymbols.setDecimalSeparator('.'); - decimalGroupSymbols.setGroupingSeparator('\''); - cm.setDecimalGroupSymbols(decimalGroupSymbols); - cm.setPrecision(2); - Assert.assertEquals("12'345'678.9", cm.evaluate(JsclOperation.numeric, "1.23456789E7").getResult()); - cm.setPrecision(10); - Assert.assertEquals("12'345'678.9", cm.evaluate(JsclOperation.numeric, "1.23456789E7").getResult()); - Assert.assertEquals("123'456'789", cm.evaluate(JsclOperation.numeric, "1.234567890E8").getResult()); - Assert.assertEquals("1'234'567'890.1", cm.evaluate(JsclOperation.numeric, "1.2345678901E9").getResult()); - } finally { - cm.setPrecision(3); - DecimalFormatSymbols decimalGroupSymbols = new DecimalFormatSymbols(Locale.getDefault()); - decimalGroupSymbols.setDecimalSeparator('.'); - decimalGroupSymbols.setGroupingSeparator(JsclMathEngine.GROUPING_SEPARATOR_DEFAULT.charAt(0)); - cm.setDecimalGroupSymbols(decimalGroupSymbols); - } - } - - @Test - public void testComparisonFunction() throws Exception { - final CalculatorEngine cm = CalculatorEngine.instance; - - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(0, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq(1, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq(1, 1.0)").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(1, 1.000000000000001)").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(1, 0)").getResult()); - - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "lt(0, 1)").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "lt(1, 1)").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "lt(1, 0)").getResult()); - - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "gt(0, 1)").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "gt(1, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "gt(1, 0)").getResult()); - - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ne(0, 1)").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ne(1, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ne(1, 0)").getResult()); - - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "le(0, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "le(1, 1)").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "le(1, 0)").getResult()); - - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ge(0, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ge(1, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ge(1, 0)").getResult()); - - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ap(0, 1)").getResult()); - Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ap(1, 1)").getResult()); - //Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ap(1, 1.000000000000001)").getResult()); - Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ap(1, 0)").getResult()); - - } - - - @Test - public void testNumeralSystems() throws Exception { - final CalculatorEngine cm = CalculatorEngine.instance; - - Assert.assertEquals("11 259 375", cm.evaluate(JsclOperation.numeric, "0x:ABCDEF").getResult()); - Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "0x:ABCDEF*e").getResult()); - Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "e*0x:ABCDEF").getResult()); - Assert.assertEquals("e", cm.evaluate(JsclOperation.numeric, "e*0x:ABCDEF/0x:ABCDEF").getResult()); - Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "0x:ABCDEF*e*0x:ABCDEF/0x:ABCDEF").getResult()); - Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "c+0x:ABCDEF*e*0x:ABCDEF/0x:ABCDEF-c+0x:C-0x:C").getResult()); - Assert.assertEquals("1 446 257 064 651.832", cm.evaluate(JsclOperation.numeric, "28*28 * sin(28) - 0b:1101 + √(28) + exp ( 28) ").getResult()); - Assert.assertEquals("13", cm.evaluate(JsclOperation.numeric, "0b:1101").getResult()); - - try { - cm.evaluate(JsclOperation.numeric, "0b:π").getResult(); - Assert.fail(); - } catch (CalculatorParseException e) { - // ok - } - - final NumeralBase defaultNumeralBase = cm.getEngine().getNumeralBase(); - try{ - cm.getEngine().setNumeralBase(NumeralBase.bin); - Assert.assertEquals("101", cm.evaluate(JsclOperation.numeric, "10+11").getResult()); - Assert.assertEquals("10/11", cm.evaluate(JsclOperation.numeric, "10/11").getResult()); - - cm.getEngine().setNumeralBase(NumeralBase.hex); - Assert.assertEquals("63 7B", cm.evaluate(JsclOperation.numeric, "56CE+CAD").getResult()); - Assert.assertEquals("E", cm.evaluate(JsclOperation.numeric, "E").getResult()); - } finally { - cm.setNumeralBase(defaultNumeralBase); - } - } - - @Test - public void testLog() throws Exception { - final CalculatorEngine cm = CalculatorEngine.instance; - - Assert.assertEquals("∞", Expression.valueOf("1/0").numeric().toString()); - Assert.assertEquals("∞", Expression.valueOf("ln(10)/ln(1)").numeric().toString()); - - // logarithm - Assert.assertEquals("ln(x)/ln(base)", ((CustomFunction) cm.getFunctionsRegistry().get("log")).getContent()); - Assert.assertEquals("∞", cm.evaluate(JsclOperation.numeric, "log(1, 10)").getResult()); - Assert.assertEquals("3.322", cm.evaluate(JsclOperation.numeric, "log(2, 10)").getResult()); - Assert.assertEquals("1.431", cm.evaluate(JsclOperation.numeric, "log(5, 10)").getResult()); - Assert.assertEquals("0.96", cm.evaluate(JsclOperation.numeric, "log(11, 10)").getResult()); - Assert.assertEquals("1/(bln(a))", cm.evaluate(JsclOperation.simplify, "∂(log(a, b), b)").getResult()); - Assert.assertEquals("-ln(b)/(aln(a)^2)", cm.evaluate(JsclOperation.simplify, "∂(log(a, b), a)").getResult()); - - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + */ + +package org.solovyev.android.calculator.model; + +import jscl.AngleUnit; +import jscl.JsclMathEngine; +import jscl.NumeralBase; +import jscl.math.Expression; +import jscl.math.Generic; +import jscl.math.function.Constant; +import jscl.math.function.CustomFunction; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.solovyev.android.calculator.CalculatorEvalException; +import org.solovyev.android.calculator.CalculatorParseException; +import org.solovyev.android.calculator.jscl.JsclOperation; + +import java.text.DecimalFormatSymbols; +import java.util.Locale; + +import static junit.framework.Assert.fail; + +/** + * User: serso + * Date: 9/17/11 + * Time: 9:47 PM + */ + +public class CalculatorEngineTest { + + @BeforeClass + public static void setUp() throws Exception { + CalculatorEngine.instance.init(null, null); + CalculatorEngine.instance.setPrecision(3); + CalculatorEngine.instance.setThreadKiller(new CalculatorEngine.ThreadKillerImpl()); + } + + @Test + public void testDegrees() throws Exception { + final CalculatorEngine cm = CalculatorEngine.instance; + + final AngleUnit defaultAngleUnit = cm.getEngine().getAngleUnits(); + try { + cm.getEngine().setAngleUnits(AngleUnit.rad); + cm.setPrecision(3); + try { + Assert.assertEquals("0.017", cm.evaluate(JsclOperation.numeric, "°")); + fail(); + } catch (CalculatorParseException e) { + + } + + Assert.assertEquals("0.017", cm.evaluate(JsclOperation.numeric, "1°").getStringResult()); + Assert.assertEquals("0.349", cm.evaluate(JsclOperation.numeric, "20.0°").getStringResult()); + Assert.assertEquals("0.5", cm.evaluate(JsclOperation.numeric, "sin(30°)").getStringResult()); + Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "asin(sin(30°))").getStringResult()); + Assert.assertEquals("∂(cos(t), t, t, 1°)", cm.evaluate(JsclOperation.numeric, "∂(cos(t),t,t,1°)").getStringResult()); + + Assert.assertEquals("∂(cos(t), t, t, 1°)", cm.evaluate(JsclOperation.simplify, "∂(cos(t),t,t,1°)").getStringResult()); + } finally { + cm.getEngine().setAngleUnits(defaultAngleUnit); + } + } + + @Test + public void testLongExecution() throws Exception { + final CalculatorEngine cm = CalculatorEngine.instance; + + try { + cm.evaluate(JsclOperation.numeric, "3^10^10^10"); + Assert.fail(); + } catch (CalculatorParseException e) { + if (e.getMessageCode().equals(Messages.msg_3)) { + + } else { + System.out.print(e.getCause().getMessage()); + Assert.fail(); + } + } + + try { + cm.evaluate(JsclOperation.numeric, "9999999!"); + Assert.fail(); + } catch (CalculatorParseException e) { + if (e.getMessageCode().equals(Messages.msg_3)) { + + } else { + System.out.print(e.getCause().getMessage()); + Assert.fail(); + } + } + + /*final long start = System.currentTimeMillis(); + try { + cm.evaluate(JsclOperation.numeric, "3^10^10^10"); + Assert.fail(); + } catch (ParseException e) { + if (e.getMessage().startsWith("Too long calculation")) { + final long end = System.currentTimeMillis(); + Assert.assertTrue(end - start < 1000); + } else { + Assert.fail(); + } + }*/ + + } + + @Test + public void testEvaluate() throws Exception { + final CalculatorEngine cm = CalculatorEngine.instance; + + Assert.assertEquals("cos(t)+10%", cm.evaluate(JsclOperation.simplify, "cos(t)+10%").getStringResult()); + + final Generic expression = cm.getEngine().simplifyGeneric("cos(t)+10%"); + expression.substitute(new Constant("t"), Expression.valueOf(100d)); + + Assert.assertEquals("it", cm.evaluate(JsclOperation.simplify, "it").getStringResult()); + Assert.assertEquals("10%", cm.evaluate(JsclOperation.simplify, "10%").getStringResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(0, 1)").getStringResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq(1, 1)").getStringResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq( 1, 1)").getStringResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.simplify, "eq( 1, 1)").getStringResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "lg(10)").getStringResult()); + Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "2+2").getStringResult()); + final AngleUnit defaultAngleUnit = cm.getEngine().getAngleUnits(); + try { + cm.getEngine().setAngleUnits(AngleUnit.rad); + Assert.assertEquals("-0.757", cm.evaluate(JsclOperation.numeric, "sin(4)").getStringResult()); + Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "asin(0.5)").getStringResult()); + Assert.assertEquals("-0.396", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)").getStringResult()); + Assert.assertEquals("-0.56", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)√(2)").getStringResult()); + Assert.assertEquals("-0.56", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)√(2)").getStringResult()); + } finally { + cm.getEngine().setAngleUnits(defaultAngleUnit); + } + Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "e^2").getStringResult()); + Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "exp(1)^2").getStringResult()); + Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "exp(2)").getStringResult()); + Assert.assertEquals("2+i", cm.evaluate(JsclOperation.numeric, "2*1+√(-1)").getStringResult()); + try { + cm.getEngine().setAngleUnits(AngleUnit.rad); + Assert.assertEquals("0.921+Πi", cm.evaluate(JsclOperation.numeric, "ln(5cosh(38π√(2cos(2))))").getStringResult()); + Assert.assertEquals("-3.41+3.41i", cm.evaluate(JsclOperation.numeric, "(5tan(2i)+2i)/(1-i)").getStringResult()); + } finally { + cm.getEngine().setAngleUnits(defaultAngleUnit); + } + Assert.assertEquals("7.389i", cm.evaluate(JsclOperation.numeric, "iexp(2)").getStringResult()); + Assert.assertEquals("2+7.389i", cm.evaluate(JsclOperation.numeric, "2+iexp(2)").getStringResult()); + Assert.assertEquals("2+7.389i", cm.evaluate(JsclOperation.numeric, "2+√(-1)exp(2)").getStringResult()); + Assert.assertEquals("2-2.5i", cm.evaluate(JsclOperation.numeric, "2-2.5i").getStringResult()); + Assert.assertEquals("-2-2.5i", cm.evaluate(JsclOperation.numeric, "-2-2.5i").getStringResult()); + Assert.assertEquals("-2+2.5i", cm.evaluate(JsclOperation.numeric, "-2+2.5i").getStringResult()); + Assert.assertEquals("-2+2.1i", cm.evaluate(JsclOperation.numeric, "-2+2.1i").getStringResult()); + Assert.assertEquals("-0.1-0.2i", cm.evaluate(JsclOperation.numeric, "(1-i)/(2+6i)").getStringResult()); + + junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "4!").getStringResult()); + junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "(2+2)!").getStringResult()); + junit.framework.Assert.assertEquals("120", cm.evaluate(JsclOperation.numeric, "(2+2+1)!").getStringResult()); + junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "(2.0+2.0)!").getStringResult()); + junit.framework.Assert.assertEquals("24", cm.evaluate(JsclOperation.numeric, "4.0!").getStringResult()); + junit.framework.Assert.assertEquals("720", cm.evaluate(JsclOperation.numeric, "(3!)!").getStringResult()); + junit.framework.Assert.assertEquals("36", Expression.valueOf("3!^2").numeric().toString()); + junit.framework.Assert.assertEquals("3", Expression.valueOf("cubic(27)").numeric().toString()); + try { + junit.framework.Assert.assertEquals("√(-1)!", cm.evaluate(JsclOperation.numeric, "i!").getStringResult()); + fail(); + } catch (CalculatorParseException e) { + } + + junit.framework.Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "(π/π)!").getStringResult()); + + try { + junit.framework.Assert.assertEquals("i", cm.evaluate(JsclOperation.numeric, "(-1)i!").getStringResult()); + fail(); + } catch (CalculatorParseException e) { + + } + junit.framework.Assert.assertEquals("24i", cm.evaluate(JsclOperation.numeric, "4!i").getStringResult()); + + CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("si", 5d)); + + try { + cm.getEngine().setAngleUnits(AngleUnit.rad); + Assert.assertEquals("0.451", cm.evaluate(JsclOperation.numeric, "acos(0.8999999999999811)").getStringResult()); + Assert.assertEquals("-0.959", cm.evaluate(JsclOperation.numeric, "sin(5)").getStringResult()); + Assert.assertEquals("-4.795", cm.evaluate(JsclOperation.numeric, "sin(5)si").getStringResult()); + Assert.assertEquals("-23.973", cm.evaluate(JsclOperation.numeric, "sisin(5)si").getStringResult()); + Assert.assertEquals("-23.973", cm.evaluate(JsclOperation.numeric, "si*sin(5)si").getStringResult()); + Assert.assertEquals("-3.309", cm.evaluate(JsclOperation.numeric, "sisin(5si)si").getStringResult()); + } finally { + cm.getEngine().setAngleUnits(defaultAngleUnit); + } + + CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("s", 1d)); + Assert.assertEquals("5", cm.evaluate(JsclOperation.numeric, "si").getStringResult()); + + CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("k", 3.5d)); + CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("k1", 4d)); + Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "k11").getStringResult()); + + CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t", (String) null)); + Assert.assertEquals("11t", cm.evaluate(JsclOperation.numeric, "t11").getStringResult()); + Assert.assertEquals("11et", cm.evaluate(JsclOperation.numeric, "t11e").getStringResult()); + Assert.assertEquals("∞", cm.evaluate(JsclOperation.numeric, "∞").getStringResult()); + Assert.assertEquals("∞", cm.evaluate(JsclOperation.numeric, "Infinity").getStringResult()); + Assert.assertEquals("11∞t", cm.evaluate(JsclOperation.numeric, "t11∞").getStringResult()); + Assert.assertEquals("-t+t^3", cm.evaluate(JsclOperation.numeric, "t(t-1)(t+1)").getStringResult()); + + Assert.assertEquals("100", cm.evaluate(JsclOperation.numeric, "0.1E3").getStringResult()); + Assert.assertEquals("3.957", cm.evaluate(JsclOperation.numeric, "ln(8)lg(8)+ln(8)").getStringResult()); + + Assert.assertEquals("0.933", cm.evaluate(JsclOperation.numeric, "0x:E/0x:F").getStringResult()); + + try { + cm.getEngine().setNumeralBase(NumeralBase.hex); + Assert.assertEquals("E/F", cm.evaluate(JsclOperation.numeric, "0x:E/0x:F").getStringResult()); + Assert.assertEquals("E/F", cm.evaluate(JsclOperation.simplify, "0x:E/0x:F").getStringResult()); + Assert.assertEquals("E/F", cm.evaluate(JsclOperation.numeric, "E/F").getStringResult()); + Assert.assertEquals("E/F", cm.evaluate(JsclOperation.simplify, "E/F").getStringResult()); + } finally { + cm.getEngine().setNumeralBase(NumeralBase.dec); + } + + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "((((((0))))))").getStringResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((0))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))").getStringResult()); + + + /* Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "30°").getResult()); + Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "(10+20)°").getResult()); + Assert.assertEquals("1.047", cm.evaluate(JsclOperation.numeric, "(10+20)°*2").getResult()); + try { + Assert.assertEquals("0.278", cm.evaluate(JsclOperation.numeric, "30°^2").getResult()); + junit.framework.Assert.fail(); + } catch (ParseException e) { + if ( !e.getMessage().equals("Power operation after postfix function is currently unsupported!") ) { + junit.framework.Assert.fail(); + } + }*/ + +/* try { + cm.setTimeout(5000); + Assert.assertEquals("2", cm.evaluate(JsclOperation.numeric, "2!").getResult()); + } finally { + cm.setTimeout(3000); + }*/ + + CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t", (String) null)); + Assert.assertEquals("2t", cm.evaluate(JsclOperation.simplify, "∂(t^2,t)").getStringResult()); + Assert.assertEquals("2t", cm.evaluate(JsclOperation.numeric, "∂(t^2,t)").getStringResult()); + CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("t", "2")); + Assert.assertEquals("2t", cm.evaluate(JsclOperation.simplify, "∂(t^2,t)").getStringResult()); + Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "∂(t^2,t)").getStringResult()); + + Assert.assertEquals("-x+x*ln(x)", cm.getEngine().simplify("∫(ln(x), x)")); + Assert.assertEquals("-(x-x*ln(x))/(ln(2)+ln(5))", cm.getEngine().simplify("∫(log(10, x), x)")); + + Assert.assertEquals("∫((ln(2)+ln(5))/ln(x), x)", cm.getEngine().simplify("∫(ln(10)/ln(x), x)")); + Assert.assertEquals("∫(ln(10)/ln(x), x)", Expression.valueOf("∫(log(x, 10), x)").expand().toString()); + Assert.assertEquals("∫((ln(2)+ln(5))/ln(x), x)", cm.getEngine().simplify("∫(log(x, 10), x)")); + } + + @Test + public void testFormatting() throws Exception { + final CalculatorEngine ce = CalculatorEngine.instance; + + Assert.assertEquals("12 345", ce.evaluate(JsclOperation.simplify, "12345").getStringResult()); + + } + + @Test + public void testI() throws CalculatorParseException, CalculatorEvalException { + final CalculatorEngine cm = CalculatorEngine.instance; + + Assert.assertEquals("-i", cm.evaluate(JsclOperation.numeric, "i^3").getStringResult()); + for (int i = 0; i < 1000; i++) { + double real = (Math.random()-0.5) * 1000; + double imag = (Math.random()-0.5) * 1000; + int exp = (int)(Math.random() * 10); + + final StringBuilder sb = new StringBuilder(); + sb.append(real); + if ( imag > 0 ) { + sb.append("+"); + } + sb.append(imag); + sb.append("^").append(exp); + try { + cm.evaluate(JsclOperation.numeric, sb.toString()).getStringResult(); + } catch (Throwable e) { + fail(sb.toString()); + } + } + } + + @Test + public void testEmptyFunction() throws Exception { + final CalculatorEngine cm = CalculatorEngine.instance; + try { + cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(acos(acos(acos(acos(acos(acos(acos(acos(cos(cos(cos(cos(cosh(acos(cos(cos(cos(cos(cos(acos(acos(acos(acos(acos(acos(acos(acos(cos(cos(cos(cos(cosh(acos(cos())))))))))))))))))))))))))))))))))))))"); + Assert.fail(); + } catch (CalculatorParseException e) { + } + Assert.assertEquals("0.34+1.382i", cm.evaluate(JsclOperation.numeric, "ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(100)))))))))))))))").getStringResult()); + try { + cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos())))))))))))))))))))))))))))))))))))"); + Assert.fail(); + } catch (CalculatorParseException e) { + } + + final AngleUnit defaultAngleUnit = cm.getEngine().getAngleUnits(); + try { + cm.getEngine().setAngleUnits(AngleUnit.rad); + Assert.assertEquals("0.739", cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(1))))))))))))))))))))))))))))))))))))").getStringResult()); + } finally { + cm.getEngine().setAngleUnits(defaultAngleUnit); + } + + CalculatorEngine.instance.getVarsRegistry().add(new Var.Builder("si", 5d)); + Assert.assertEquals("5", cm.evaluate(JsclOperation.numeric, "si").getStringResult()); + + try { + cm.evaluate(JsclOperation.numeric, "sin"); + Assert.fail(); + } catch (CalculatorParseException e) { + } + } + + @Test + public void testRounding() throws Exception { + final CalculatorEngine cm = CalculatorEngine.instance; + + try { + DecimalFormatSymbols decimalGroupSymbols = new DecimalFormatSymbols(Locale.getDefault()); + decimalGroupSymbols.setDecimalSeparator('.'); + decimalGroupSymbols.setGroupingSeparator('\''); + cm.setDecimalGroupSymbols(decimalGroupSymbols); + cm.setPrecision(2); + Assert.assertEquals("12'345'678.9", cm.evaluate(JsclOperation.numeric, "1.23456789E7").getStringResult()); + cm.setPrecision(10); + Assert.assertEquals("12'345'678.9", cm.evaluate(JsclOperation.numeric, "1.23456789E7").getStringResult()); + Assert.assertEquals("123'456'789", cm.evaluate(JsclOperation.numeric, "1.234567890E8").getStringResult()); + Assert.assertEquals("1'234'567'890.1", cm.evaluate(JsclOperation.numeric, "1.2345678901E9").getStringResult()); + } finally { + cm.setPrecision(3); + DecimalFormatSymbols decimalGroupSymbols = new DecimalFormatSymbols(Locale.getDefault()); + decimalGroupSymbols.setDecimalSeparator('.'); + decimalGroupSymbols.setGroupingSeparator(JsclMathEngine.GROUPING_SEPARATOR_DEFAULT.charAt(0)); + cm.setDecimalGroupSymbols(decimalGroupSymbols); + } + } + + @Test + public void testComparisonFunction() throws Exception { + final CalculatorEngine cm = CalculatorEngine.instance; + + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(0, 1)").getStringResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq(1, 1)").getStringResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "eq(1, 1.0)").getStringResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(1, 1.000000000000001)").getStringResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "eq(1, 0)").getStringResult()); + + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "lt(0, 1)").getStringResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "lt(1, 1)").getStringResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "lt(1, 0)").getStringResult()); + + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "gt(0, 1)").getStringResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "gt(1, 1)").getStringResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "gt(1, 0)").getStringResult()); + + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ne(0, 1)").getStringResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ne(1, 1)").getStringResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ne(1, 0)").getStringResult()); + + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "le(0, 1)").getStringResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "le(1, 1)").getStringResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "le(1, 0)").getStringResult()); + + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ge(0, 1)").getStringResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ge(1, 1)").getStringResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ge(1, 0)").getStringResult()); + + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ap(0, 1)").getStringResult()); + Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ap(1, 1)").getStringResult()); + //Assert.assertEquals("1", cm.evaluate(JsclOperation.numeric, "ap(1, 1.000000000000001)").getResult()); + Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "ap(1, 0)").getStringResult()); + + } + + + @Test + public void testNumeralSystems() throws Exception { + final CalculatorEngine cm = CalculatorEngine.instance; + + Assert.assertEquals("11 259 375", cm.evaluate(JsclOperation.numeric, "0x:ABCDEF").getStringResult()); + Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "0x:ABCDEF*e").getStringResult()); + Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "e*0x:ABCDEF").getStringResult()); + Assert.assertEquals("e", cm.evaluate(JsclOperation.numeric, "e*0x:ABCDEF/0x:ABCDEF").getStringResult()); + Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "0x:ABCDEF*e*0x:ABCDEF/0x:ABCDEF").getStringResult()); + Assert.assertEquals("30 606 154.462", cm.evaluate(JsclOperation.numeric, "c+0x:ABCDEF*e*0x:ABCDEF/0x:ABCDEF-c+0x:C-0x:C").getStringResult()); + Assert.assertEquals("1 446 257 064 651.832", cm.evaluate(JsclOperation.numeric, "28*28 * sin(28) - 0b:1101 + √(28) + exp ( 28) ").getStringResult()); + Assert.assertEquals("13", cm.evaluate(JsclOperation.numeric, "0b:1101").getStringResult()); + + try { + cm.evaluate(JsclOperation.numeric, "0b:π").getStringResult(); + Assert.fail(); + } catch (CalculatorParseException e) { + // ok + } + + final NumeralBase defaultNumeralBase = cm.getEngine().getNumeralBase(); + try{ + cm.getEngine().setNumeralBase(NumeralBase.bin); + Assert.assertEquals("101", cm.evaluate(JsclOperation.numeric, "10+11").getStringResult()); + Assert.assertEquals("10/11", cm.evaluate(JsclOperation.numeric, "10/11").getStringResult()); + + cm.getEngine().setNumeralBase(NumeralBase.hex); + Assert.assertEquals("63 7B", cm.evaluate(JsclOperation.numeric, "56CE+CAD").getStringResult()); + Assert.assertEquals("E", cm.evaluate(JsclOperation.numeric, "E").getStringResult()); + } finally { + cm.setNumeralBase(defaultNumeralBase); + } + } + + @Test + public void testLog() throws Exception { + final CalculatorEngine cm = CalculatorEngine.instance; + + Assert.assertEquals("∞", Expression.valueOf("1/0").numeric().toString()); + Assert.assertEquals("∞", Expression.valueOf("ln(10)/ln(1)").numeric().toString()); + + // logarithm + Assert.assertEquals("ln(x)/ln(base)", ((CustomFunction) cm.getFunctionsRegistry().get("log")).getContent()); + Assert.assertEquals("∞", cm.evaluate(JsclOperation.numeric, "log(1, 10)").getStringResult()); + Assert.assertEquals("3.322", cm.evaluate(JsclOperation.numeric, "log(2, 10)").getStringResult()); + Assert.assertEquals("1.431", cm.evaluate(JsclOperation.numeric, "log(5, 10)").getStringResult()); + Assert.assertEquals("0.96", cm.evaluate(JsclOperation.numeric, "log(11, 10)").getStringResult()); + Assert.assertEquals("1/(bln(a))", cm.evaluate(JsclOperation.simplify, "∂(log(a, b), b)").getStringResult()); + Assert.assertEquals("-ln(b)/(aln(a)^2)", cm.evaluate(JsclOperation.simplify, "∂(log(a, b), a)").getStringResult()); + + } +} diff --git a/calculatorpp/src/test/java/org/solovyev/android/calculator/model/NumeralBaseTest.java b/calculatorpp/src/test/java/org/solovyev/android/calculator/model/NumeralBaseTest.java index bd658b31..5e05495c 100644 --- a/calculatorpp/src/test/java/org/solovyev/android/calculator/model/NumeralBaseTest.java +++ b/calculatorpp/src/test/java/org/solovyev/android/calculator/model/NumeralBaseTest.java @@ -1,148 +1,148 @@ -package org.solovyev.android.calculator.model; - -import au.com.bytecode.opencsv.CSVReader; -import jscl.JsclMathEngine; -import jscl.MathEngine; -import jscl.math.Expression; -import jscl.text.ParseException; -import jscl.util.ExpressionGeneratorWithInput; -import org.jetbrains.annotations.NotNull; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; -import org.solovyev.android.calculator.CalculatorEvalException; -import org.solovyev.android.calculator.CalculatorParseException; -import org.solovyev.android.calculator.jscl.JsclOperation; -import org.solovyev.common.Converter; - -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.List; - -/** - * User: serso - * Date: 12/14/11 - * Time: 4:16 PM - */ -public class NumeralBaseTest { - - @BeforeClass - public static void setUp() throws Exception { - CalculatorEngine.instance.init(null, null); - CalculatorEngine.instance.setPrecision(3); - CalculatorEngine.instance.setThreadKiller(new CalculatorEngine.ThreadKillerImpl()); - } - - @Test - public void testConversion() throws Exception { - CSVReader reader = null; - try { - final MathEngine me = JsclMathEngine.instance; - - reader = new CSVReader(new InputStreamReader(NumeralBaseTest.class.getResourceAsStream("/org/solovyev/android/calculator/model/nb_table.csv")), '\t'); - - // skip first line - reader.readNext(); - - String[] line = reader.readNext(); - for (; line != null; line = reader.readNext()) { - testExpression(line, new DummyExpression()); - testExpression(line, new Expression1()); - testExpression(line, new Expression2()); - testExpression(line, new Expression3()); - - final String dec = line[0].toUpperCase(); - final String hex = "0x:" + line[1].toUpperCase(); - final String bin = "0b:" + line[2].toUpperCase(); - - final List input = new ArrayList(); - input.add(dec); - input.add(hex); - input.add(bin); - - //System.out.println("Dec: " + dec); - //System.out.println("Hex: " + hex); - //System.out.println("Bin: " + bin); - - final ExpressionGeneratorWithInput eg = new ExpressionGeneratorWithInput(input, 20); - final List expressions = eg.generate(); - - final String decExpression = expressions.get(0); - final String hexExpression = expressions.get(1); - final String binExpression = expressions.get(2); - - //System.out.println("Dec expression: " + decExpression); - //System.out.println("Hex expression: " + hexExpression); - //System.out.println("Bin expression: " + binExpression); - - final String decResult = Expression.valueOf(decExpression).numeric().toString(); - //System.out.println("Dec result: " + decResult); - - final String hexResult = Expression.valueOf(hexExpression).numeric().toString(); - //System.out.println("Hex result: " + hexResult); - - final String binResult = Expression.valueOf(binExpression).numeric().toString(); - //System.out.println("Bin result: " + binResult); - - Assert.assertEquals("dec-hex: " + decExpression + " : " + hexExpression, decResult, hexResult); - Assert.assertEquals("dec-bin: " + decExpression + " : " + binExpression, decResult, binResult); - } - } finally { - if (reader != null) { - reader.close(); - } - } - } - - public static void testExpression(@NotNull String[] line, @NotNull Converter converter) throws ParseException, CalculatorEvalException, CalculatorParseException { - final String dec = line[0].toUpperCase(); - final String hex = "0x:" + line[1].toUpperCase(); - final String bin = "0b:" + line[2].toUpperCase(); - - final String decExpression = converter.convert(dec); - final String decResult = CalculatorEngine.instance.evaluate(JsclOperation.numeric, decExpression).getResult(); - final String hexExpression = converter.convert(hex); - final String hexResult = CalculatorEngine.instance.evaluate(JsclOperation.numeric, hexExpression).getResult(); - final String binExpression = converter.convert(bin); - final String binResult = CalculatorEngine.instance.evaluate(JsclOperation.numeric, binExpression).getResult(); - - Assert.assertEquals("dec-hex: " + decExpression + " : " + hexExpression, decResult, hexResult); - Assert.assertEquals("dec-bin: " + decExpression + " : " + binExpression, decResult, binResult); - } - - private static class DummyExpression implements Converter { - - @NotNull - @Override - public String convert(@NotNull String s) { - return s; - } - } - - private static class Expression1 implements Converter { - - @NotNull - @Override - public String convert(@NotNull String s) { - return s + "*" + s; - } - } - - private static class Expression2 implements Converter { - - @NotNull - @Override - public String convert(@NotNull String s) { - return s + "*" + s + " * sin(" + s + ") - 0b:1101"; - } - } - - private static class Expression3 implements Converter { - - @NotNull - @Override - public String convert(@NotNull String s) { - return s + "*" + s + " * sin(" + s + ") - 0b:1101 + √(" + s + ") + exp ( " + s + ")"; - } - } -} +package org.solovyev.android.calculator.model; + +import au.com.bytecode.opencsv.CSVReader; +import jscl.JsclMathEngine; +import jscl.MathEngine; +import jscl.math.Expression; +import jscl.text.ParseException; +import jscl.util.ExpressionGeneratorWithInput; +import org.jetbrains.annotations.NotNull; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.solovyev.android.calculator.CalculatorEvalException; +import org.solovyev.android.calculator.CalculatorParseException; +import org.solovyev.android.calculator.jscl.JsclOperation; +import org.solovyev.common.Converter; + +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + +/** + * User: serso + * Date: 12/14/11 + * Time: 4:16 PM + */ +public class NumeralBaseTest { + + @BeforeClass + public static void setUp() throws Exception { + CalculatorEngine.instance.init(null, null); + CalculatorEngine.instance.setPrecision(3); + CalculatorEngine.instance.setThreadKiller(new CalculatorEngine.ThreadKillerImpl()); + } + + @Test + public void testConversion() throws Exception { + CSVReader reader = null; + try { + final MathEngine me = JsclMathEngine.instance; + + reader = new CSVReader(new InputStreamReader(NumeralBaseTest.class.getResourceAsStream("/org/solovyev/android/calculator/model/nb_table.csv")), '\t'); + + // skip first line + reader.readNext(); + + String[] line = reader.readNext(); + for (; line != null; line = reader.readNext()) { + testExpression(line, new DummyExpression()); + testExpression(line, new Expression1()); + testExpression(line, new Expression2()); + testExpression(line, new Expression3()); + + final String dec = line[0].toUpperCase(); + final String hex = "0x:" + line[1].toUpperCase(); + final String bin = "0b:" + line[2].toUpperCase(); + + final List input = new ArrayList(); + input.add(dec); + input.add(hex); + input.add(bin); + + //System.out.println("Dec: " + dec); + //System.out.println("Hex: " + hex); + //System.out.println("Bin: " + bin); + + final ExpressionGeneratorWithInput eg = new ExpressionGeneratorWithInput(input, 20); + final List expressions = eg.generate(); + + final String decExpression = expressions.get(0); + final String hexExpression = expressions.get(1); + final String binExpression = expressions.get(2); + + //System.out.println("Dec expression: " + decExpression); + //System.out.println("Hex expression: " + hexExpression); + //System.out.println("Bin expression: " + binExpression); + + final String decResult = Expression.valueOf(decExpression).numeric().toString(); + //System.out.println("Dec result: " + decResult); + + final String hexResult = Expression.valueOf(hexExpression).numeric().toString(); + //System.out.println("Hex result: " + hexResult); + + final String binResult = Expression.valueOf(binExpression).numeric().toString(); + //System.out.println("Bin result: " + binResult); + + Assert.assertEquals("dec-hex: " + decExpression + " : " + hexExpression, decResult, hexResult); + Assert.assertEquals("dec-bin: " + decExpression + " : " + binExpression, decResult, binResult); + } + } finally { + if (reader != null) { + reader.close(); + } + } + } + + public static void testExpression(@NotNull String[] line, @NotNull Converter converter) throws ParseException, CalculatorEvalException, CalculatorParseException { + final String dec = line[0].toUpperCase(); + final String hex = "0x:" + line[1].toUpperCase(); + final String bin = "0b:" + line[2].toUpperCase(); + + final String decExpression = converter.convert(dec); + final String decResult = CalculatorEngine.instance.evaluate(JsclOperation.numeric, decExpression).getStringResult(); + final String hexExpression = converter.convert(hex); + final String hexResult = CalculatorEngine.instance.evaluate(JsclOperation.numeric, hexExpression).getStringResult(); + final String binExpression = converter.convert(bin); + final String binResult = CalculatorEngine.instance.evaluate(JsclOperation.numeric, binExpression).getStringResult(); + + Assert.assertEquals("dec-hex: " + decExpression + " : " + hexExpression, decResult, hexResult); + Assert.assertEquals("dec-bin: " + decExpression + " : " + binExpression, decResult, binResult); + } + + private static class DummyExpression implements Converter { + + @NotNull + @Override + public String convert(@NotNull String s) { + return s; + } + } + + private static class Expression1 implements Converter { + + @NotNull + @Override + public String convert(@NotNull String s) { + return s + "*" + s; + } + } + + private static class Expression2 implements Converter { + + @NotNull + @Override + public String convert(@NotNull String s) { + return s + "*" + s + " * sin(" + s + ") - 0b:1101"; + } + } + + private static class Expression3 implements Converter { + + @NotNull + @Override + public String convert(@NotNull String s) { + return s + "*" + s + " * sin(" + s + ") - 0b:1101 + √(" + s + ") + exp ( " + s + ")"; + } + } +} From b3cc081f47b469b0a42a04d5fecf98ed57f715a8 Mon Sep 17 00:00:00 2001 From: Sergey Solovyev Date: Fri, 21 Sep 2012 00:36:52 +0400 Subject: [PATCH 6/6] Calculator display changes --- .../android/calculator/Calculator.java | 5 + .../CalculatorEvaluationEventDataImpl.java | 4 +- .../calculator/CalculatorEventDataId.java | 3 +- .../calculator/CalculatorEventDataIdImpl.java | 20 +- .../calculator/CalculatorEventDataImpl.java | 4 +- .../calculator/CalculatorEventType.java | 14 +- .../android/calculator/CalculatorImpl.java | 108 +++- calculatorpp/project.properties | 39 +- .../AndroidCalculatorDisplayView.java | 99 +--- .../android/calculator/CalculatorModel.java | 7 +- .../calculator/ConversionMenuItem.java | 63 +++ .../view/UnitConverterViewBuilder.java | 483 ++++++++---------- 12 files changed, 446 insertions(+), 403 deletions(-) create mode 100644 calculatorpp/src/main/java/org/solovyev/android/calculator/ConversionMenuItem.java diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/Calculator.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/Calculator.java index ee0e756b..433c2163 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/Calculator.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/Calculator.java @@ -1,5 +1,7 @@ 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; @@ -22,4 +24,7 @@ public interface Calculator extends CalculatorEventContainer { 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/CalculatorEvaluationEventDataImpl.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEvaluationEventDataImpl.java index 677d6eca..9df965a0 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEvaluationEventDataImpl.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEvaluationEventDataImpl.java @@ -47,8 +47,8 @@ public class CalculatorEvaluationEventDataImpl implements CalculatorEvaluationEv @Override @Nullable - public Long getCalculationId() { - return calculatorEventData.getCalculationId(); + public Long getSequenceId() { + return calculatorEventData.getSequenceId(); } @Override diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataId.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataId.java index 61684be3..783d82d6 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataId.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataId.java @@ -13,8 +13,9 @@ public interface CalculatorEventDataId { // the higher id => the later event long getEventId(); + // the higher id => the later event @Nullable - Long getCalculationId(); + 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 index 6992bef6..ad2e2ca9 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataIdImpl.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataIdImpl.java @@ -13,18 +13,16 @@ class CalculatorEventDataIdImpl implements CalculatorEventDataId { private final long eventId; @Nullable - private final Long calculationId; + private final Long sequenceId; - private CalculatorEventDataIdImpl(long id, - @Nullable Long calculationId) { + private CalculatorEventDataIdImpl(long id, @Nullable Long sequenceId) { this.eventId = id; - this.calculationId = calculationId; + this.sequenceId = sequenceId; } @NotNull - static CalculatorEventDataId newInstance(long id, - @Nullable Long calculationId) { - return new CalculatorEventDataIdImpl(id, calculationId); + static CalculatorEventDataId newInstance(long id, @Nullable Long sequenceId) { + return new CalculatorEventDataIdImpl(id, sequenceId); } @Override @@ -34,8 +32,8 @@ class CalculatorEventDataIdImpl implements CalculatorEventDataId { @Nullable @Override - public Long getCalculationId() { - return this.calculationId; + public Long getSequenceId() { + return this.sequenceId; } @Override @@ -51,7 +49,7 @@ class CalculatorEventDataIdImpl implements CalculatorEventDataId { CalculatorEventDataIdImpl that = (CalculatorEventDataIdImpl) o; if (eventId != that.eventId) return false; - if (calculationId != null ? !calculationId.equals(that.calculationId) : that.calculationId != null) + if (sequenceId != null ? !sequenceId.equals(that.sequenceId) : that.sequenceId != null) return false; return true; @@ -60,7 +58,7 @@ class CalculatorEventDataIdImpl implements CalculatorEventDataId { @Override public int hashCode() { int result = (int) (eventId ^ (eventId >>> 32)); - result = 31 * result + (calculationId != null ? calculationId.hashCode() : 0); + 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 index ad9d19b8..34b37d2b 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataImpl.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventDataImpl.java @@ -29,8 +29,8 @@ class CalculatorEventDataImpl implements CalculatorEventData { @Override @Nullable - public Long getCalculationId() { - return calculatorEventDataId.getCalculationId(); + public Long getSequenceId() { + return calculatorEventDataId.getSequenceId(); } @Override diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventType.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventType.java index c06da282..1fd179b6 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventType.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorEventType.java @@ -28,7 +28,19 @@ public enum CalculatorEventType { calculation_finished, // @NotNull org.solovyev.android.calculator.CalculatorFailure - calculation_failed; + calculation_failed, + + /* + ********************************************************************** + * + * CONVERSION + * + ********************************************************************** + */ + conversion_started, + + // @NotNull String conversion result + conversion_finished; public boolean isOfType(@NotNull CalculatorEventType... types) { for (CalculatorEventType type : types) { diff --git a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorImpl.java b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorImpl.java index fab2b16a..3db62bfb 100644 --- a/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorImpl.java +++ b/calculatorpp-core/src/main/java/org/solovyev/android/calculator/CalculatorImpl.java @@ -1,6 +1,7 @@ package org.solovyev.android.calculator; import jscl.AbstractJsclArithmeticException; +import jscl.NumeralBase; import jscl.NumeralBaseException; import jscl.math.Generic; import jscl.text.ParseInterruptedException; @@ -10,6 +11,10 @@ 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; @@ -43,6 +48,32 @@ public class CalculatorImpl implements Calculator { 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(); @@ -50,9 +81,9 @@ public class CalculatorImpl implements Calculator { } @NotNull - private CalculatorEventDataId nextEventDataId(@NotNull Long calculationId) { + private CalculatorEventDataId nextEventDataId(@NotNull Long sequenceId) { long eventId = counter.incrementAndGet(); - return CalculatorEventDataIdImpl.newInstance(eventId, calculationId); + return CalculatorEventDataIdImpl.newInstance(eventId, sequenceId); } /* @@ -86,14 +117,56 @@ public class CalculatorImpl implements Calculator { threadPoolExecutor.execute(new Runnable() { @Override public void run() { - CalculatorImpl.this.evaluate(eventDataId.getCalculationId(), operation, expression, mr); + CalculatorImpl.this.evaluate(eventDataId.getSequenceId(), operation, expression, mr); } }); return eventDataId; } - private void evaluate(@NotNull Long calculationId, + @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) { @@ -101,7 +174,7 @@ public class CalculatorImpl implements Calculator { PreparedExpression preparedExpression = null; - fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_started, new CalculatorInputImpl(expression, operation)); + fireCalculatorEvent(newCalculationEventData(operation, expression, sequenceId), CalculatorEventType.calculation_started, new CalculatorInputImpl(expression, operation)); try { preparedExpression = preprocessor.process(expression); @@ -116,27 +189,27 @@ public class CalculatorImpl implements Calculator { result.toString(); final CalculatorOutputImpl data = new CalculatorOutputImpl(operation.getFromProcessor().process(result), operation, result); - fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_result, data); + fireCalculatorEvent(newCalculationEventData(operation, expression, sequenceId), CalculatorEventType.calculation_result, data); } catch (AbstractJsclArithmeticException e) { - handleException(calculationId, operation, expression, mr, new CalculatorEvalException(e, e, jsclExpression)); + handleException(sequenceId, operation, expression, mr, new CalculatorEvalException(e, e, jsclExpression)); } } catch (ArithmeticException e) { - handleException(calculationId, operation, expression, mr, preparedExpression, new CalculatorParseException(expression, new CalculatorMessage(CalculatorMessages.msg_001, MessageType.error, e.getMessage()))); + handleException(sequenceId, operation, expression, mr, preparedExpression, new CalculatorParseException(expression, new CalculatorMessage(CalculatorMessages.msg_001, MessageType.error, e.getMessage()))); } catch (StackOverflowError e) { - handleException(calculationId, operation, expression, mr, preparedExpression, new CalculatorParseException(expression, new CalculatorMessage(CalculatorMessages.msg_002, MessageType.error))); + handleException(sequenceId, operation, expression, mr, preparedExpression, new CalculatorParseException(expression, new CalculatorMessage(CalculatorMessages.msg_002, MessageType.error))); } catch (jscl.text.ParseException e) { - handleException(calculationId, operation, expression, mr, preparedExpression, new CalculatorParseException(e)); + handleException(sequenceId, operation, expression, mr, preparedExpression, new CalculatorParseException(e)); } catch (ParseInterruptedException e) { // do nothing - we ourselves interrupt the calculations - fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_cancelled, null); + fireCalculatorEvent(newCalculationEventData(operation, expression, sequenceId), CalculatorEventType.calculation_cancelled, null); } catch (CalculatorParseException e) { - handleException(calculationId, operation, expression, mr, preparedExpression, e); + handleException(sequenceId, operation, expression, mr, preparedExpression, e); } finally { - fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_finished, null); + fireCalculatorEvent(newCalculationEventData(operation, expression, sequenceId), CalculatorEventType.calculation_finished, null); } } } @@ -206,4 +279,13 @@ public class CalculatorImpl implements Calculator { 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/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/src/main/java/org/solovyev/android/calculator/AndroidCalculatorDisplayView.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/AndroidCalculatorDisplayView.java index e1420698..d8db12c7 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/AndroidCalculatorDisplayView.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/AndroidCalculatorDisplayView.java @@ -10,7 +10,6 @@ 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; @@ -20,12 +19,9 @@ import org.solovyev.android.calculator.model.CalculatorEngine; import org.solovyev.android.calculator.text.TextProcessor; import org.solovyev.android.calculator.view.NumeralBaseConverterDialog; import org.solovyev.android.calculator.view.TextHighlighter; -import org.solovyev.android.calculator.view.UnitConverterViewBuilder; -import org.solovyev.android.menu.AMenuItem; import org.solovyev.android.menu.LabeledMenuItem; import org.solovyev.android.view.AutoResizeTextView; import org.solovyev.common.collections.CollectionsUtils; -import org.solovyev.common.text.StringUtils; import java.util.HashSet; import java.util.Set; @@ -37,95 +33,18 @@ import java.util.Set; */ public class AndroidCalculatorDisplayView extends AutoResizeTextView implements CalculatorDisplayView { - private static enum ConversionMenuItem implements AMenuItem { - convert_to_bin(NumeralBase.bin), - convert_to_dec(NumeralBase.dec), - convert_to_hex(NumeralBase.hex); - - @NotNull - private final NumeralBase toNumeralBase; - - private ConversionMenuItem(@NotNull NumeralBase toNumeralBase) { - this.toNumeralBase = toNumeralBase; - } - - protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { - boolean result = false; - - if (operation == JsclOperation.numeric) { - if (generic.getConstants().isEmpty()) { - try { - convert(generic); - - // conversion possible => return true - result = true; - - } catch (UnitConverterViewBuilder.ConversionException e) { - // conversion is not possible => return false - } - } - } - - return result; - } - - @Override - public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) { - final NumeralBase fromNumeralBase = CalculatorEngine.instance.getEngine().getNumeralBase(); - - final Generic lastResult = CalculatorLocatorImpl.getInstance().getCalculatorDisplay().getViewState().getResult(); - - if (lastResult != null) { - String to; - try { - to = convert(lastResult); - - // add prefix - if (fromNumeralBase != toNumeralBase) { - to = toNumeralBase.getJsclPrefix() + to; - } - } catch (UnitConverterViewBuilder.ConversionException e) { - to = context.getString(R.string.c_error); - } - - data.setText(to); - //data.redraw(); - } - } - - @NotNull - private String convert(@NotNull Generic generic) throws UnitConverterViewBuilder.ConversionException { - final NumeralBase fromNumeralBase = CalculatorEngine.instance.getEngine().getNumeralBase(); - - if (fromNumeralBase != toNumeralBase) { - String from = generic.toString(); - if (!StringUtils.isEmpty(from)) { - try { - from = ToJsclTextProcessor.getInstance().process(from).getExpression(); - } catch (CalculatorParseException e) { - // ok, problems while processing occurred - } - } - - return UnitConverterViewBuilder.doConversion(AndroidNumeralBase.getConverter(), from, AndroidNumeralBase.valueOf(fromNumeralBase), AndroidNumeralBase.valueOf(toNumeralBase)); - } else { - return generic.toString(); - } - } - } - - public static enum MenuItem implements LabeledMenuItem { + public static enum MenuItem implements LabeledMenuItem { copy(R.string.c_copy) { @Override - public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) { + 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 CalculatorDisplayView data, @NotNull Context context) { + public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) { ConversionMenuItem.convert_to_bin.onClick(data, context); } @@ -137,7 +56,7 @@ public class AndroidCalculatorDisplayView extends AutoResizeTextView implements convert_to_dec(R.string.convert_to_dec) { @Override - public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) { + public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) { ConversionMenuItem.convert_to_dec.onClick(data, context); } @@ -149,7 +68,7 @@ public class AndroidCalculatorDisplayView extends AutoResizeTextView implements convert_to_hex(R.string.convert_to_hex) { @Override - public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) { + public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) { ConversionMenuItem.convert_to_hex.onClick(data, context); } @@ -161,8 +80,8 @@ public class AndroidCalculatorDisplayView extends AutoResizeTextView implements convert(R.string.c_convert) { @Override - public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) { - final Generic result = data.getState().getResult(); + public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) { + final Generic result = data.getResult(); if (result != null) { new NumeralBaseConverterDialog(result.toString()).show(context); } @@ -176,8 +95,8 @@ public class AndroidCalculatorDisplayView extends AutoResizeTextView implements plot(R.string.c_plot) { @Override - public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) { - final Generic generic = data.getState().getResult(); + public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) { + final Generic generic = data.getResult(); assert generic != null; final Constant constant = CollectionsUtils.getFirstCollectionElement(getNotSystemConstants(generic)); 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 d3268689..e43e9131 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorModel.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorModel.java @@ -97,10 +97,9 @@ 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/view/UnitConverterViewBuilder.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/view/UnitConverterViewBuilder.java index d75c7240..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 @@ -1,259 +1,224 @@ -package org.solovyev.android.calculator.view; - -import android.app.Activity; -import android.content.Context; -import android.text.ClipboardManager; -import android.text.Editable; -import android.text.TextWatcher; -import android.view.View; -import android.view.ViewGroup; -import android.widget.*; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -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; - -/** - * User: serso - * Date: 4/20/12 - * Time: 4:50 PM - */ -public class UnitConverterViewBuilder implements ViewBuilder { - - @NotNull - private List> fromUnitTypes = Collections.emptyList(); - - @NotNull - private List> toUnitTypes = Collections.emptyList(); - - @Nullable - private Unit fromValue; - - @NotNull - private UnitConverter converter = UnitConverter.Dummy.getInstance(); - - @Nullable - private View.OnClickListener okButtonOnClickListener; - - @Nullable - private CustomButtonData customButtonData; - - public void setFromUnitTypes(@NotNull List> fromUnitTypes) { - this.fromUnitTypes = fromUnitTypes; - } - - public void setToUnitTypes(@NotNull List> toUnitTypes) { - this.toUnitTypes = toUnitTypes; - } - - public void setFromValue(@Nullable Unit fromValue) { - this.fromValue = fromValue; - } - - public void setConverter(@NotNull UnitConverter converter) { - this.converter = converter; - } - - public void setOkButtonOnClickListener(@Nullable View.OnClickListener okButtonOnClickListener) { - this.okButtonOnClickListener = okButtonOnClickListener; - } - - public void setCustomButtonData(@Nullable CustomButtonData customButtonData) { - this.customButtonData = customButtonData; - } - - @NotNull - @Override - public View build(@NotNull final Context context) { - final View main = ViewFromLayoutBuilder.newInstance(R.layout.unit_converter).build(context); - - final Spinner fromSpinner = (Spinner) main.findViewById(R.id.unit_types_from); - final EditText fromEditText = (EditText) main.findViewById(R.id.units_from); - fromEditText.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - - @Override - public void afterTextChanged(Editable s) { - doConversion(main, context, UnitConverterViewBuilder.this.converter); - } - }); - - fillSpinner(main, context, R.id.unit_types_from, fromUnitTypes); - fillSpinner(main, context, R.id.unit_types_to, toUnitTypes); - - if (fromValue != null) { - fromEditText.setText(fromValue.getValue()); - - int i = fromUnitTypes.indexOf(fromValue.getUnitType()); - if ( i >= 0 ) { - fromSpinner.setSelection(i); - } - } - - final Button copyButton = (Button) main.findViewById(R.id.unit_converter_copy_button); - copyButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - final EditText toEditText = (EditText) main.findViewById(R.id.units_to); - - final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE); - clipboard.setText(toEditText.getText().toString()); - Toast.makeText(context, context.getText(R.string.c_result_copied), Toast.LENGTH_SHORT).show(); - } - }); - - final Button okButton = (Button) main.findViewById(R.id.unit_converter_ok_button); - if ( okButtonOnClickListener == null ) { - ((ViewGroup) okButton.getParent()).removeView(okButton); - } else { - okButton.setOnClickListener(this.okButtonOnClickListener); - } - - final Button customButton = (Button) main.findViewById(R.id.unit_converter_custom_button); - if ( customButtonData == null ) { - ((ViewGroup) customButton.getParent()).removeView(customButton); - } else { - customButton.setText(customButtonData.text); - customButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - customButtonData.clickListener.onClick(getFromUnit(main), getToUnit(main)); - } - }); - } - - - - return main; - } - - private void fillSpinner(@NotNull final View main, - @NotNull final Context context, - final int spinnerId, - @NotNull List> unitTypes) { - final Spinner spinner = (Spinner) main.findViewById(spinnerId); - - final ArrayAdapter> adapter = new ArrayAdapter>(context, android.R.layout.simple_spinner_item); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - for (UnitType fromUnitType : unitTypes) { - adapter.add(fromUnitType); - } - spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - doConversion(main, context, UnitConverterViewBuilder.this.converter); - } - - @Override - public void onNothingSelected(AdapterView parent) { - } - }); - spinner.setAdapter(adapter); - } - - private static void doConversion(@NotNull View main, @NotNull Context context, @NotNull UnitConverter converter) { - final EditText fromEditText = (EditText) main.findViewById(R.id.units_from); - - final EditText toEditText = (EditText) main.findViewById(R.id.units_to); - - final String from = fromEditText.getText().toString(); - try { - toEditText.setText(doConversion(converter, from, getFromUnitType(main), getToUnitType(main))); - } catch (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); - return UnitImpl.newInstance(toUnits.getText().toString(), getToUnitType(main)); - } - - @NotNull - private static UnitType getToUnitType(@NotNull View main) { - final Spinner toSpinner = (Spinner) main.findViewById(R.id.unit_types_to); - return (UnitType) toSpinner.getSelectedItem(); - } - - @NotNull - private static Unit getFromUnit(@NotNull View main) { - final EditText fromUnits = (EditText) main.findViewById(R.id.units_from); - return UnitImpl.newInstance(fromUnits.getText().toString(), getFromUnitType(main)); - } - - @NotNull - private static UnitType getFromUnitType(@NotNull View main) { - final Spinner fromSpinner = (Spinner) main.findViewById(R.id.unit_types_from); - return (UnitType) fromSpinner.getSelectedItem(); - } - - public static class CustomButtonData { - - @NotNull - private String text; - - @NotNull - private CustomButtonOnClickListener clickListener; - - - public CustomButtonData(@NotNull String text, @NotNull CustomButtonOnClickListener clickListener) { - this.text = text; - this.clickListener = clickListener; - } - } - - public static interface CustomButtonOnClickListener { - void onClick(@NotNull Unit fromUnits, @NotNull Unit toUnits); - } -} +package org.solovyev.android.calculator.view; + +import android.app.Activity; +import android.content.Context; +import android.text.ClipboardManager; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.View; +import android.view.ViewGroup; +import android.widget.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +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 java.util.Collections; +import java.util.List; + +/** + * User: serso + * Date: 4/20/12 + * Time: 4:50 PM + */ +public class UnitConverterViewBuilder implements ViewBuilder { + + @NotNull + private List> fromUnitTypes = Collections.emptyList(); + + @NotNull + private List> toUnitTypes = Collections.emptyList(); + + @Nullable + private Unit fromValue; + + @NotNull + private UnitConverter converter = UnitConverter.Dummy.getInstance(); + + @Nullable + private View.OnClickListener okButtonOnClickListener; + + @Nullable + private CustomButtonData customButtonData; + + public void setFromUnitTypes(@NotNull List> fromUnitTypes) { + this.fromUnitTypes = fromUnitTypes; + } + + public void setToUnitTypes(@NotNull List> toUnitTypes) { + this.toUnitTypes = toUnitTypes; + } + + public void setFromValue(@Nullable Unit fromValue) { + this.fromValue = fromValue; + } + + public void setConverter(@NotNull UnitConverter converter) { + this.converter = converter; + } + + public void setOkButtonOnClickListener(@Nullable View.OnClickListener okButtonOnClickListener) { + this.okButtonOnClickListener = okButtonOnClickListener; + } + + public void setCustomButtonData(@Nullable CustomButtonData customButtonData) { + this.customButtonData = customButtonData; + } + + @NotNull + @Override + public View build(@NotNull final Context context) { + final View main = ViewFromLayoutBuilder.newInstance(R.layout.unit_converter).build(context); + + final Spinner fromSpinner = (Spinner) main.findViewById(R.id.unit_types_from); + final EditText fromEditText = (EditText) main.findViewById(R.id.units_from); + fromEditText.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + + @Override + public void afterTextChanged(Editable s) { + doConversion(main, context, UnitConverterViewBuilder.this.converter); + } + }); + + fillSpinner(main, context, R.id.unit_types_from, fromUnitTypes); + fillSpinner(main, context, R.id.unit_types_to, toUnitTypes); + + if (fromValue != null) { + fromEditText.setText(fromValue.getValue()); + + int i = fromUnitTypes.indexOf(fromValue.getUnitType()); + if ( i >= 0 ) { + fromSpinner.setSelection(i); + } + } + + final Button copyButton = (Button) main.findViewById(R.id.unit_converter_copy_button); + copyButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + final EditText toEditText = (EditText) main.findViewById(R.id.units_to); + + final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE); + clipboard.setText(toEditText.getText().toString()); + Toast.makeText(context, context.getText(R.string.c_result_copied), Toast.LENGTH_SHORT).show(); + } + }); + + final Button okButton = (Button) main.findViewById(R.id.unit_converter_ok_button); + if ( okButtonOnClickListener == null ) { + ((ViewGroup) okButton.getParent()).removeView(okButton); + } else { + okButton.setOnClickListener(this.okButtonOnClickListener); + } + + final Button customButton = (Button) main.findViewById(R.id.unit_converter_custom_button); + if ( customButtonData == null ) { + ((ViewGroup) customButton.getParent()).removeView(customButton); + } else { + customButton.setText(customButtonData.text); + customButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + customButtonData.clickListener.onClick(getFromUnit(main), getToUnit(main)); + } + }); + } + + + + return main; + } + + private void fillSpinner(@NotNull final View main, + @NotNull final Context context, + final int spinnerId, + @NotNull List> unitTypes) { + final Spinner spinner = (Spinner) main.findViewById(spinnerId); + + final ArrayAdapter> adapter = new ArrayAdapter>(context, android.R.layout.simple_spinner_item); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + for (UnitType fromUnitType : unitTypes) { + adapter.add(fromUnitType); + } + spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + doConversion(main, context, UnitConverterViewBuilder.this.converter); + } + + @Override + public void onNothingSelected(AdapterView parent) { + } + }); + spinner.setAdapter(adapter); + } + + private static void doConversion(@NotNull View main, @NotNull Context context, @NotNull UnitConverter converter) { + final EditText fromEditText = (EditText) main.findViewById(R.id.units_from); + + final EditText toEditText = (EditText) main.findViewById(R.id.units_to); + + final String from = fromEditText.getText().toString(); + try { + toEditText.setText(CalculatorImpl.doConversion(converter, from, getFromUnitType(main), getToUnitType(main))); + } catch (CalculatorImpl.ConversionException e) { + toEditText.setText(context.getString(R.string.c_error)); + } + } + + @NotNull + private static Unit getToUnit(@NotNull View main) { + final EditText toUnits = (EditText) main.findViewById(R.id.units_to); + return UnitImpl.newInstance(toUnits.getText().toString(), getToUnitType(main)); + } + + @NotNull + private static UnitType getToUnitType(@NotNull View main) { + final Spinner toSpinner = (Spinner) main.findViewById(R.id.unit_types_to); + return (UnitType) toSpinner.getSelectedItem(); + } + + @NotNull + private static Unit getFromUnit(@NotNull View main) { + final EditText fromUnits = (EditText) main.findViewById(R.id.units_from); + return UnitImpl.newInstance(fromUnits.getText().toString(), getFromUnitType(main)); + } + + @NotNull + private static UnitType getFromUnitType(@NotNull View main) { + final Spinner fromSpinner = (Spinner) main.findViewById(R.id.unit_types_from); + return (UnitType) fromSpinner.getSelectedItem(); + } + + public static class CustomButtonData { + + @NotNull + private String text; + + @NotNull + private CustomButtonOnClickListener clickListener; + + + public CustomButtonData(@NotNull String text, @NotNull CustomButtonOnClickListener clickListener) { + this.text = text; + this.clickListener = clickListener; + } + } + + public static interface CustomButtonOnClickListener { + void onClick(@NotNull Unit fromUnits, @NotNull Unit toUnits); + } +}