number formatting + fixes
This commit is contained in:
parent
e483ac6af5
commit
567551d952
@ -35,7 +35,7 @@ public class CalculatorDisplay extends AutoResizeTextView {
|
||||
private JsclOperation jsclOperation = JsclOperation.numeric;
|
||||
|
||||
@NotNull
|
||||
private final static TextProcessor<TextHighlighter.Result, String> textHighlighter = new TextHighlighter(Color.WHITE, true, CalculatorEngine.instance.getEngine());
|
||||
private final static TextProcessor<TextHighlighter.Result, String> textHighlighter = new TextHighlighter(Color.WHITE, false, CalculatorEngine.instance.getEngine());
|
||||
|
||||
@Nullable
|
||||
private Generic genericResult;
|
||||
|
@ -31,7 +31,7 @@ public class CalculatorEditor extends EditText implements SharedPreferences.OnSh
|
||||
private boolean highlightText = true;
|
||||
|
||||
@NotNull
|
||||
private final static TextProcessor<TextHighlighter.Result, String> textHighlighter = new TextHighlighter(Color.WHITE, false, CalculatorEngine.instance.getEngine());
|
||||
private final static TextProcessor<TextHighlighter.Result, String> textHighlighter = new TextHighlighter(Color.WHITE, true, CalculatorEngine.instance.getEngine());
|
||||
|
||||
public CalculatorEditor(Context context) {
|
||||
super(context);
|
||||
|
@ -10,8 +10,9 @@ 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.NumberBuilder;
|
||||
import org.solovyev.android.calculator.model.CalculatorEngine;
|
||||
import org.solovyev.android.calculator.model.CalculatorParseException;
|
||||
import org.solovyev.android.calculator.model.NumberBuilder;
|
||||
import org.solovyev.android.calculator.model.TextProcessor;
|
||||
import org.solovyev.common.utils.MutableObject;
|
||||
|
||||
@ -74,11 +75,11 @@ public class TextHighlighter implements TextProcessor<TextHighlighter.Result, St
|
||||
private final int colorRed;
|
||||
private final int colorGreen;
|
||||
private final int colorBlue;
|
||||
private final boolean simpleFormat;
|
||||
private final boolean allowScientificFormat;
|
||||
|
||||
public TextHighlighter(int baseColor, boolean simpleFormat, @NotNull MathContext mathContext) {
|
||||
public TextHighlighter(int baseColor, boolean allowScientificFormat, @NotNull MathContext mathContext) {
|
||||
this.color = baseColor;
|
||||
this.simpleFormat = simpleFormat;
|
||||
this.allowScientificFormat = allowScientificFormat;
|
||||
this.mathContext = mathContext;
|
||||
//this.colorRed = Color.red(baseColor);
|
||||
this.colorRed = (baseColor >> 16) & 0xFF;
|
||||
@ -100,7 +101,7 @@ public class TextHighlighter implements TextProcessor<TextHighlighter.Result, St
|
||||
|
||||
int numberOffset = 0;
|
||||
|
||||
final NumberBuilder numberBuilder = new NumberBuilder(simpleFormat, mathContext.getNumeralBase());
|
||||
final NumberBuilder numberBuilder = new NumberBuilder(allowScientificFormat, CalculatorEngine.instance.getEngine());
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
MathType.Result mathType = MathType.getType(text, i, numberBuilder.isHexMode());
|
||||
|
||||
@ -144,7 +145,7 @@ public class TextHighlighter implements TextProcessor<TextHighlighter.Result, St
|
||||
}
|
||||
|
||||
final MutableObject<Integer> localNumberOffset = new MutableObject<Integer>(0);
|
||||
numberBuilder.process(text1, localNumberOffset);
|
||||
numberBuilder.processNumber(text1, localNumberOffset);
|
||||
numberOffset += localNumberOffset.getObject();
|
||||
|
||||
if (maxNumberOfOpenGroupSymbols > 0) {
|
||||
|
@ -21,7 +21,7 @@ import java.util.*;
|
||||
|
||||
public enum MathType {
|
||||
|
||||
numeral_base(50, true, false) {
|
||||
numeral_base(50, true, false, MathGroupType.number) {
|
||||
|
||||
private final List<String> tokens = new ArrayList<String>(10);
|
||||
{
|
||||
@ -40,23 +40,23 @@ public enum MathType {
|
||||
}
|
||||
},
|
||||
|
||||
dot(200, true, true, ".") {
|
||||
dot(200, true, true, MathGroupType.number, ".") {
|
||||
@Override
|
||||
public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) {
|
||||
return super.isNeedMultiplicationSignBefore(mathTypeBefore) && mathTypeBefore != digit;
|
||||
}
|
||||
},
|
||||
|
||||
grouping_separator(250, false, false, "'", " "){
|
||||
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, "E"),
|
||||
power_10(300, false, false, MathGroupType.number, "E"),
|
||||
|
||||
postfix_function(400, false, true) {
|
||||
postfix_function(400, false, true, MathGroupType.function) {
|
||||
@NotNull
|
||||
@Override
|
||||
public List<String> getTokens() {
|
||||
@ -64,8 +64,8 @@ public enum MathType {
|
||||
}
|
||||
},
|
||||
|
||||
unary_operation(500, false, false, "-", "="),
|
||||
binary_operation(600, false, false, "-", "+", "*", "×", "∙", "/", "^") {
|
||||
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("∙")) {
|
||||
@ -76,7 +76,7 @@ public enum MathType {
|
||||
}
|
||||
},
|
||||
|
||||
open_group_symbol(800, true, false, "[", "(", "{") {
|
||||
open_group_symbol(800, true, false, MathGroupType.other, "[", "(", "{") {
|
||||
@Override
|
||||
public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) {
|
||||
return super.isNeedMultiplicationSignBefore(mathTypeBefore) && mathTypeBefore != function && mathTypeBefore != operator;
|
||||
@ -88,7 +88,7 @@ public enum MathType {
|
||||
}
|
||||
},
|
||||
|
||||
close_group_symbol(900, false, true, "]", ")", "}") {
|
||||
close_group_symbol(900, false, true, MathGroupType.other, "]", ")", "}") {
|
||||
@Override
|
||||
public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) {
|
||||
return false;
|
||||
@ -100,7 +100,7 @@ public enum MathType {
|
||||
}
|
||||
},
|
||||
|
||||
function(1000, true, true) {
|
||||
function(1000, true, true, MathGroupType.function) {
|
||||
@NotNull
|
||||
@Override
|
||||
public List<String> getTokens() {
|
||||
@ -108,7 +108,7 @@ public enum MathType {
|
||||
}
|
||||
},
|
||||
|
||||
operator(1050, true, true) {
|
||||
operator(1050, true, true, MathGroupType.function) {
|
||||
@NotNull
|
||||
@Override
|
||||
public List<String> getTokens() {
|
||||
@ -116,7 +116,7 @@ public enum MathType {
|
||||
}
|
||||
},
|
||||
|
||||
constant(1100, true, true) {
|
||||
constant(1100, true, true, MathGroupType.other) {
|
||||
@NotNull
|
||||
@Override
|
||||
public List<String> getTokens() {
|
||||
@ -129,7 +129,7 @@ public enum MathType {
|
||||
}
|
||||
},
|
||||
|
||||
digit(1125, true, true) {
|
||||
digit(1125, true, true, MathGroupType.number) {
|
||||
|
||||
private final List<String> tokens = new ArrayList<String>(16);
|
||||
{
|
||||
@ -149,9 +149,9 @@ public enum MathType {
|
||||
}
|
||||
},
|
||||
|
||||
comma(1150, false, false, ","),
|
||||
comma(1150, false, false, MathGroupType.other, ","),
|
||||
|
||||
text(1200, false, false) {
|
||||
text(1200, false, false, MathGroupType.other) {
|
||||
@Override
|
||||
public int processToJscl(@NotNull StringBuilder result, int i, @NotNull String match) {
|
||||
if (match.length() > 0) {
|
||||
@ -169,6 +169,13 @@ public enum MathType {
|
||||
}
|
||||
};
|
||||
|
||||
public static enum MathGroupType {
|
||||
function,
|
||||
number,
|
||||
operation,
|
||||
other
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private final List<String> tokens;
|
||||
|
||||
@ -179,24 +186,35 @@ public enum MathType {
|
||||
|
||||
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, CollectionsUtils.asList(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);
|
||||
}
|
||||
|
||||
/* public static int getPostfixFunctionStart(@NotNull CharSequence s, int position) throws ParseException {
|
||||
@NotNull
|
||||
public MathGroupType getGroupType() {
|
||||
return groupType;
|
||||
}
|
||||
|
||||
/* public static int getPostfixFunctionStart(@NotNull CharSequence s, int position) throws ParseException {
|
||||
assert s.length() > position;
|
||||
|
||||
int numberOfOpenGroups = 0;
|
||||
|
@ -169,7 +169,7 @@ public enum CalculatorEngine {
|
||||
|
||||
calculationResult.setObject(genericResult);
|
||||
} catch (AbstractJsclArithmeticException e) {
|
||||
evalException.setObject(new CalculatorEvalException(e, jsclExpression));
|
||||
evalException.setObject(new CalculatorEvalException(e, e, jsclExpression));
|
||||
} catch (ArithmeticException e) {
|
||||
//System.out.println(e.getMessage());
|
||||
parseException.setObject(new CalculatorParseException(Messages.msg_1, jsclExpression, e.getMessage()));
|
||||
@ -207,7 +207,8 @@ public enum CalculatorEngine {
|
||||
}
|
||||
|
||||
if (parseExceptionObject != null || evalExceptionObject != null) {
|
||||
if (finalOperation == JsclOperation.numeric && preparedExpression.isExistsUndefinedVar()) {
|
||||
if (finalOperation == JsclOperation.numeric &&
|
||||
( preparedExpression.isExistsUndefinedVar() || ( evalExceptionObject != null && evalExceptionObject.getCause() instanceof NumeralBaseException)) ) {
|
||||
return evaluate(JsclOperation.simplify, expression, mr);
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
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;
|
||||
@ -27,7 +28,8 @@ public class CalculatorEvalException extends SersoException implements Message {
|
||||
@NotNull
|
||||
private final String expression;
|
||||
|
||||
public CalculatorEvalException(@NotNull Message message, String expression) {
|
||||
public CalculatorEvalException(@NotNull Message message, @NotNull Throwable cause, String expression) {
|
||||
super(cause);
|
||||
this.message = message;
|
||||
this.expression = expression;
|
||||
}
|
||||
|
@ -33,16 +33,16 @@ public class FromJsclSimplifyTextProcessor implements TextProcessor<String, Gene
|
||||
public String process(@NotNull String s) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
final NumberBuilder numberBuilder = new NumberBuilder(true, mathContext.getNumeralBase());
|
||||
final NumberBuilder nb = new NumberBuilder(false, CalculatorEngine.instance.getEngine());
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
final MathType.Result mathTypeResult = MathType.getType(s, i, numberBuilder.isHexMode());
|
||||
final MathType.Result mathTypeResult = MathType.getType(s, i, nb.isHexMode());
|
||||
|
||||
numberBuilder.process(sb, mathTypeResult, null);
|
||||
nb.process(sb, mathTypeResult, null);
|
||||
|
||||
i = mathTypeResult.processFromJscl(sb, i);
|
||||
}
|
||||
|
||||
numberBuilder.process(sb, null);
|
||||
nb.processNumber(sb, null);
|
||||
|
||||
return removeMultiplicationSigns(sb.toString());
|
||||
}
|
||||
|
@ -6,9 +6,10 @@
|
||||
|
||||
package org.solovyev.android.calculator.model;
|
||||
|
||||
import jscl.MathContext;
|
||||
import jscl.MathEngine;
|
||||
import jscl.NumeralBase;
|
||||
import jscl.math.numeric.Numeric;
|
||||
import jscl.math.function.IConstant;
|
||||
import jscl.math.numeric.Real;
|
||||
import jscl.text.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -28,59 +29,79 @@ import java.util.List;
|
||||
*/
|
||||
public class NumberBuilder {
|
||||
|
||||
@NotNull
|
||||
private final MathEngine engine;
|
||||
|
||||
@Nullable
|
||||
private StringBuilder numberBuilder = null;
|
||||
@Nullable
|
||||
private String number = null;
|
||||
|
||||
private final boolean simpleFormat;
|
||||
|
||||
@NotNull
|
||||
private final NumeralBase defaultNumeralBase;
|
||||
private final boolean allowScientificFormat;
|
||||
|
||||
@Nullable
|
||||
private NumeralBase nb;
|
||||
|
||||
public NumberBuilder(boolean simpleFormat, @NotNull NumeralBase defaultNumeralBase) {
|
||||
this.simpleFormat = simpleFormat;
|
||||
this.defaultNumeralBase = defaultNumeralBase;
|
||||
this.nb = defaultNumeralBase;
|
||||
public NumberBuilder(boolean allowScientificFormat, @NotNull MathEngine engine) {
|
||||
this.allowScientificFormat = allowScientificFormat;
|
||||
this.nb = engine.getNumeralBase();
|
||||
this.engine = 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 sb, @NotNull MathType.Result mathTypeResult, @Nullable MutableObject<Integer> numberOffset) {
|
||||
number = null;
|
||||
|
||||
public MathType.Result process(@NotNull StringBuilder text, @NotNull MathType.Result mathTypeResult, @Nullable MutableObject<Integer> offset) {
|
||||
final MathType.Result possibleResult;
|
||||
if ((CollectionsUtils.contains(mathTypeResult.getMathType(), MathType.digit, MathType.numeral_base, MathType.dot, MathType.grouping_separator, MathType.power_10) ||
|
||||
isSignAfterE(mathTypeResult)) && numeralBaseCheck(mathTypeResult) && numeralBaseInTheStart(mathTypeResult.getMathType())) {
|
||||
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 {
|
||||
possibleResult = process(sb, numberOffset);
|
||||
// process current number (and go to the next one)
|
||||
possibleResult = processNumber(text, offset);
|
||||
}
|
||||
|
||||
return possibleResult == null ? mathTypeResult : possibleResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
private boolean canContinue(@NotNull MathType.Result mathTypeResult) {
|
||||
return ((mathTypeResult.getMathType().getGroupType() == MathType.MathGroupType.number && numeralBaseCheck(mathTypeResult) && numeralBaseInTheStart(mathTypeResult.getMathType()) || isSignAfterE(mathTypeResult)));
|
||||
}
|
||||
|
||||
private boolean numeralBaseInTheStart(@NotNull MathType mathType) {
|
||||
return mathType != MathType.numeral_base || numberBuilder == null;
|
||||
}
|
||||
|
||||
private boolean numeralBaseCheck( @NotNull MathType.Result mathType ) {
|
||||
if ( mathType.getMathType() == MathType.digit ) {
|
||||
private boolean numeralBaseCheck(@NotNull MathType.Result mathType) {
|
||||
if (mathType.getMathType() == MathType.digit) {
|
||||
final Character ch = mathType.getMatch().charAt(0);
|
||||
if ( NumeralBase.hex.getAcceptableCharacters().contains(ch) && !NumeralBase.dec.getAcceptableCharacters().contains(ch) ) {
|
||||
if ( nb == NumeralBase.hex ) {
|
||||
if (NumeralBase.hex.getAcceptableCharacters().contains(ch) && !NumeralBase.dec.getAcceptableCharacters().contains(ch)) {
|
||||
if (nb == NumeralBase.hex) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@ -104,101 +125,92 @@ public class NumberBuilder {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 process(@NotNull StringBuilder sb, @Nullable MutableObject<Integer> numberOffset) {
|
||||
int numberOfTokens = 0;
|
||||
public MathType.Result processNumber(@NotNull StringBuilder text, @Nullable MutableObject<Integer> 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();
|
||||
|
||||
final NumeralBase localNb;
|
||||
if (numberBuilder != null) {
|
||||
try {
|
||||
number = numberBuilder.toString();
|
||||
List<String> tokens = new ArrayList<String>();
|
||||
|
||||
// let's get rid of unnecessary characters (grouping separators, + after E)
|
||||
final List<String> tokens = new ArrayList<String>();
|
||||
tokens.addAll(MathType.grouping_separator.getTokens());
|
||||
// + after E can be omitted: 10+E = 10E (NOTE: - cannot be omitted )
|
||||
tokens.add("+");
|
||||
for (String groupingSeparator : tokens) {
|
||||
String newNumber = number.replace(groupingSeparator, "");
|
||||
numberOfTokens += number.length() - newNumber.length();
|
||||
number = newNumber;
|
||||
final String trimmedNumber = number.replace(groupingSeparator, "");
|
||||
trimmedChars += number.length() - trimmedNumber.length();
|
||||
number = trimmedNumber;
|
||||
}
|
||||
|
||||
toDouble(number, getNumeralBase());
|
||||
// check if number still valid
|
||||
toDouble(number, getNumeralBase(), engine);
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
// number is not valid => stop
|
||||
number = null;
|
||||
}
|
||||
|
||||
numberBuilder = null;
|
||||
localNb = getNumeralBase();
|
||||
nb = defaultNumeralBase;
|
||||
} else {
|
||||
number = null;
|
||||
localNb = getNumeralBase();
|
||||
|
||||
// must set default numeral base (exit numeral base mode)
|
||||
nb = engine.getNumeralBase();
|
||||
}
|
||||
|
||||
return replaceSystemVars(sb, number, numberOfTokens, numberOffset, localNb, simpleFormat);
|
||||
return replaceNumberInText(text, number, trimmedChars, offset, localNb, allowScientificFormat, engine);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static MathType.Result replaceSystemVars(StringBuilder sb, String number, int numberOfTokens, @Nullable MutableObject<Integer> numberOffset, @NotNull NumeralBase nb, boolean simpleFormat) {
|
||||
private static MathType.Result replaceNumberInText(@NotNull StringBuilder text,
|
||||
@Nullable String number,
|
||||
int trimmedChars,
|
||||
@Nullable MutableObject<Integer> offset,
|
||||
@NotNull NumeralBase nb,
|
||||
boolean allowScientificFormat,
|
||||
@NotNull final MathEngine engine) {
|
||||
MathType.Result result = null;
|
||||
|
||||
if (number != null) {
|
||||
final String finalNumber = number;
|
||||
final Var var = CollectionsUtils.find(CalculatorEngine.instance.getVarsRegister().getSystemEntities(), new Finder<Var>() {
|
||||
|
||||
// detect if current number is precisely equals to constant in constants' registry (NOTE: ONLY FOR SYSTEM CONSTANTS)
|
||||
final IConstant constant = CollectionsUtils.find(engine.getConstantsRegistry().getSystemEntities(), new Finder<IConstant>() {
|
||||
@Override
|
||||
public boolean isFound(@Nullable Var var) {
|
||||
return var != null && finalNumber.equals(var.getValue());
|
||||
public boolean isFound(@Nullable IConstant constant) {
|
||||
return constant != null && finalNumber.equals(constant.getValue());
|
||||
}
|
||||
});
|
||||
|
||||
if (var != null) {
|
||||
sb.delete(sb.length() - number.length() - numberOfTokens, sb.length());
|
||||
sb.append(var.getName());
|
||||
result = new MathType.Result(MathType.constant, var.getName());
|
||||
// in any case remove old number from text
|
||||
final int oldNumberLength = number.length() + trimmedChars;
|
||||
text.delete(text.length() - oldNumberLength, text.length());
|
||||
|
||||
if (constant != null) {
|
||||
// let's change number with constant from registry
|
||||
text.append(constant.getName());
|
||||
result = new MathType.Result(MathType.constant, constant.getName());
|
||||
} else {
|
||||
sb.delete(sb.length() - number.length() - numberOfTokens, sb.length());
|
||||
|
||||
final String formattedNumber;
|
||||
|
||||
if (!simpleFormat) {
|
||||
int indexOfDot = number.indexOf('.');
|
||||
|
||||
if (indexOfDot < 0) {
|
||||
int indexOfE;
|
||||
if (nb == NumeralBase.hex) {
|
||||
indexOfE = -1;
|
||||
} else {
|
||||
indexOfE = number.indexOf('E');
|
||||
}
|
||||
if (indexOfE < 0) {
|
||||
formattedNumber = toString(toDouble(number, nb), nb);
|
||||
} else {
|
||||
final String part;
|
||||
if (indexOfDot != 0) {
|
||||
part = toString(toDouble(number.substring(0, indexOfE), nb), nb);
|
||||
} else {
|
||||
part = "";
|
||||
}
|
||||
formattedNumber = part + number.substring(indexOfE);
|
||||
}
|
||||
} else {
|
||||
final String integerPart;
|
||||
if (indexOfDot != 0) {
|
||||
integerPart = toString(toDouble(number.substring(0, indexOfDot), nb), nb);
|
||||
} else {
|
||||
integerPart = "";
|
||||
}
|
||||
formattedNumber = integerPart + number.substring(indexOfDot);
|
||||
}
|
||||
} else {
|
||||
formattedNumber = toString(toDouble(number, nb), nb);
|
||||
final String newNumber = formatNumber(number, nb, allowScientificFormat, engine);
|
||||
if (offset != null) {
|
||||
// register offset between old number and new number
|
||||
offset.setObject(newNumber.length() - oldNumberLength);
|
||||
}
|
||||
|
||||
if (numberOffset != null) {
|
||||
numberOffset.setObject(formattedNumber.length() - number.length() - numberOfTokens);
|
||||
}
|
||||
sb.append(formattedNumber);
|
||||
text.append(newNumber);
|
||||
}
|
||||
}
|
||||
|
||||
@ -206,40 +218,78 @@ public class NumberBuilder {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String toString(@NotNull Double value, @NotNull NumeralBase nb) {
|
||||
return CalculatorEngine.instance.getEngine().format(value, nb);
|
||||
private static String formatNumber(@NotNull String number, @NotNull NumeralBase nb, boolean allowScientificFormat, @NotNull MathEngine engine) {
|
||||
String result;
|
||||
|
||||
if (allowScientificFormat) {
|
||||
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 = toString(number, nb, engine);
|
||||
} else {
|
||||
final String part;
|
||||
if (indexOfDot != 0) {
|
||||
part = toString(number.substring(0, indexOfE), nb, engine);
|
||||
} else {
|
||||
part = "";
|
||||
}
|
||||
result = part + number.substring(indexOfE);
|
||||
}
|
||||
} else {
|
||||
final String integerPart;
|
||||
if (indexOfDot != 0) {
|
||||
integerPart = toString(number.substring(0, indexOfDot), nb, engine);
|
||||
} else {
|
||||
integerPart = "";
|
||||
}
|
||||
result = integerPart + number.substring(indexOfDot);
|
||||
}
|
||||
} else {
|
||||
result = toString(number, nb, engine);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String toString(@NotNull String value, @NotNull NumeralBase nb, @NotNull MathContext mathContext) {
|
||||
return mathContext.format(toDouble(value, nb, mathContext), nb);
|
||||
}
|
||||
|
||||
public boolean isHexMode() {
|
||||
return nb == NumeralBase.hex || ( nb == null && defaultNumeralBase == NumeralBase.hex);
|
||||
return nb == NumeralBase.hex || (nb == null && engine.getNumeralBase() == NumeralBase.hex);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private NumeralBase getNumeralBase(){
|
||||
return nb == null ? defaultNumeralBase : nb;
|
||||
private NumeralBase getNumeralBase() {
|
||||
return nb == null ? engine.getNumeralBase() : nb;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Double toDouble(@NotNull String s, @NotNull NumeralBase nb) throws NumberFormatException{
|
||||
|
||||
final MathEngine me = CalculatorEngine.instance.getEngine();
|
||||
|
||||
final NumeralBase defaultNb = me.getNumeralBase();
|
||||
private static Double toDouble(@NotNull String s, @NotNull NumeralBase nb, @NotNull final MathContext mc) throws NumberFormatException {
|
||||
final NumeralBase defaultNb = mc.getNumeralBase();
|
||||
try {
|
||||
me.setNumeralBase(nb);
|
||||
mc.setNumeralBase(nb);
|
||||
|
||||
try {
|
||||
return JsclIntegerParser.parser.parse(Parser.Parameters.newInstance(s, new MutableInt(0), me), null).content().doubleValue();
|
||||
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), me), null).content()).doubleValue();
|
||||
return ((Real) DoubleParser.parser.parse(Parser.Parameters.newInstance(s, new MutableInt(0), mc), null).content()).doubleValue();
|
||||
} catch (ParseException e1) {
|
||||
throw new NumberFormatException();
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
me.setNumeralBase(defaultNb);
|
||||
mc.setNumeralBase(defaultNb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,6 @@
|
||||
|
||||
package org.solovyev.android.calculator.model;
|
||||
|
||||
import jscl.JsclMathEngine;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.solovyev.android.calculator.StartsWithFinder;
|
||||
import org.solovyev.android.calculator.jscl.JsclOperation;
|
||||
@ -41,7 +40,7 @@ class ToJsclTextProcessor implements TextProcessor<PreparedExpression, String> {
|
||||
|
||||
final StringBuilder sb = new StringBuilder(s);
|
||||
|
||||
final NumberBuilder nb = new NumberBuilder(true, CalculatorEngine.instance.getEngine().getNumeralBase());
|
||||
final NumberBuilder nb = new NumberBuilder(false, CalculatorEngine.instance.getEngine());
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
if (s.charAt(i) == ' ') continue;
|
||||
startsWithFinder.setI(i);
|
||||
|
@ -26,7 +26,7 @@ public class TextHighlighterTest {
|
||||
|
||||
@Test
|
||||
public void testProcess() throws Exception {
|
||||
TextProcessor<?, String> textHighlighter = new TextHighlighter(0, true, JsclMathEngine.instance);
|
||||
TextProcessor<?, String> textHighlighter = new TextHighlighter(0, false, JsclMathEngine.instance);
|
||||
|
||||
final Random random = new Random(new Date().getTime());
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
@ -48,7 +48,7 @@ public class TextHighlighterTest {
|
||||
Assert.assertEquals("1 000 000", textHighlighter.process("1000000").toString());
|
||||
Assert.assertEquals("1 000 000", textHighlighter.process("1000000").toString());
|
||||
|
||||
textHighlighter = new TextHighlighter(0, false, JsclMathEngine.instance);
|
||||
textHighlighter = new TextHighlighter(0, true, JsclMathEngine.instance);
|
||||
Assert.assertEquals("0.1E3", textHighlighter.process("0.1E3").toString());
|
||||
Assert.assertEquals("1E3", textHighlighter.process("1E3").toString());
|
||||
Assert.assertEquals("2<b>0x:</b>", textHighlighter.process("20x:").toString());
|
||||
@ -60,7 +60,7 @@ public class TextHighlighterTest {
|
||||
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, true, JsclMathEngine.instance);
|
||||
textHighlighter = new TextHighlighter(0, false, JsclMathEngine.instance);
|
||||
|
||||
textHighlighter.process("cannot calculate 3^10^10 !!!\n" +
|
||||
" unable to enter 0. FIXED\n" +
|
||||
|
@ -183,6 +183,18 @@ public class CalculatorEngineTest {
|
||||
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());
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user