b180f61928cad3c18f64084ee41e7c33058aff93
[debian/openrocket] / core / src / net / sf / openrocket / util / ExpressionParser.java
1 package net.sf.openrocket.util;
2
3 import java.text.DecimalFormatSymbols;
4
5 import net.sf.openrocket.logging.LogHelper;
6 import net.sf.openrocket.startup.Application;
7 import de.congrace.exp4j.Calculable;
8 import de.congrace.exp4j.ExpressionBuilder;
9
10 public class ExpressionParser {
11         private static final LogHelper log = Application.getLogger();
12         
13         private static final char DECIMAL_SEPARATOR;
14         private static final char MINUS_SIGN;
15         static {
16                 DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance();
17                 DECIMAL_SEPARATOR = symbols.getDecimalSeparator();
18                 MINUS_SIGN = symbols.getMinusSign();
19         }
20         
21         public double parse(String expression) throws InvalidExpressionException {
22                 
23                 String modified = null;
24                 try {
25                         modified = modify(expression);
26                         ExpressionBuilder builder = new ExpressionBuilder(modified);
27                         Calculable calc = builder.build();
28                         double n = calc.calculate();
29                         log.debug("Evaluated expression '" + expression + "' (modified='" + modified + "') to " + n);
30                         return n;
31                 } catch (Exception e) {
32                         log.warn("Unable to parse expression '" + expression + "' (modified='" + modified + "')", e);
33                         throw new InvalidExpressionException("Invalid expression: " + expression, e);
34                 }
35         }
36         
37         private String modify(String exp) throws InvalidExpressionException {
38                 
39                 // Normalize digit equivalents, fraction sign, decimal separators and minus sign
40                 char[] chars = exp.toCharArray();
41                 for (int i = 0; i < chars.length; i++) {
42                         int value = Character.getNumericValue(chars[i]);
43                         if (value >= 0 && value < 10) {
44                                 chars[i] = Character.toChars(48 + value)[0];
45                         }
46                         if (chars[i] == Chars.FRACTION) {
47                                 chars[i] = '/';
48                         }
49                         if (chars[i] == DECIMAL_SEPARATOR || chars[i] == ',') {
50                                 chars[i] = '.';
51                         }
52                         if (chars[i] == MINUS_SIGN) {
53                                 chars[i] = '-';
54                         }
55                 }
56                 exp = String.copyValueOf(chars);
57                 
58                 // Replace fraction equivalents "1 3/4" with "(1+3/4)"
59                 exp = exp.replaceAll("(?<![\\d.])(\\d+)\\s+(\\d+)\\s*/\\s*(\\d+)(?![\\d.])", "($1+$2/$3)");
60                 
61                 // Disallow spaces between numbers - default is to remove spaces!
62                 if (exp.matches(".*[0-9.]\\s+[0-9.].*")) {
63                         throw new InvalidExpressionException("Expression contains excess space: " + exp);
64                 }
65                 return exp;
66         }
67 }