package net.sf.openrocket.util;
import java.text.DecimalFormatSymbols;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import de.congrace.exp4j.Calculable;
import de.congrace.exp4j.ExpressionBuilder;
public class ExpressionParser {
private static final Logger log = LoggerFactory.getLogger(ExpressionParser.class);
private static final char DECIMAL_SEPARATOR;
private static final char MINUS_SIGN;
static {
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance();
DECIMAL_SEPARATOR = symbols.getDecimalSeparator();
MINUS_SIGN = symbols.getMinusSign();
}
public double parse(String expression) throws InvalidExpressionException {
String modified = null;
try {
modified = modify(expression);
ExpressionBuilder builder = new ExpressionBuilder(modified);
Calculable calc = builder.build();
double n = calc.calculate().getDoubleValue();
log.debug("Evaluated expression '" + expression + "' (modified='" + modified + "') to " + n);
return n;
} catch (Exception e) {
log.warn("Unable to parse expression '" + expression + "' (modified='" + modified + "')", e);
throw new InvalidExpressionException("Invalid expression: " + expression, e);
}
}
private String modify(String exp) throws InvalidExpressionException {
// Normalize digit equivalents, fraction sign, decimal separators and minus sign
char[] chars = exp.toCharArray();
for (int i = 0; i < chars.length; i++) {
int value = Character.getNumericValue(chars[i]);
if (value >= 0 && value < 10) {
chars[i] = Character.toChars(48 + value)[0];
}
if (chars[i] == Chars.FRACTION) {
chars[i] = '/';
}
if (chars[i] == DECIMAL_SEPARATOR || chars[i] == ',') {
chars[i] = '.';
}
if (chars[i] == MINUS_SIGN) {
chars[i] = '-';
}
}
exp = String.copyValueOf(chars);
// Replace fraction equivalents "1 3/4" with "(1+3/4)"
exp = exp.replaceAll("(?<![\\d.])(\\d+)\\s+(\\d+)\\s*/\\s*(\\d+)(?![\\d.])", "($1+$2/$3)");
// Disallow spaces between numbers - default is to remove spaces!
if (exp.matches(".*[0-9.]\\s+[0-9.].*")) {
throw new InvalidExpressionException("Expression contains excess space: " + exp);
}
return exp;
}
}