number formatting + fixes

This commit is contained in:
Sergey Solovyev 2011-12-15 15:56:32 +04:00
parent e483ac6af5
commit 567551d952
11 changed files with 222 additions and 139 deletions

View File

@ -35,7 +35,7 @@ public class CalculatorDisplay extends AutoResizeTextView {
private JsclOperation jsclOperation = JsclOperation.numeric; private JsclOperation jsclOperation = JsclOperation.numeric;
@NotNull @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 @Nullable
private Generic genericResult; private Generic genericResult;

View File

@ -31,7 +31,7 @@ public class CalculatorEditor extends EditText implements SharedPreferences.OnSh
private boolean highlightText = true; private boolean highlightText = true;
@NotNull @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) { public CalculatorEditor(Context context) {
super(context); super(context);

View File

@ -10,8 +10,9 @@ import jscl.MathContext;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.math.MathType; 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.CalculatorParseException;
import org.solovyev.android.calculator.model.NumberBuilder;
import org.solovyev.android.calculator.model.TextProcessor; import org.solovyev.android.calculator.model.TextProcessor;
import org.solovyev.common.utils.MutableObject; 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 colorRed;
private final int colorGreen; private final int colorGreen;
private final int colorBlue; 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.color = baseColor;
this.simpleFormat = simpleFormat; this.allowScientificFormat = allowScientificFormat;
this.mathContext = mathContext; this.mathContext = mathContext;
//this.colorRed = Color.red(baseColor); //this.colorRed = Color.red(baseColor);
this.colorRed = (baseColor >> 16) & 0xFF; this.colorRed = (baseColor >> 16) & 0xFF;
@ -100,7 +101,7 @@ public class TextHighlighter implements TextProcessor<TextHighlighter.Result, St
int numberOffset = 0; 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++) { for (int i = 0; i < text.length(); i++) {
MathType.Result mathType = MathType.getType(text, i, numberBuilder.isHexMode()); 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); final MutableObject<Integer> localNumberOffset = new MutableObject<Integer>(0);
numberBuilder.process(text1, localNumberOffset); numberBuilder.processNumber(text1, localNumberOffset);
numberOffset += localNumberOffset.getObject(); numberOffset += localNumberOffset.getObject();
if (maxNumberOfOpenGroupSymbols > 0) { if (maxNumberOfOpenGroupSymbols > 0) {

View File

@ -21,7 +21,7 @@ import java.util.*;
public enum MathType { public enum MathType {
numeral_base(50, true, false) { numeral_base(50, true, false, MathGroupType.number) {
private final List<String> tokens = new ArrayList<String>(10); 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 @Override
public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) { public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) {
return super.isNeedMultiplicationSignBefore(mathTypeBefore) && mathTypeBefore != digit; return super.isNeedMultiplicationSignBefore(mathTypeBefore) && mathTypeBefore != digit;
} }
}, },
grouping_separator(250, false, false, "'", " "){ grouping_separator(250, false, false, MathGroupType.number, "'", " "){
@Override @Override
public int processToJscl(@NotNull StringBuilder result, int i, @NotNull String match) throws CalculatorParseException { public int processToJscl(@NotNull StringBuilder result, int i, @NotNull String match) throws CalculatorParseException {
return i; 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 @NotNull
@Override @Override
public List<String> getTokens() { public List<String> getTokens() {
@ -64,8 +64,8 @@ public enum MathType {
} }
}, },
unary_operation(500, false, false, "-", "="), unary_operation(500, false, false, MathGroupType.operation, "-", "="),
binary_operation(600, false, false, "-", "+", "*", "×", "", "/", "^") { binary_operation(600, false, false, MathGroupType.operation, "-", "+", "*", "×", "", "/", "^") {
@Override @Override
protected String getSubstituteToJscl(@NotNull String match) { protected String getSubstituteToJscl(@NotNull String match) {
if (match.equals("×") || match.equals("")) { 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 @Override
public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) { public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) {
return super.isNeedMultiplicationSignBefore(mathTypeBefore) && mathTypeBefore != function && mathTypeBefore != operator; 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 @Override
public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) { public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) {
return false; return false;
@ -100,7 +100,7 @@ public enum MathType {
} }
}, },
function(1000, true, true) { function(1000, true, true, MathGroupType.function) {
@NotNull @NotNull
@Override @Override
public List<String> getTokens() { public List<String> getTokens() {
@ -108,7 +108,7 @@ public enum MathType {
} }
}, },
operator(1050, true, true) { operator(1050, true, true, MathGroupType.function) {
@NotNull @NotNull
@Override @Override
public List<String> getTokens() { public List<String> getTokens() {
@ -116,7 +116,7 @@ public enum MathType {
} }
}, },
constant(1100, true, true) { constant(1100, true, true, MathGroupType.other) {
@NotNull @NotNull
@Override @Override
public List<String> getTokens() { 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); 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 @Override
public int processToJscl(@NotNull StringBuilder result, int i, @NotNull String match) { public int processToJscl(@NotNull StringBuilder result, int i, @NotNull String match) {
if (match.length() > 0) { if (match.length() > 0) {
@ -169,6 +169,13 @@ public enum MathType {
} }
}; };
public static enum MathGroupType {
function,
number,
operation,
other
}
@NotNull @NotNull
private final List<String> tokens; private final List<String> tokens;
@ -179,24 +186,35 @@ public enum MathType {
private final boolean needMultiplicationSignAfter; private final boolean needMultiplicationSignAfter;
@NotNull
private final MathGroupType groupType;
MathType(@NotNull Integer priority, MathType(@NotNull Integer priority,
boolean needMultiplicationSignBefore, boolean needMultiplicationSignBefore,
boolean needMultiplicationSignAfter, boolean needMultiplicationSignAfter,
@NotNull MathGroupType groupType,
@NotNull String... tokens) { @NotNull String... tokens) {
this(priority, needMultiplicationSignBefore, needMultiplicationSignAfter, CollectionsUtils.asList(tokens)); this(priority, needMultiplicationSignBefore, needMultiplicationSignAfter, groupType, CollectionsUtils.asList(tokens));
} }
MathType(@NotNull Integer priority, MathType(@NotNull Integer priority,
boolean needMultiplicationSignBefore, boolean needMultiplicationSignBefore,
boolean needMultiplicationSignAfter, boolean needMultiplicationSignAfter,
@NotNull MathGroupType groupType,
@NotNull List<String> tokens) { @NotNull List<String> tokens) {
this.priority = priority; this.priority = priority;
this.needMultiplicationSignBefore = needMultiplicationSignBefore; this.needMultiplicationSignBefore = needMultiplicationSignBefore;
this.needMultiplicationSignAfter = needMultiplicationSignAfter; this.needMultiplicationSignAfter = needMultiplicationSignAfter;
this.groupType = groupType;
this.tokens = Collections.unmodifiableList(tokens); 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; assert s.length() > position;
int numberOfOpenGroups = 0; int numberOfOpenGroups = 0;

View File

@ -169,7 +169,7 @@ public enum CalculatorEngine {
calculationResult.setObject(genericResult); calculationResult.setObject(genericResult);
} catch (AbstractJsclArithmeticException e) { } catch (AbstractJsclArithmeticException e) {
evalException.setObject(new CalculatorEvalException(e, jsclExpression)); evalException.setObject(new CalculatorEvalException(e, e, jsclExpression));
} catch (ArithmeticException e) { } catch (ArithmeticException e) {
//System.out.println(e.getMessage()); //System.out.println(e.getMessage());
parseException.setObject(new CalculatorParseException(Messages.msg_1, jsclExpression, 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 (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); return evaluate(JsclOperation.simplify, expression, mr);
} }

View File

@ -6,6 +6,7 @@
package org.solovyev.android.calculator.model; package org.solovyev.android.calculator.model;
import jscl.AbstractJsclArithmeticException;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.solovyev.common.exceptions.SersoException; import org.solovyev.common.exceptions.SersoException;
import org.solovyev.common.msg.Message; import org.solovyev.common.msg.Message;
@ -27,7 +28,8 @@ public class CalculatorEvalException extends SersoException implements Message {
@NotNull @NotNull
private final String expression; 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.message = message;
this.expression = expression; this.expression = expression;
} }

View File

@ -33,16 +33,16 @@ public class FromJsclSimplifyTextProcessor implements TextProcessor<String, Gene
public String process(@NotNull String s) { public String process(@NotNull String s) {
final StringBuilder sb = new StringBuilder(); 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++) { 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); i = mathTypeResult.processFromJscl(sb, i);
} }
numberBuilder.process(sb, null); nb.processNumber(sb, null);
return removeMultiplicationSigns(sb.toString()); return removeMultiplicationSigns(sb.toString());
} }

View File

@ -6,9 +6,10 @@
package org.solovyev.android.calculator.model; package org.solovyev.android.calculator.model;
import jscl.MathContext;
import jscl.MathEngine; import jscl.MathEngine;
import jscl.NumeralBase; import jscl.NumeralBase;
import jscl.math.numeric.Numeric; import jscl.math.function.IConstant;
import jscl.math.numeric.Real; import jscl.math.numeric.Real;
import jscl.text.*; import jscl.text.*;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -28,59 +29,79 @@ import java.util.List;
*/ */
public class NumberBuilder { public class NumberBuilder {
@NotNull
private final MathEngine engine;
@Nullable @Nullable
private StringBuilder numberBuilder = null; private StringBuilder numberBuilder = null;
@Nullable
private String number = null;
private final boolean simpleFormat; private final boolean allowScientificFormat;
@NotNull
private final NumeralBase defaultNumeralBase;
@Nullable @Nullable
private NumeralBase nb; private NumeralBase nb;
public NumberBuilder(boolean simpleFormat, @NotNull NumeralBase defaultNumeralBase) { public NumberBuilder(boolean allowScientificFormat, @NotNull MathEngine engine) {
this.simpleFormat = simpleFormat; this.allowScientificFormat = allowScientificFormat;
this.defaultNumeralBase = defaultNumeralBase; this.nb = engine.getNumeralBase();
this.nb = defaultNumeralBase; 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 @NotNull
public MathType.Result process(@NotNull StringBuilder sb, @NotNull MathType.Result mathTypeResult, @Nullable MutableObject<Integer> numberOffset) { public MathType.Result process(@NotNull StringBuilder text, @NotNull MathType.Result mathTypeResult, @Nullable MutableObject<Integer> offset) {
number = null;
final MathType.Result possibleResult; final MathType.Result possibleResult;
if ((CollectionsUtils.contains(mathTypeResult.getMathType(), MathType.digit, MathType.numeral_base, MathType.dot, MathType.grouping_separator, MathType.power_10) || if (canContinue(mathTypeResult)) {
isSignAfterE(mathTypeResult)) && numeralBaseCheck(mathTypeResult) && numeralBaseInTheStart(mathTypeResult.getMathType())) { // let's continue building number
if (numberBuilder == null) { if (numberBuilder == null) {
// if new number => create new builder
numberBuilder = new StringBuilder(); numberBuilder = new StringBuilder();
} }
if (mathTypeResult.getMathType() != MathType.numeral_base) { if (mathTypeResult.getMathType() != MathType.numeral_base) {
// just add matching string
numberBuilder.append(mathTypeResult.getMatch()); numberBuilder.append(mathTypeResult.getMatch());
} else { } else {
// set explicitly numeral base (do not include it into number)
nb = NumeralBase.getByPrefix(mathTypeResult.getMatch()); nb = NumeralBase.getByPrefix(mathTypeResult.getMatch());
} }
possibleResult = null; possibleResult = null;
} else { } else {
possibleResult = process(sb, numberOffset); // process current number (and go to the next one)
possibleResult = processNumber(text, offset);
} }
return possibleResult == null ? mathTypeResult : possibleResult; 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) { private boolean numeralBaseInTheStart(@NotNull MathType mathType) {
return mathType != MathType.numeral_base || numberBuilder == null; return mathType != MathType.numeral_base || numberBuilder == null;
} }
private boolean numeralBaseCheck( @NotNull MathType.Result mathType ) { private boolean numeralBaseCheck(@NotNull MathType.Result mathType) {
if ( mathType.getMathType() == MathType.digit ) { if (mathType.getMathType() == MathType.digit) {
final Character ch = mathType.getMatch().charAt(0); final Character ch = mathType.getMatch().charAt(0);
if ( NumeralBase.hex.getAcceptableCharacters().contains(ch) && !NumeralBase.dec.getAcceptableCharacters().contains(ch) ) { if (NumeralBase.hex.getAcceptableCharacters().contains(ch) && !NumeralBase.dec.getAcceptableCharacters().contains(ch)) {
if ( nb == NumeralBase.hex ) { if (nb == NumeralBase.hex) {
return true; return true;
} else { } else {
return false; return false;
@ -104,101 +125,92 @@ public class NumberBuilder {
return false; 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 @Nullable
public MathType.Result process(@NotNull StringBuilder sb, @Nullable MutableObject<Integer> numberOffset) { public MathType.Result processNumber(@NotNull StringBuilder text, @Nullable MutableObject<Integer> offset) {
int numberOfTokens = 0; // 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) { if (numberBuilder != null) {
try { try {
number = numberBuilder.toString(); 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()); tokens.addAll(MathType.grouping_separator.getTokens());
// + after E can be omitted: 10+E = 10E (NOTE: - cannot be omitted )
tokens.add("+"); tokens.add("+");
for (String groupingSeparator : tokens) { for (String groupingSeparator : tokens) {
String newNumber = number.replace(groupingSeparator, ""); final String trimmedNumber = number.replace(groupingSeparator, "");
numberOfTokens += number.length() - newNumber.length(); trimmedChars += number.length() - trimmedNumber.length();
number = newNumber; number = trimmedNumber;
} }
toDouble(number, getNumeralBase()); // check if number still valid
toDouble(number, getNumeralBase(), engine);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
// number is not valid => stop
number = null; number = null;
} }
numberBuilder = null; numberBuilder = null;
localNb = getNumeralBase();
nb = defaultNumeralBase; // must set default numeral base (exit numeral base mode)
} else { nb = engine.getNumeralBase();
number = null;
localNb = getNumeralBase();
} }
return replaceSystemVars(sb, number, numberOfTokens, numberOffset, localNb, simpleFormat); return replaceNumberInText(text, number, trimmedChars, offset, localNb, allowScientificFormat, engine);
} }
@Nullable @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; MathType.Result result = null;
if (number != null) { if (number != null) {
final String finalNumber = number; 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 @Override
public boolean isFound(@Nullable Var var) { public boolean isFound(@Nullable IConstant constant) {
return var != null && finalNumber.equals(var.getValue()); return constant != null && finalNumber.equals(constant.getValue());
} }
}); });
if (var != null) { // in any case remove old number from text
sb.delete(sb.length() - number.length() - numberOfTokens, sb.length()); final int oldNumberLength = number.length() + trimmedChars;
sb.append(var.getName()); text.delete(text.length() - oldNumberLength, text.length());
result = new MathType.Result(MathType.constant, var.getName());
} else {
sb.delete(sb.length() - number.length() - numberOfTokens, sb.length());
final String formattedNumber; if (constant != null) {
// let's change number with constant from registry
if (!simpleFormat) { text.append(constant.getName());
int indexOfDot = number.indexOf('.'); result = new MathType.Result(MathType.constant, constant.getName());
if (indexOfDot < 0) {
int indexOfE;
if (nb == NumeralBase.hex) {
indexOfE = -1;
} else { } else {
indexOfE = number.indexOf('E'); 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 (indexOfE < 0) { text.append(newNumber);
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);
}
if (numberOffset != null) {
numberOffset.setObject(formattedNumber.length() - number.length() - numberOfTokens);
}
sb.append(formattedNumber);
} }
} }
@ -206,40 +218,78 @@ public class NumberBuilder {
} }
@NotNull @NotNull
private static String toString(@NotNull Double value, @NotNull NumeralBase nb) { private static String formatNumber(@NotNull String number, @NotNull NumeralBase nb, boolean allowScientificFormat, @NotNull MathEngine engine) {
return CalculatorEngine.instance.getEngine().format(value, nb); 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() { public boolean isHexMode() {
return nb == NumeralBase.hex || ( nb == null && defaultNumeralBase == NumeralBase.hex); return nb == NumeralBase.hex || (nb == null && engine.getNumeralBase() == NumeralBase.hex);
} }
@NotNull @NotNull
private NumeralBase getNumeralBase(){ private NumeralBase getNumeralBase() {
return nb == null ? defaultNumeralBase : nb; return nb == null ? engine.getNumeralBase() : nb;
} }
@NotNull @NotNull
private static Double toDouble(@NotNull String s, @NotNull NumeralBase nb) throws NumberFormatException{ private static Double toDouble(@NotNull String s, @NotNull NumeralBase nb, @NotNull final MathContext mc) throws NumberFormatException {
final NumeralBase defaultNb = mc.getNumeralBase();
final MathEngine me = CalculatorEngine.instance.getEngine();
final NumeralBase defaultNb = me.getNumeralBase();
try { try {
me.setNumeralBase(nb); mc.setNumeralBase(nb);
try { 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) { } catch (ParseException e) {
try { 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) { } catch (ParseException e1) {
throw new NumberFormatException(); throw new NumberFormatException();
} }
} }
} finally { } finally {
me.setNumeralBase(defaultNb); mc.setNumeralBase(defaultNb);
} }
} }
} }

View File

@ -6,7 +6,6 @@
package org.solovyev.android.calculator.model; package org.solovyev.android.calculator.model;
import jscl.JsclMathEngine;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.StartsWithFinder; import org.solovyev.android.calculator.StartsWithFinder;
import org.solovyev.android.calculator.jscl.JsclOperation; import org.solovyev.android.calculator.jscl.JsclOperation;
@ -41,7 +40,7 @@ class ToJsclTextProcessor implements TextProcessor<PreparedExpression, String> {
final StringBuilder sb = new StringBuilder(s); 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++) { for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == ' ') continue; if (s.charAt(i) == ' ') continue;
startsWithFinder.setI(i); startsWithFinder.setI(i);

View File

@ -26,7 +26,7 @@ public class TextHighlighterTest {
@Test @Test
public void testProcess() throws Exception { 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()); final Random random = new Random(new Date().getTime());
for (int i = 0; i < 1000; i++) { 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());
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("0.1E3", textHighlighter.process("0.1E3").toString());
Assert.assertEquals("1E3", textHighlighter.process("1E3").toString()); Assert.assertEquals("1E3", textHighlighter.process("1E3").toString());
Assert.assertEquals("2<b>0x:</b>", textHighlighter.process("20x:").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 000E3", textHighlighter.process("-1000000E3").toString());
Assert.assertEquals("-1 000 000E-3", textHighlighter.process("-1000000E-3").toString()); Assert.assertEquals("-1 000 000E-3", textHighlighter.process("-1000000E-3").toString());
Assert.assertEquals("-1 000 000E-30000", textHighlighter.process("-1000000E-30000").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" + textHighlighter.process("cannot calculate 3^10^10 !!!\n" +
" unable to enter 0. FIXED\n" + " unable to enter 0. FIXED\n" +

View File

@ -183,6 +183,18 @@ public class CalculatorEngineTest {
Assert.assertEquals("100", cm.evaluate(JsclOperation.numeric, "0.1E3").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("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", cm.evaluate(JsclOperation.numeric, "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((0))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))").getResult()); Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((0))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))").getResult());