Module separation

This commit is contained in:
serso
2012-09-20 12:59:21 +04:00
parent 33db715776
commit 417cf88912
23 changed files with 1567 additions and 1448 deletions

View File

@@ -0,0 +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();
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -0,0 +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;
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);
}

View File

@@ -0,0 +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.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();
}

View File

@@ -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<IConstant> getVarsRegistry();
@NotNull
MathRegistry<Function> getFunctionsRegistry();
@NotNull
MathRegistry<Operator> getOperatorsRegistry();
@NotNull
MathRegistry<Operator> getPostfixFunctionsRegistry();
@NotNull
MathEngine getEngine();
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.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<String, Generic> {
public static final FromJsclNumericTextProcessor instance = new FromJsclNumericTextProcessor();
@NotNull
@Override
public String process(@NotNull Generic numeric) throws CalculatorParseException {
return numeric.toString().replace("*", "");
}
}

View File

@@ -0,0 +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.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<String, Generic> 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();
}
}
}

View File

@@ -0,0 +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.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<String> tokens = new ArrayList<String>(10);
{
for (NumeralBase numeralBase : NumeralBase.values()) {
tokens.add(numeralBase.getJsclPrefix());
}
}
@NotNull
@Override
public List<String> 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<String> 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<String> getTokens() {
return CalculatorLocatorImpl.getInstance().getCalculatorEngine().getFunctionsRegistry().getNames();
}
},
operator(1050, true, true, MathGroupType.function) {
@NotNull
@Override
public List<String> getTokens() {
return CalculatorLocatorImpl.getInstance().getCalculatorEngine().getOperatorsRegistry().getNames();
}
},
constant(1100, true, true, MathGroupType.other) {
@NotNull
@Override
public List<String> 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<String> tokens = new ArrayList<String>(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<String> 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<String> 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<String> 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<String> 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<String> 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<MathType> mathTypesByPriority;
@NotNull
private static List<MathType> getMathTypesByPriority() {
if (mathTypesByPriority == null) {
final List<MathType> result = CollectionsUtils.asList(MathType.values());
Collections.sort(result, new Comparator<MathType>() {
@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<String> {
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;
}
}
}

View File

@@ -0,0 +1,92 @@
/*
* 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 org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
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: 10/6/11
* Time: 9:25 PM
*/
public class CalculatorParseException extends SersoException implements Message {
@NotNull
private final Message message;
@NotNull
private final String expression;
@Nullable
private final Integer position;
public CalculatorParseException(@NotNull jscl.text.ParseException jsclParseException) {
this.message = jsclParseException;
this.expression = jsclParseException.getExpression();
this.position = jsclParseException.getPosition();
}
public CalculatorParseException(@Nullable Integer position,
@NotNull String expression,
@NotNull Message message) {
this.message = message;
this.expression = expression;
this.position = position;
}
public CalculatorParseException(@NotNull String expression,
@NotNull Message message) {
this(null, expression, message);
}
@NotNull
public String getExpression() {
return expression;
}
@Nullable
public Integer getPosition() {
return position;
}
@NotNull
@Override
public String getMessageCode() {
return this.message.getMessageCode();
}
@NotNull
@Override
public List<Object> getParameters() {
return this.message.getParameters();
}
@NotNull
@Override
public MessageType getMessageType() {
return this.message.getMessageType();
}
@Override
@Nullable
public String getLocalizedMessage() {
return this.message.getLocalizedMessage(Locale.getDefault());
}
@NotNull
@Override
public String getLocalizedMessage(@NotNull Locale locale) {
return this.message.getLocalizedMessage(locale);
}
}

View File

@@ -0,0 +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<String, Generic> {
instance;
@NotNull
@Override
public String process(@NotNull Generic s) throws CalculatorParseException {
return s.toString();
}
}

View File

@@ -0,0 +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.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<String, Generic> {
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<MathType> 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;
}
}

View File

@@ -0,0 +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<TO extends CharSequence, FROM> {
@NotNull
TO process(@NotNull FROM from) throws CalculatorParseException;
}