This commit is contained in:
serso 2011-11-11 00:13:41 +04:00
parent 7ccd94d1ca
commit 046114b42d
10 changed files with 401 additions and 126 deletions

View File

@ -39,7 +39,6 @@
<EditText a:id="@+id/var_edit_value"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:inputType="numberDecimal"
style="@style/default_text_size">
</EditText>

View File

@ -26,6 +26,7 @@ import org.solovyev.common.utils.StringUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
/**
@ -59,7 +60,7 @@ public class CalculatorVarsActivity extends ListActivity {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
final Var var = (Var) parent.getItemAtPosition(position);
createEditVariableDialog(var, var.getName(), var.getValue(), var.getDescription());
createEditVariableDialog(var, var.getName(), StringUtils.getNotEmpty(var.getValue(), ""), var.getDescription());
return true;
}
});
@ -81,6 +82,8 @@ public class CalculatorVarsActivity extends ListActivity {
}
});
sort();
final Intent intent = getIntent();
if ( intent != null ) {
final String varValue = intent.getStringExtra(CREATE_VAR_EXTRA_STRING);
@ -206,8 +209,17 @@ public class CalculatorVarsActivity extends ListActivity {
final AndroidVarsRegistry varsRegistry = CalculatorEngine.instance.getVarsRegister();
if (!StringUtils.isEmpty(name)) {
boolean canBeSaved = false;
final Var varFromRegister = varsRegistry.get(name);
if (varFromRegister == null || varFromRegister == editedInstance) {
if ( varFromRegister == null ) {
canBeSaved = true;
} else if ( editedInstance != null && varFromRegister.getId().equals(editedInstance.getId()) ) {
canBeSaved = true;
}
if (canBeSaved) {
final MathType.Result mathType = MathType.getType(name, 0);
if (mathType.getMathType() == MathType.text || mathType.getMathType() == MathType.constant) {
@ -220,9 +232,9 @@ public class CalculatorVarsActivity extends ListActivity {
error = null;
} else {
// value is not empty => must be a number
boolean correctDouble = isValid(value);
boolean valid = isValid(value);
if (correctDouble) {
if (valid) {
varBuilder.setName(name);
varBuilder.setDescription(description);
varBuilder.setValue(value);
@ -248,25 +260,33 @@ public class CalculatorVarsActivity extends ListActivity {
if ( editedInstance == null ) {
CalculatorVarsActivity.this.adapter.add(varsRegistry.add(null, varBuilder));
} else {
varsRegistry.add(editedInstance.getName(), varBuilder);
final Var newInstance = varsRegistry.add(editedInstance.getName(), varBuilder);
CalculatorVarsActivity.this.adapter.remove(editedInstance);
CalculatorVarsActivity.this.adapter.add(newInstance);
}
varsRegistry.save(CalculatorVarsActivity.this);
CalculatorVarsActivity.this.adapter.notifyDataSetChanged();
sort();
}
}
}
}
public static boolean isValid(@NotNull String value) {
boolean valid = true;
try {
Double.valueOf(value);
} catch (NumberFormatException e) {
valid = false;
private void sort() {
CalculatorVarsActivity.this.adapter.sort(new Comparator<Var>() {
@Override
public int compare(Var var, Var var1) {
return var.getName().compareTo(var1.getName());
}
return valid;
});
CalculatorVarsActivity.this.adapter.notifyDataSetChanged();
}
public static boolean isValid(@NotNull String value) {
// now every string might be constant
return true;
}
private class VarsArrayAdapter extends ArrayAdapter<Var> {

View File

@ -71,7 +71,7 @@ public class AndroidFunctionsRegistryImpl implements AndroidFunctionsRegistry {
}
@Override
public Function add(@Nullable String name, @NotNull IBuilder<Function> IBuilder) {
public Function add(@Nullable String name, @NotNull IBuilder<? extends Function> IBuilder) {
return functionsRegistry.add(name, IBuilder);
}
@ -95,4 +95,9 @@ public class AndroidFunctionsRegistryImpl implements AndroidFunctionsRegistry {
public Function get(@NotNull String name) {
return functionsRegistry.get(name);
}
@Override
public Function getById(@NotNull Integer id) {
return functionsRegistry.getById(id);
}
}

View File

@ -9,43 +9,39 @@ package org.solovyev.android.calculator.model;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import jscl.math.function.IConstant;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;
import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.math.MathType;
import org.solovyev.common.math.AbstractMathRegistry;
import org.solovyev.common.definitions.IBuilder;
import org.solovyev.common.math.MathRegistry;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
/**
* User: serso
* Date: 9/29/11
* Time: 4:57 PM
*/
class AndroidVarsRegistryImpl extends AbstractMathRegistry<Var> implements AndroidVarsRegistry {
class AndroidVarsRegistryImpl implements AndroidVarsRegistry {
protected AndroidVarsRegistryImpl() {
@NotNull
private final MathRegistry<IConstant> mathRegistry;
protected AndroidVarsRegistryImpl(@NotNull MathRegistry<IConstant> mathRegistry) {
this.mathRegistry = mathRegistry;
}
/* public void merge(@NotNull final List<Var> varsParam) {
final Set<Var> result = new HashSet<Var>(varsParam);
for (Var systemVar : systemEntities) {
if (!result.contains(systemVar)) {
result.add(systemVar);
}
}
entities.clear();
entities.addAll(result);
}*/
private boolean initialized = false;
public synchronized void init(@Nullable Context context, @Nullable SharedPreferences preferences) {
this.entities.clear();
this.systemEntities.clear();
if (!initialized) {
if (context != null && preferences != null) {
final String value = preferences.getString(context.getString(R.string.p_calc_vars), null);
@ -53,7 +49,11 @@ class AndroidVarsRegistryImpl extends AbstractMathRegistry<Var> implements Andro
final Serializer serializer = new Persister();
try {
final Vars vars = serializer.read(Vars.class, value);
this.entities.addAll(vars.getVars());
for (Var var : vars.getVars()) {
if (!contains(var.getName())) {
add(null, new Var.Builder(var));
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
@ -103,13 +103,13 @@ class AndroidVarsRegistryImpl extends AbstractMathRegistry<Var> implements Andro
builder.setDescription(context.getString(varDescription));
}
final Var systemVar = builder.create();
if (!contains(systemVarName)) {
add(null, builder);
}
}
}
systemEntities.add(systemVar);
if (!entities.contains(systemVar)) {
entities.add(systemVar);
}
}
initialized = true;
/*Log.d(AndroidVarsRegistry.class.getName(), vars.size() + " variables registered!");
for (Var var : vars) {
@ -123,7 +123,7 @@ class AndroidVarsRegistryImpl extends AbstractMathRegistry<Var> implements Andro
final SharedPreferences.Editor editor = settings.edit();
final Vars vars = new Vars();
for (Var var : this.entities) {
for (Var var : this.getEntities()) {
if (!var.isSystem()) {
vars.getVars().add(var);
}
@ -141,4 +141,81 @@ class AndroidVarsRegistryImpl extends AbstractMathRegistry<Var> implements Andro
editor.commit();
}
@NotNull
@Override
public List<Var> getEntities() {
final List<Var> result = new ArrayList<Var>();
for (IConstant iConstant : mathRegistry.getEntities()) {
result.add(transform(iConstant));
}
return result;
}
@NotNull
@Override
public List<Var> getSystemEntities() {
final List<Var> result = new ArrayList<Var>();
for (IConstant iConstant : mathRegistry.getSystemEntities()) {
result.add(transform(iConstant));
}
return result;
}
@Override
public Var add(@Nullable String name, @NotNull IBuilder<? extends Var> IBuilder) {
IConstant result = mathRegistry.add(name, IBuilder);
if (result instanceof Var) {
return (Var) result;
} else if (result != null) {
return transform(result);
} else {
return null;
}
}
@NotNull
private Var transform(@NotNull IConstant result) {
return new Var.Builder(result).create();
}
@Override
public void remove(@NotNull Var var) {
mathRegistry.remove(var);
}
@NotNull
@Override
public List<String> getNames() {
return mathRegistry.getNames();
}
@Override
public boolean contains(@NotNull String name) {
return mathRegistry.contains(name);
}
@Override
public Var get(@NotNull String name) {
IConstant result = mathRegistry.get(name);
if (result instanceof Var) {
return (Var) result;
} else if (result != null) {
return transform(result);
} else {
return null;
}
}
@Override
public Var getById(@NotNull Integer id) {
final IConstant result = mathRegistry.getById(id);
if (result instanceof Var) {
return (Var) result;
} else if (result != null) {
return transform(result);
} else {
return null;
}
}
}

View File

@ -67,7 +67,7 @@ public enum CalculatorEngine {
public final TextProcessor<PreparedExpression> preprocessor = new ToJsclTextProcessor();
@NotNull
private final AndroidVarsRegistry varsRegister = new AndroidVarsRegistryImpl();
private final AndroidVarsRegistry varsRegister = new AndroidVarsRegistryImpl(engine.getConstantsRegistry());
@NotNull
private final AndroidFunctionsRegistry functionsRegistry = new AndroidFunctionsRegistryImpl(engine.getFunctionsRegistry());

View File

@ -17,10 +17,21 @@ import java.util.List;
class ToJsclTextProcessor implements TextProcessor<PreparedExpression> {
@NotNull
private static final Integer MAX_DEPTH = 10;
@Override
@NotNull
public PreparedExpression process(@NotNull String s) throws ParseException {
return processWithDepth(s, 0, new ArrayList<Var>());
}
private static PreparedExpression processWithDepth(@NotNull String s, int depth, @NotNull List<Var> undefinedVars) throws ParseException {
return replaceVariables(processExpression(s).toString(), depth, undefinedVars);
}
@NotNull
private static StringBuilder processExpression(@NotNull String s) throws ParseException {
final StartsWithFinder startsWithFinder = new StartsWithFinder(s, 0);
final StringBuilder result = new StringBuilder();
@ -49,15 +60,18 @@ class ToJsclTextProcessor implements TextProcessor<PreparedExpression> {
i = mathTypeResult.processToJscl(result, i);
}
return replaceVariables(result.toString());
return result;
}
@NotNull
private PreparedExpression replaceVariables(@NotNull final String s) {
final StartsWithFinder startsWithFinder = new StartsWithFinder(s, 0);
private static PreparedExpression replaceVariables(@NotNull final String s, int depth, @NotNull List<Var> undefinedVars) throws ParseException {
if (depth >= MAX_DEPTH) {
throw new ParseException("Infinite loop in expression: " + s);
} else {
depth++;
}
final List<Var> undefinedVars = new ArrayList<Var>();
final StartsWithFinder startsWithFinder = new StartsWithFinder(s, 0);
final StringBuilder result = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
@ -70,12 +84,19 @@ class ToJsclTextProcessor implements TextProcessor<PreparedExpression> {
if (varName != null) {
final Var var = CalculatorEngine.instance.getVarsRegister().get(varName);
if (var != null) {
if (var.isUndefined()) {
if (!var.isDefined()) {
undefinedVars.add(var);
result.append(varName);
offset = varName.length();
} else {
result.append(var.getValue());
final String value = var.getValue();
assert value != null;
if ( var.getDoubleValue() != null ) {
result.append(value);
} else {
result.append("(").append(processWithDepth(value, depth, undefinedVars)).append(")");
}
offset = varName.length();
}
}

View File

@ -6,10 +6,13 @@
package org.solovyev.android.calculator.model;
import jscl.math.function.Constant;
import jscl.math.function.IConstant;
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.common.definitions.IBuilder;
import org.solovyev.common.math.MathEntity;
import org.solovyev.common.utils.StringUtils;
@ -21,7 +24,11 @@ import org.solovyev.common.utils.StringUtils;
*/
@Root
public class Var implements MathEntity{
public class Var implements IConstant {
@NotNull
@Transient
private Integer id;
@Element
@NotNull
@ -38,6 +45,9 @@ public class Var implements MathEntity{
@Nullable
private String description;
@Transient
private Constant constant;
public static class Builder implements IBuilder<Var> {
@NotNull
@ -51,6 +61,9 @@ public class Var implements MathEntity{
@Nullable
private String description;
@Nullable
private Integer id;
public Builder() {
}
@ -59,6 +72,17 @@ public class Var implements MathEntity{
this.value = var.value;
this.system = var.system;
this.description = var.description;
this.id = var.id;
}
public Builder(@NotNull IConstant iConstant) {
this.name = iConstant.getName();
this.value = iConstant.getValue();
this.system = iConstant.isSystem();
this.description = iConstant.getDescription();
this.id = iConstant.getId();
}
public Builder(@NotNull String name, @NotNull Double value) {
@ -70,6 +94,7 @@ public class Var implements MathEntity{
this.value = value;
}
public void setName(@NotNull String name) {
this.name = name;
}
@ -90,48 +115,102 @@ public class Var implements MathEntity{
@NotNull
public Var create() {
final Var var = new Var();
final Var result;
if (id != null) {
result = new Var(id);
} else {
result = new Var();
}
var.name = name;
var.value = value;
var.system = system;
var.description = description;
result.name = name;
result.value = value;
result.system = system;
result.description = description;
return var;
return result;
}
}
private Var() {
}
private Var(@NotNull Integer id) {
this.id = id;
}
public void copy(@NotNull MathEntity o) {
if (o instanceof Var) {
final Var that = ((Var) o);
this.name = that.name;
this.value = that.value;
this.description = that.description;
this.system = that.system;
if (o instanceof IConstant) {
final IConstant that = ((IConstant) o);
this.name = that.getName();
this.value = that.getValue();
this.description = that.getDescription();
this.system = that.isSystem();
} else {
throw new IllegalArgumentException("Trying to make a copy of unsupported type: " + o.getClass());
}
}
@Nullable
public Double getDoubleValue() {
Double result = null;
if (value != null) {
try {
result = Double.valueOf(value);
} catch (NumberFormatException e) {
// do nothing - string is not a double
}
}
return result;
}
@Nullable
public String getValue() {
return value;
}
@NotNull
@Override
public String toJava() {
return String.valueOf(value);
}
public boolean isSystem() {
return system;
}
@NotNull
@Override
public Integer getId() {
return this.id;
}
@Override
public void setId(@NotNull Integer id) {
this.id = id;
}
@Override
public boolean same(@Nullable MathEntity mathEntity) {
if (mathEntity instanceof IConstant) {
if (mathEntity.getId().equals(this.getId())) {
return true;
}
}
return false;
}
@NotNull
public String getName() {
return name;
}
public boolean isUndefined() {
return StringUtils.isEmpty(this.value);
@NotNull
@Override
public Constant getConstant() {
if (constant == null) {
constant = new Constant(this.name);
}
return constant;
}
@Nullable
@ -139,10 +218,16 @@ public class Var implements MathEntity{
return description;
}
@Override
public boolean isDefined() {
return !StringUtils.isEmpty(value);
}
@Override
public String toString() {
if (value != null) {
return getName() + " = " + value;
final String stringValue = getValue();
if (!StringUtils.isEmpty(stringValue)) {
return getName() + " = " + stringValue;
} else {
return getName();
}

View File

@ -38,8 +38,8 @@ public class FromJsclSimplifyTextProcessorTest {
Assert.assertEquals("ee", tp.process("2.718281828459045*2.718281828459045"));
Assert.assertEquals("t×", tp.process("t*"));
Assert.assertEquals("×t", tp.process("*t"));
Assert.assertEquals("t×2", tp.process("t*2"));
Assert.assertEquals("2×t", tp.process("2*t"));
Assert.assertEquals("t2", tp.process("t*2"));
Assert.assertEquals("2t", tp.process("2*t"));
CalculatorEngine.instance.getVarsRegister().add(null, new Var.Builder("t", (String) null));
Assert.assertEquals("t×", tp.process("t*"));
Assert.assertEquals("×t", tp.process("*t"));

View File

@ -47,11 +47,11 @@ public class ToJsclTextProcessorTest {
Assert.assertEquals( "sin(4)*cos(5)", preprocessor.process("sin(4)cos(5)").toString());
Assert.assertEquals( "3.141592653589793*sin(4)*3.141592653589793*cos(√(5))", preprocessor.process("πsin(4)πcos(√(5))").toString());
Assert.assertEquals( "3.141592653589793*sin(4)+3.141592653589793*cos(√(5))", preprocessor.process("πsin(4)+πcos(√(5))").toString());
Assert.assertEquals( "3.141592653589793*sin(4)+3.141592653589793*cos(√(5+√(-1)))", preprocessor.process("πsin(4)+πcos(√(5+i))").toString());
Assert.assertEquals( "3.141592653589793*sin(4.01)+3.141592653589793*cos(√(5+√(-1)))", preprocessor.process("πsin(4.01)+πcos(√(5+i))").toString());
Assert.assertEquals( "2.718281828459045^3.141592653589793*sin(4.01)+3.141592653589793*cos(√(5+√(-1)))", preprocessor.process("e^πsin(4.01)+πcos(√(5+i))").toString());
Assert.assertEquals( "2.718281828459045^3.141592653589793*sin(4.01)+3.141592653589793*cos(√(5+√(-1)))*10^2", preprocessor.process("e^πsin(4.01)+πcos(√(5+i))E2").toString());
Assert.assertEquals( "2.718281828459045^3.141592653589793*sin(4.01)+3.141592653589793*cos(√(5+√(-1)))*10^-2", preprocessor.process("e^πsin(4.01)+πcos(√(5+i))E-2").toString());
Assert.assertEquals( "3.141592653589793*sin(4)+3.141592653589793*cos(√(5+(√(-1))))", preprocessor.process("πsin(4)+πcos(√(5+i))").toString());
Assert.assertEquals( "3.141592653589793*sin(4.01)+3.141592653589793*cos(√(5+(√(-1))))", preprocessor.process("πsin(4.01)+πcos(√(5+i))").toString());
Assert.assertEquals( "2.718281828459045^3.141592653589793*sin(4.01)+3.141592653589793*cos(√(5+(√(-1))))", preprocessor.process("e^πsin(4.01)+πcos(√(5+i))").toString());
Assert.assertEquals( "2.718281828459045^3.141592653589793*sin(4.01)+3.141592653589793*cos(√(5+(√(-1))))*10^2", preprocessor.process("e^πsin(4.01)+πcos(√(5+i))E2").toString());
Assert.assertEquals( "2.718281828459045^3.141592653589793*sin(4.01)+3.141592653589793*cos(√(5+(√(-1))))*10^-2", preprocessor.process("e^πsin(4.01)+πcos(√(5+i))E-2").toString());
Assert.assertEquals( "10^2", preprocessor.process("E2").toString());
Assert.assertEquals( "10^-2", preprocessor.process("E-2").toString());
Assert.assertEquals( "10^-1/2", preprocessor.process("E-1/2").toString());

View File

@ -0,0 +1,68 @@
/*
* 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 junit.framework.Assert;
import org.junit.Test;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;
import java.io.StringWriter;
/**
* User: serso
* Date: 11/7/11
* Time: 7:52 PM
*/
public class VarTest {
private static final String xml = "<vars>\n" +
" <vars class=\"java.util.ArrayList\">\n" +
" <var>\n" +
" <name>e</name>\n" +
" <value>2.718281828459045</value>\n" +
" <system>true</system>\n" +
" <description>description</description>\n" +
" </var>\n" +
" <var>\n" +
" <name>;</name>\n" +
" <value>3.0</value>\n" +
" <system>true</system>\n" +
" </var>\n" +
" </vars>\n" +
"</vars>";
@Test
public void testXml() throws Exception {
final Vars vars = new Vars();
Var first = new Var.Builder("e", Math.E).setDescription("description").setSystem(true).create();
vars.getVars().add(first);
Var second = new Var.Builder(";", 3d).setSystem(true).create();
vars.getVars().add(second);
final StringWriter sw = new StringWriter();
final Serializer serializer = new Persister();
serializer.write(vars, sw);
Assert.assertEquals(xml, sw.toString());
final Vars result = serializer.read(Vars.class, xml);
final Var actualFirst = result.getVars().get(0);
final Var actualSecond = result.getVars().get(1);
areEqual(first, actualFirst);
areEqual(second, actualSecond);
}
private void areEqual(Var expected, Var actual) {
Assert.assertEquals(expected.getName(), actual.getName());
Assert.assertEquals(expected.getDescription(), actual.getDescription());
Assert.assertEquals(expected.getValue(), actual.getValue());
}
}