Expression parser updates
authorplaa <plaa@180e2498-e6e9-4542-8430-84ac67f01cd8>
Tue, 22 May 2012 04:11:25 +0000 (04:11 +0000)
committerplaa <plaa@180e2498-e6e9-4542-8430-84ac67f01cd8>
Tue, 22 May 2012 04:11:25 +0000 (04:11 +0000)
git-svn-id: https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk@700 180e2498-e6e9-4542-8430-84ac67f01cd8

26 files changed:
core/.classpath
core/build.xml
core/lib/exp4j-0.2.9.jar [new file with mode: 0644]
core/src/net/sf/openrocket/gui/adaptors/DoubleModel.java
core/src/net/sf/openrocket/util/ExpressionParser.java [new file with mode: 0644]
core/src/net/sf/openrocket/util/InvalidExpressionException.java [new file with mode: 0644]
core/src/net/sf/openrocket/util/exp4j/AbstractExpression.java [deleted file]
core/src/net/sf/openrocket/util/exp4j/Calculable.java [deleted file]
core/src/net/sf/openrocket/util/exp4j/CalculationToken.java [deleted file]
core/src/net/sf/openrocket/util/exp4j/CommandlineInterpreter.java [deleted file]
core/src/net/sf/openrocket/util/exp4j/CustomFunction.java [deleted file]
core/src/net/sf/openrocket/util/exp4j/ExpressionBuilder.java [deleted file]
core/src/net/sf/openrocket/util/exp4j/FunctionSeparatorToken.java [deleted file]
core/src/net/sf/openrocket/util/exp4j/FunctionToken.java [deleted file]
core/src/net/sf/openrocket/util/exp4j/InfixTranslator.java [deleted file]
core/src/net/sf/openrocket/util/exp4j/InvalidCustomFunctionException.java [deleted file]
core/src/net/sf/openrocket/util/exp4j/NumberToken.java [deleted file]
core/src/net/sf/openrocket/util/exp4j/OperatorToken.java [deleted file]
core/src/net/sf/openrocket/util/exp4j/ParenthesisToken.java [deleted file]
core/src/net/sf/openrocket/util/exp4j/PostfixExpression.java [deleted file]
core/src/net/sf/openrocket/util/exp4j/Token.java [deleted file]
core/src/net/sf/openrocket/util/exp4j/Tokenizer.java [deleted file]
core/src/net/sf/openrocket/util/exp4j/UnknownFunctionException.java [deleted file]
core/src/net/sf/openrocket/util/exp4j/UnparsableExpressionException.java [deleted file]
core/src/net/sf/openrocket/util/exp4j/VariableToken.java [deleted file]
core/test/net/sf/openrocket/util/ExpressionParserTest.java [new file with mode: 0644]

index 275144a9aa93ac20bae19be76e7fa8a614439bf6..002d4561b6f234889558c1c3f76a17efe7013eaf 100644 (file)
@@ -27,5 +27,6 @@
        <classpathentry kind="lib" path="resources"/>
        <classpathentry kind="lib" path="lib/jspf.core-1.0.2.jar" sourcepath="/home/sampo/Projects/lib/jspf/documentation/api"/>
        <classpathentry kind="lib" path="lib/opencsv-2.3.jar"/>
+       <classpathentry kind="lib" path="lib/exp4j-0.2.9.jar"/>
        <classpathentry kind="output" path="bin"/>
 </classpath>
index 7d93b880a735fc2bb20601bbd5a438f09f912b44..af9ed8a4cf1b6035302aeefa32089baf74560855 100644 (file)
@@ -86,6 +86,7 @@
                        <zipfileset src="lib/jfreechart-1.0.13.jar" />
                        <zipfileset src="lib/iText-5.0.2.jar" />
                        <zipfileset src="lib/opencsv-2.3.jar" />
+                       <zipfileset src="lib/exp4j-0.2.9.jar" />
                </jar>
        </target>
 
diff --git a/core/lib/exp4j-0.2.9.jar b/core/lib/exp4j-0.2.9.jar
new file mode 100644 (file)
index 0000000..a120994
Binary files /dev/null and b/core/lib/exp4j-0.2.9.jar differ
index b79903cd1db34983056eeceb5e28079a5a2f2eb0..3216f0a7d7bf7d36f6c995b2b346736e0752cd8a 100644 (file)
@@ -23,16 +23,14 @@ import net.sf.openrocket.unit.Unit;
 import net.sf.openrocket.unit.UnitGroup;
 import net.sf.openrocket.util.BugException;
 import net.sf.openrocket.util.ChangeSource;
+import net.sf.openrocket.util.ExpressionParser;
+import net.sf.openrocket.util.InvalidExpressionException;
 import net.sf.openrocket.util.Invalidatable;
 import net.sf.openrocket.util.Invalidator;
 import net.sf.openrocket.util.MathUtil;
 import net.sf.openrocket.util.MemoryManagement;
 import net.sf.openrocket.util.Reflection;
 import net.sf.openrocket.util.StateChangeListener;
-import net.sf.openrocket.util.exp4j.Calculable;
-import net.sf.openrocket.util.exp4j.ExpressionBuilder;
-import net.sf.openrocket.util.exp4j.UnknownFunctionException;
-import net.sf.openrocket.util.exp4j.UnparsableExpressionException;
 
 
 /**
@@ -51,12 +49,12 @@ import net.sf.openrocket.util.exp4j.UnparsableExpressionException;
 
 public class DoubleModel implements StateChangeListener, ChangeSource, Invalidatable {
        private static final LogHelper log = Application.getLogger();
-
-
+       
+       
        public static final DoubleModel ZERO = new DoubleModel(0);
-
+       
        //////////// JSpinner Model ////////////
-
+       
        /**
         * Model suitable for JSpinner. 
         * Note: Previously used using JSpinner.NumberEditor and extended SpinnerNumberModel
@@ -65,12 +63,14 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
         * to be entered so that fractional units and expressions can be used.
         */
        public class ValueSpinnerModel extends AbstractSpinnerModel implements Invalidatable {
-
+               
+               private ExpressionParser parser = new ExpressionParser();
+               
                @Override
                public Object getValue() {
                        return currentUnit.toString(DoubleModel.this.getValue());
                }
-
+               
                @Override
                public void setValue(Object value) {
                        if (firing > 0) {
@@ -83,44 +83,39 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
                        Number num = Double.NaN;
                        
                        // Set num if possible
-                       if ( value instanceof Number ) {
-                               num = (Number)value;
+                       if (value instanceof Number) {
+                               num = (Number) value;
                        }
-                       else if ( value instanceof String ) {
+                       else if (value instanceof String) {
                                try {
-                                       String newValString = (String)value;
-                                       ExpressionBuilder builder=new ExpressionBuilder(newValString);
-                                       Calculable calc=builder.build();
-                                       num = calc.calculate();
+                                       String newValString = (String) value;
+                                       num = parser.parse(newValString);
+                               } catch (InvalidExpressionException e) {
+                                       // Ignore
                                }
-                               catch ( java.lang.NumberFormatException e ) {
-                               } catch (UnknownFunctionException e) {
-                               } catch (UnparsableExpressionException e) {
-                               } catch (java.util.EmptyStackException e) {
-                               } 
                        }
-
+                       
                        // Update the doublemodel with the new number or return to the last number if not possible
-                       if ( ((Double)num).isNaN() ) { 
-                               DoubleModel.this.setValue( lastValue );
+                       if (((Double) num).isNaN()) {
+                               DoubleModel.this.setValue(lastValue);
                                log.user("SpinnerModel could not set value for " + DoubleModel.this.toString() + ". Could not convert " + value.toString());
-                       } 
-                       else {                  
+                       }
+                       else {
                                double newValue = num.doubleValue();
                                double converted = currentUnit.fromUnit(newValue);
-       
+                               
                                log.user("SpinnerModel setValue called for " + DoubleModel.this.toString() + " newValue=" + newValue +
                                                " converted=" + converted);
                                DoubleModel.this.setValue(converted);
                        }
                        
                        // Force a refresh if text doesn't match up exactly with the stored value
-                       if ( ! ((Double)lastValue).toString().equals( this.getValue().toString() ) )  {
+                       if (!((Double) lastValue).toString().equals(this.getValue().toString())) {
                                DoubleModel.this.fireStateChanged();
-                               log.debug("SpinnerModel "+DoubleModel.this.toString()+" refresh forced because string did not match actual value.");
+                               log.debug("SpinnerModel " + DoubleModel.this.toString() + " refresh forced because string did not match actual value.");
                        }
                }
-
+               
                @Override
                public Object getNextValue() {
                        double d = currentUnit.toUnit(DoubleModel.this.getValue());
@@ -132,7 +127,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
                                d = max;
                        return d;
                }
-
+               
                @Override
                public Object getPreviousValue() {
                        double d = currentUnit.toUnit(DoubleModel.this.getValue());
@@ -144,23 +139,23 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
                                d = min;
                        return d;
                }
-
+               
                @Override
                public void addChangeListener(ChangeListener l) {
                        DoubleModel.this.addChangeListener(l);
                }
-
+               
                @Override
                public void removeChangeListener(ChangeListener l) {
                        DoubleModel.this.removeChangeListener(l);
                }
-
+               
                @Override
                public void invalidate() {
                        DoubleModel.this.invalidate();
                }
        }
-
+       
        /**
         * Returns a new SpinnerModel with the same base as the DoubleModel.
         * The values given to the JSpinner are in the currently selected units.
@@ -170,69 +165,69 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
        public SpinnerModel getSpinnerModel() {
                return new ValueSpinnerModel();
        }
-
+       
        ////////////  JSlider model  ////////////
-
+       
        private class ValueSliderModel implements BoundedRangeModel, StateChangeListener, Invalidatable {
                private static final int MAX = 1000;
-
+               
                /*
                 * Use linear scale  value = linear1 * x + linear0  when x < linearPosition
                 * Use quadratic scale  value = quad2 * x^2 + quad1 * x + quad0  otherwise
                 */
                private final boolean islinear;
-
+               
                // Linear in range x <= linearPosition
                private final double linearPosition;
-
+               
                // May be changing DoubleModels when using linear model
                private final DoubleModel min, mid, max;
-
+               
                // Linear multiplier and constant
                //private final double linear1;
                //private final double linear0;
-
+               
                // Non-linear multiplier, exponent and constant
                private double quad2, quad1, quad0;
-
+               
                public ValueSliderModel(DoubleModel min, DoubleModel max) {
                        this.islinear = true;
                        linearPosition = 1.0;
-
+                       
                        this.min = min;
                        this.mid = max; // Never use exponential scale
                        this.max = max;
-
+                       
                        min.addChangeListener(this);
                        max.addChangeListener(this);
-
+                       
                        quad2 = quad1 = quad0 = 0; // Not used
                }
-
-
-
+               
+               
+               
                /**
                 * Generate a linear model from min to max.
                 */
                public ValueSliderModel(double min, double max) {
                        this.islinear = true;
                        linearPosition = 1.0;
-
+                       
                        this.min = new DoubleModel(min);
                        this.mid = new DoubleModel(max); // Never use exponential scale
                        this.max = new DoubleModel(max);
-
+                       
                        quad2 = quad1 = quad0 = 0; // Not used
                }
-
+               
                public ValueSliderModel(double min, double mid, double max) {
                        this(min, 0.5, mid, max);
                }
-
+               
                public ValueSliderModel(double min, double mid, DoubleModel max) {
                        this(min, 0.5, mid, max);
                }
-
+               
                /*
                 * v(x)  = mul * x^exp + add
                 * 
@@ -240,31 +235,32 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
                 * v(1)    = mul + add = max
                 * v'(pos) = mul*exp * pos^(exp-1) = linearMul
                 */
-               public ValueSliderModel(double min, double pos, double mid, double max ) {
+               public ValueSliderModel(double min, double pos, double mid, double max) {
                        this(min, pos, mid, new DoubleModel(max));
                }
+               
                public ValueSliderModel(double min, double pos, double mid, DoubleModel max) {
                        this.min = new DoubleModel(min);
                        this.mid = new DoubleModel(mid);
                        this.max = max;
-
+                       
                        this.islinear = false;
-
+                       
                        max.addChangeListener(this);
-
+                       
                        linearPosition = pos;
                        //linear0 = min;
                        //linear1 = (mid-min)/pos;
-
+                       
                        if (!(min < mid && mid <= max.getValue() && 0 < pos && pos < 1)) {
                                throw new IllegalArgumentException("Bad arguments for ValueSliderModel " +
                                                "min=" + min + " mid=" + mid + " max=" + max + " pos=" + pos);
                        }
-
+                       
                        updateExponentialParameters();
-
+                       
                }
-
+               
                private void updateExponentialParameters() {
                        double pos = this.linearPosition;
                        double minValue = this.min.getValue();
@@ -281,11 +277,11 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
                        quad1 = (delta + 2 * (midValue - maxValue) * pos - delta * pos * pos) / pow2(pos - 1);
                        quad0 = (midValue - (2 * midValue + delta) * pos + (maxValue + delta) * pos * pos) / pow2(pos - 1);
                }
-
+               
                private double pow2(double x) {
                        return x * x;
                }
-
+               
                @Override
                public int getValue() {
                        double value = DoubleModel.this.getValue();
@@ -293,13 +289,13 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
                                return 0;
                        if (value >= max.getValue())
                                return MAX;
-
+                       
                        double x;
                        if (value <= mid.getValue()) {
                                // Use linear scale
                                //linear0 = min;
                                //linear1 = (mid-min)/pos;
-
+                               
                                x = (value - min.getValue()) * linearPosition / (mid.getValue() - min.getValue());
                        } else {
                                // Use quadratic scale
@@ -309,8 +305,8 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
                        }
                        return (int) (x * MAX);
                }
-
-
+               
+               
                @Override
                public void setValue(int newValue) {
                        if (firing > 0) {
@@ -319,96 +315,96 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
                                                " value=" + newValue + ", currently firing events");
                                return;
                        }
-
+                       
                        double x = (double) newValue / MAX;
                        double scaledValue;
-
+                       
                        if (x <= linearPosition) {
                                // Use linear scale
                                //linear0 = min;
                                //linear1 = (mid-min)/pos;
-
+                               
                                scaledValue = (mid.getValue() - min.getValue()) / linearPosition * x + min.getValue();
                        } else {
                                // Use quadratic scale
                                scaledValue = quad2 * x * x + quad1 * x + quad0;
                        }
-
+                       
                        double converted = currentUnit.fromUnit(currentUnit.round(currentUnit.toUnit(scaledValue)));
                        log.user("SliderModel setValue called for " + DoubleModel.this.toString() + " newValue=" + newValue +
                                        " scaledValue=" + scaledValue + " converted=" + converted);
                        DoubleModel.this.setValue(converted);
                }
-
-
+               
+               
                // Static get-methods
                private boolean isAdjusting;
-
+               
                @Override
                public int getExtent() {
                        return 0;
                }
-
+               
                @Override
                public int getMaximum() {
                        return MAX;
                }
-
+               
                @Override
                public int getMinimum() {
                        return 0;
                }
-
+               
                @Override
                public boolean getValueIsAdjusting() {
                        return isAdjusting;
                }
-
+               
                // Ignore set-values
                @Override
                public void setExtent(int newExtent) {
                }
-
+               
                @Override
                public void setMaximum(int newMaximum) {
                }
-
+               
                @Override
                public void setMinimum(int newMinimum) {
                }
-
+               
                @Override
                public void setValueIsAdjusting(boolean b) {
                        isAdjusting = b;
                }
-
+               
                @Override
                public void setRangeProperties(int value, int extent, int min, int max, boolean adjusting) {
                        setValueIsAdjusting(adjusting);
                        setValue(value);
                }
-
+               
                // Pass change listeners to the underlying model
                @Override
                public void addChangeListener(ChangeListener l) {
                        DoubleModel.this.addChangeListener(l);
                }
-
+               
                @Override
                public void removeChangeListener(ChangeListener l) {
                        DoubleModel.this.removeChangeListener(l);
                }
-
+               
                @Override
                public void invalidate() {
                        DoubleModel.this.invalidate();
                }
-
+               
                @Override
                public void stateChanged(EventObject e) {
                        // Min or max range has changed.
-                       if ( !islinear ) {
-                               double midValue = (max.getValue() - min.getValue()) /3.0;
+                       if (!islinear) {
+                               double midValue = (max.getValue() - min.getValue()) / 3.0;
                                mid.setValue(midValue);
                                updateExponentialParameters();
                        }
@@ -417,48 +413,48 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
                                fireStateChanged();
                }
        }
-
-
+       
+       
        public BoundedRangeModel getSliderModel(DoubleModel min, DoubleModel max) {
                return new ValueSliderModel(min, max);
        }
-
+       
        public BoundedRangeModel getSliderModel(double min, double max) {
                return new ValueSliderModel(min, max);
        }
-
+       
        public BoundedRangeModel getSliderModel(double min, double mid, double max) {
                return new ValueSliderModel(min, mid, max);
        }
-
+       
        public BoundedRangeModel getSliderModel(double min, double mid, DoubleModel max) {
                return new ValueSliderModel(min, mid, max);
        }
-
+       
        public BoundedRangeModel getSliderModel(double min, double pos, double mid, double max) {
                return new ValueSliderModel(min, pos, mid, max);
        }
-
-
-
-
-
+       
+       
+       
+       
+       
        ////////////  Action model  ////////////
-
+       
        private class AutomaticActionModel extends AbstractAction implements StateChangeListener, Invalidatable {
                private boolean oldValue = false;
-
+               
                public AutomaticActionModel() {
                        oldValue = isAutomatic();
                        addChangeListener(this);
                }
-
-
+               
+               
                @Override
                public boolean isEnabled() {
                        return isAutomaticAvailable();
                }
-
+               
                @Override
                public Object getValue(String key) {
                        if (key.equals(Action.SELECTED_KEY)) {
@@ -467,7 +463,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
                        }
                        return super.getValue(key);
                }
-
+               
                @Override
                public void putValue(String key, Object value) {
                        if (firing > 0) {
@@ -486,24 +482,24 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
                                super.putValue(key, value);
                        }
                }
-
+               
                // Implement a wrapper to the ChangeListeners
                ArrayList<PropertyChangeListener> propertyChangeListeners =
                                new ArrayList<PropertyChangeListener>();
-
+               
                @Override
                public void addPropertyChangeListener(PropertyChangeListener listener) {
                        propertyChangeListeners.add(listener);
                        DoubleModel.this.addChangeListener(this);
                }
-
+               
                @Override
                public void removePropertyChangeListener(PropertyChangeListener listener) {
                        propertyChangeListeners.remove(listener);
                        if (propertyChangeListeners.isEmpty())
                                DoubleModel.this.removeChangeListener(this);
                }
-
+               
                // If the value has changed, generate an event to the listeners
                @Override
                public void stateChanged(EventObject e) {
@@ -518,18 +514,18 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
                                ((PropertyChangeListener) l[i]).propertyChange(event);
                        }
                }
-
+               
                @Override
                public void actionPerformed(ActionEvent e) {
                        // Setting performed in putValue
                }
-
+               
                @Override
                public void invalidate() {
                        DoubleModel.this.invalidate();
                }
        }
-
+       
        /**
         * Returns a new Action corresponding to the changes of the automatic setting
         * property of the value model.  This may be used directly with e.g. check buttons.
@@ -539,48 +535,48 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
        public Action getAutomaticAction() {
                return new AutomaticActionModel();
        }
-
-
-
-
-
+       
+       
+       
+       
+       
        ////////////  Main model  /////////////
-
+       
        /*
         * The main model handles all values in SI units, i.e. no conversion is made within the model.
         */
-
+       
        private final ChangeSource source;
        private final String valueName;
        private final double multiplier;
-
+       
        private final Method getMethod;
        private final Method setMethod;
-
+       
        private final Method getAutoMethod;
        private final Method setAutoMethod;
-
+       
        private final ArrayList<EventListener> listeners = new ArrayList<EventListener>();
-
+       
        private final UnitGroup units;
        private Unit currentUnit;
-
+       
        private final double minValue;
        private double maxValue;
-
+       
        private String toString = null;
-
-
+       
+       
        private int firing = 0; //  >0 when model itself is sending events
-
-
+       
+       
        // Used to differentiate changes in valueName and other changes in the component:
        private double lastValue = 0;
        private boolean lastAutomatic = false;
-
+       
        private Invalidator invalidator = new Invalidator(this);
-
-
+       
+       
        /**
         * Generate a DoubleModel that contains an internal double value.
         * 
@@ -589,7 +585,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
        public DoubleModel(double value) {
                this(value, UnitGroup.UNITS_NONE, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
        }
-
+       
        /**
         * Generate a DoubleModel that contains an internal double value.
         * 
@@ -599,7 +595,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
        public DoubleModel(double value, UnitGroup unit) {
                this(value, unit, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
        }
-
+       
        /**
         * Generate a DoubleModel that contains an internal double value.
         * 
@@ -610,7 +606,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
        public DoubleModel(double value, UnitGroup unit, double min) {
                this(value, unit, min, Double.POSITIVE_INFINITY);
        }
-
+       
        /**
         * Generate a DoubleModel that contains an internal double value.
         * 
@@ -623,18 +619,18 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
                this.lastValue = value;
                this.minValue = min;
                this.maxValue = max;
-
+               
                source = null;
                valueName = "Constant value";
                multiplier = 1;
-
+               
                getMethod = setMethod = null;
                getAutoMethod = setAutoMethod = null;
                units = unit;
                currentUnit = units.getDefaultUnit();
        }
-
-
+       
+       
        /**
         * Generates a new DoubleModel that changes the values of the specified component.
         * The double value is read and written using the methods "get"/"set" + valueName.
@@ -650,37 +646,37 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
                this.source = source;
                this.valueName = valueName;
                this.multiplier = multiplier;
-
+               
                this.units = unit;
                currentUnit = units.getDefaultUnit();
-
+               
                this.minValue = min;
                this.maxValue = max;
-
+               
                try {
                        getMethod = source.getClass().getMethod("get" + valueName);
                } catch (NoSuchMethodException e) {
                        throw new IllegalArgumentException("get method for value '" + valueName +
                                        "' not present in class " + source.getClass().getCanonicalName());
                }
-
+               
                Method s = null;
                try {
                        s = source.getClass().getMethod("set" + valueName, double.class);
                } catch (NoSuchMethodException e1) {
                } // Ignore
                setMethod = s;
-
+               
                // Automatic selection methods
-
+               
                Method set = null, get = null;
-
+               
                try {
                        get = source.getClass().getMethod("is" + valueName + "Automatic");
                        set = source.getClass().getMethod("set" + valueName + "Automatic", boolean.class);
                } catch (NoSuchMethodException e) {
                } // ignore
-
+               
                if (set != null && get != null) {
                        getAutoMethod = get;
                        setAutoMethod = set;
@@ -688,53 +684,53 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
                        getAutoMethod = null;
                        setAutoMethod = null;
                }
-
+               
        }
-
+       
        public DoubleModel(ChangeSource source, String valueName, double multiplier, UnitGroup unit,
                        double min) {
                this(source, valueName, multiplier, unit, min, Double.POSITIVE_INFINITY);
        }
-
+       
        public DoubleModel(ChangeSource source, String valueName, double multiplier, UnitGroup unit) {
                this(source, valueName, multiplier, unit, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
        }
-
+       
        public DoubleModel(ChangeSource source, String valueName, UnitGroup unit,
                        double min, double max) {
                this(source, valueName, 1.0, unit, min, max);
        }
-
+       
        public DoubleModel(ChangeSource source, String valueName, UnitGroup unit, double min) {
                this(source, valueName, 1.0, unit, min, Double.POSITIVE_INFINITY);
        }
-
+       
        public DoubleModel(ChangeSource source, String valueName, UnitGroup unit) {
                this(source, valueName, 1.0, unit, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
        }
-
+       
        public DoubleModel(ChangeSource source, String valueName) {
                this(source, valueName, 1.0, UnitGroup.UNITS_NONE,
                                Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
        }
-
+       
        public DoubleModel(ChangeSource source, String valueName, double min) {
                this(source, valueName, 1.0, UnitGroup.UNITS_NONE, min, Double.POSITIVE_INFINITY);
        }
-
+       
        public DoubleModel(ChangeSource source, String valueName, double min, double max) {
                this(source, valueName, 1.0, UnitGroup.UNITS_NONE, min, max);
        }
-
-
-
+       
+       
+       
        /**
         * Returns the value of the variable (in SI units).
         */
        public double getValue() {
                if (getMethod == null) // Constant value
                        return lastValue;
-
+               
                try {
                        return (Double) getMethod.invoke(source) * multiplier;
                } catch (IllegalArgumentException e) {
@@ -745,14 +741,14 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
                        throw Reflection.handleWrappedException(e);
                }
        }
-
+       
        /**
         * Sets the value of the variable.
         * @param v New value for parameter in SI units.
         */
        public void setValue(double v) {
                checkState(true);
-
+               
                log.debug("Setting value " + v + " for " + this);
                if (setMethod == null) {
                        if (getMethod != null) {
@@ -763,7 +759,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
                        fireStateChanged();
                        return;
                }
-
+               
                try {
                        setMethod.invoke(source, v / multiplier);
                } catch (IllegalArgumentException e) {
@@ -774,14 +770,14 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
                        throw Reflection.handleWrappedException(e);
                }
        }
-
+       
        /**
         * Returns whether setting the value automatically is available.
         */
        public boolean isAutomaticAvailable() {
                return (getAutoMethod != null) && (setAutoMethod != null);
        }
-
+       
        /**
         * Returns whether the value is currently being set automatically.
         * Returns false if automatic setting is not available at all.
@@ -789,7 +785,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
        public boolean isAutomatic() {
                if (getAutoMethod == null)
                        return false;
-
+               
                try {
                        return (Boolean) getAutoMethod.invoke(source);
                } catch (IllegalArgumentException e) {
@@ -800,20 +796,20 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
                        throw Reflection.handleWrappedException(e);
                }
        }
-
+       
        /**
         * Sets whether the value should be set automatically.  Simply fires a
         * state change event if automatic setting is not available.
         */
        public void setAutomatic(boolean auto) {
                checkState(true);
-
+               
                if (setAutoMethod == null) {
                        log.debug("Setting automatic to " + auto + " for " + this + ", automatic not available");
                        fireStateChanged(); // in case something is out-of-sync
                        return;
                }
-
+               
                log.debug("Setting automatic to " + auto + " for " + this);
                lastAutomatic = auto;
                try {
@@ -826,8 +822,8 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
                        throw Reflection.handleWrappedException(e);
                }
        }
-
-
+       
+       
        /**
         * Returns the current Unit.  At the beginning it is the default unit of the UnitGroup.
         * @return The most recently set unit.
@@ -835,7 +831,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
        public Unit getCurrentUnit() {
                return currentUnit;
        }
-
+       
        /**
         * Sets the current Unit.  The unit must be one of those included in the UnitGroup.
         * @param u  The unit to set active.
@@ -848,8 +844,8 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
                currentUnit = u;
                fireStateChanged();
        }
-
-
+       
+       
        /**
         * Returns the UnitGroup associated with the parameter value.
         *
@@ -858,9 +854,9 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
        public UnitGroup getUnitGroup() {
                return units;
        }
-
-
-
+       
+       
+       
        /**
         * Add a listener to the model.  Adds the model as a listener to the value source if this
         * is the first listener.
@@ -869,7 +865,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
        @Override
        public void addChangeListener(EventListener l) {
                checkState(true);
-
+               
                if (listeners.isEmpty()) {
                        if (source != null) {
                                source.addChangeListener(this);
@@ -877,11 +873,11 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
                                lastAutomatic = isAutomatic();
                        }
                }
-
+               
                listeners.add(l);
                log.verbose(this + " adding listener (total " + listeners.size() + "): " + l);
        }
-
+       
        /**
         * Remove a listener from the model.  Removes the model from being a listener to the Component
         * if this was the last listener of the model.
@@ -890,15 +886,15 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
        @Override
        public void removeChangeListener(EventListener l) {
                checkState(false);
-
+               
                listeners.remove(l);
                if (listeners.isEmpty() && source != null) {
                        source.removeChangeListener(this);
                }
                log.verbose(this + " removing listener (total " + listeners.size() + "): " + l);
        }
-
-
+       
+       
        /**
         * Invalidates this model by removing all listeners and removing this from
         * listening to the source.  After invalidation no listeners can be added to this
@@ -908,7 +904,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
        public void invalidate() {
                log.verbose("Invalidating " + this);
                invalidator.invalidate();
-
+               
                if (!listeners.isEmpty()) {
                        log.warn("Invalidating " + this + " while still having listeners " + listeners);
                }
@@ -918,13 +914,13 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
                }
                MemoryManagement.collectable(this);
        }
-
-
+       
+       
        private void checkState(boolean error) {
                invalidator.check(error);
        }
-
-
+       
+       
        @Override
        protected void finalize() throws Throwable {
                super.finalize();
@@ -932,29 +928,29 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
                        log.warn(this + " being garbage-collected while having listeners " + listeners);
                }
        };
-
-
+       
+       
        /**
         * Fire a ChangeEvent to all listeners.
         */
        protected void fireStateChanged() {
                checkState(true);
-
+               
                EventObject event = new EventObject(this);
                ChangeEvent cevent = new ChangeEvent(this);
                firing++;
                // Copy the list before iterating to prevent concurrent modification exceptions.
                EventListener[] ls = listeners.toArray(new EventListener[0]);
                for (EventListener l : ls) {
-                       if ( l instanceof StateChangeListener ) {
-                               ((StateChangeListener)l).stateChanged(event);
-                       } else if ( l instanceof ChangeListener ) {
-                               ((ChangeListener)l).stateChanged(cevent);
+                       if (l instanceof StateChangeListener) {
+                               ((StateChangeListener) l).stateChanged(event);
+                       } else if (l instanceof ChangeListener) {
+                               ((ChangeListener) l).stateChanged(cevent);
                        }
                }
                firing--;
        }
-
+       
        /**
         * Called when the component changes.  Checks whether the modeled value has changed, and if
         * it has, updates lastValue and generates ChangeEvents for all listeners of the model.
@@ -962,7 +958,7 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
        @Override
        public void stateChanged(EventObject e) {
                checkState(true);
-
+               
                double v = getValue();
                boolean b = isAutomatic();
                if (lastValue == v && lastAutomatic == b)
@@ -971,8 +967,8 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
                lastAutomatic = b;
                fireStateChanged();
        }
-
-
+       
+       
        /**
         * Explain the DoubleModel as a String.
         */
diff --git a/core/src/net/sf/openrocket/util/ExpressionParser.java b/core/src/net/sf/openrocket/util/ExpressionParser.java
new file mode 100644 (file)
index 0000000..091a379
--- /dev/null
@@ -0,0 +1,37 @@
+package net.sf.openrocket.util;
+
+import de.congrace.exp4j.Calculable;
+import de.congrace.exp4j.ExpressionBuilder;
+import de.congrace.exp4j.UnknownFunctionException;
+import de.congrace.exp4j.UnparsableExpressionException;
+
+public class ExpressionParser {
+       
+       
+       public double parse(String expression) throws InvalidExpressionException {
+               try {
+                       ExpressionBuilder builder = new ExpressionBuilder(modify(expression));
+                       Calculable calc = builder.build();
+                       return calc.calculate();
+               } catch (java.lang.NumberFormatException e) {
+                       throw new InvalidExpressionException("Invalid expression: " + expression, e);
+               } catch (UnknownFunctionException e) {
+                       throw new InvalidExpressionException("Invalid expression: " + expression, e);
+               } catch (UnparsableExpressionException e) {
+                       throw new InvalidExpressionException("Invalid expression: " + expression, e);
+               } catch (java.util.EmptyStackException e) {
+                       throw new InvalidExpressionException("Invalid expression: " + expression, e);
+               }
+       }
+       
+       private String modify(String exp) throws InvalidExpressionException {
+               exp = exp.replaceAll("(\\d+)\\s+(\\d+)\\s*/\\s*(\\d+)", "($1+$2/$3)");
+               exp = exp.replace(',', '.');
+               // Disallow spaces between numbers - default is to remove spaces!
+               if (exp.matches(".*[0-9.]\\s+[0-9.].*")) {
+                       throw new InvalidExpressionException("Invalid expression: " + exp);
+               }
+               return exp;
+       }
+       
+}
diff --git a/core/src/net/sf/openrocket/util/InvalidExpressionException.java b/core/src/net/sf/openrocket/util/InvalidExpressionException.java
new file mode 100644 (file)
index 0000000..118cefe
--- /dev/null
@@ -0,0 +1,22 @@
+package net.sf.openrocket.util;
+
+/**
+ * Exception indicating an invalid expression.
+ * 
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public class InvalidExpressionException extends Exception {
+       
+       public InvalidExpressionException(String message) {
+               super(message);
+       }
+       
+       public InvalidExpressionException(Throwable cause) {
+               super(cause);
+       }
+       
+       public InvalidExpressionException(String message, Throwable cause) {
+               super(message, cause);
+       }
+       
+}
diff --git a/core/src/net/sf/openrocket/util/exp4j/AbstractExpression.java b/core/src/net/sf/openrocket/util/exp4j/AbstractExpression.java
deleted file mode 100644 (file)
index 908e507..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
-   Copyright 2011 frank asseg
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-
- */
-package net.sf.openrocket.util.exp4j;
-
-import java.text.NumberFormat;
-import java.util.List;
-
-/**
- * Abstract base class for mathematical expressions
- * 
- * @author fas@congrace.de
- */
-abstract class AbstractExpression {
-       private final String expression;
-       private final Token[] tokens;
-       private final String[] variableNames;
-       private final NumberFormat numberFormat = NumberFormat.getInstance();
-
-       /**
-        * Construct a new {@link AbstractExpression}
-        * 
-        * @param expression
-        *            the mathematical expression to be used
-        * @param tokens
-        *            the {@link Token}s in the expression
-        * @param variableNames
-        *            an array of variable names which are used in the expression
-        */
-       AbstractExpression(String expression, Token[] tokens, String[] variableNames) {
-               this.expression = expression;
-               this.tokens = tokens;
-               this.variableNames = variableNames;
-       }
-
-       /**
-        * get the mathematical expression {@link String}
-        * 
-        * @return the expression
-        */
-       public String getExpression() {
-               return expression;
-       }
-
-       /**
-        * get the used {@link NumberFormat}
-        * 
-        * @return the used {@link NumberFormat}
-        */
-       public NumberFormat getNumberFormat() {
-               return numberFormat;
-       }
-
-       /**
-        * get the {@link Token}s
-        * 
-        * @return the array of {@link Token}s
-        */
-       Token[] getTokens() {
-               return tokens;
-       }
-
-       /**
-        * get the variable names
-        * 
-        * @return the {@link List} of variable names
-        */
-       String[] getVariableNames() {
-               return variableNames;
-       }
-
-}
diff --git a/core/src/net/sf/openrocket/util/exp4j/Calculable.java b/core/src/net/sf/openrocket/util/exp4j/Calculable.java
deleted file mode 100644 (file)
index 3fecba5..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-package net.sf.openrocket.util.exp4j;
-
-/**
- * This is the basic result class of the exp4j {@link ExpressionBuilder}
- * 
- * @author ruckus
- * 
- */
-public interface Calculable {
-       /**
-        * calculate the result of the expression
-        * 
-        * @return the result of the calculation
-        */
-       public double calculate();
-
-       /**
-        * return the expression in reverse polish postfix notation
-        * 
-        * @return the expression used to construct this {@link Calculable}
-        */
-       public String getExpression();
-
-       /**
-        * set a variable value for the calculation
-        * 
-        * @param name
-        *            the variable name
-        * @param value
-        *            the value of the variable
-        */
-       public void setVariable(String name, double value);
-}
diff --git a/core/src/net/sf/openrocket/util/exp4j/CalculationToken.java b/core/src/net/sf/openrocket/util/exp4j/CalculationToken.java
deleted file mode 100644 (file)
index 75d9dc9..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
-   Copyright 2011 frank asseg
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-
- */
-package net.sf.openrocket.util.exp4j;
-
-import java.util.Map;
-import java.util.Stack;
-
-abstract class CalculationToken extends Token {
-
-       CalculationToken(String value) {
-               super(value);
-       }
-
-       abstract void mutateStackForCalculation(Stack<Double> stack, Map<String, Double> variableValues);
-
-}
diff --git a/core/src/net/sf/openrocket/util/exp4j/CommandlineInterpreter.java b/core/src/net/sf/openrocket/util/exp4j/CommandlineInterpreter.java
deleted file mode 100644 (file)
index 443545f..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
-   Copyright 2011 frank asseg
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-
- */
-package net.sf.openrocket.util.exp4j;
-/**
- * Simple commandline interpreter for mathematical expressions the interpreter
- * takes a mathematical expressions as a {@link String} argument, evaluates it
- * and prints out the result.
- * 
- * 
- * <pre>
- * java de.congrace.exp4j.CommandlineInterpreter "2 * log(2.2223) - ((2-3.221) * 14.232^2)"
- * > 248.91042049521056
- * </pre>
- * 
- * @author fas@congrace.de
- * 
- */
-public class CommandlineInterpreter {
-       private static void calculateExpression(String string) {
-               try {
-                       final PostfixExpression pe = PostfixExpression.fromInfix(string);
-                       System.out.println(pe.calculate());
-               } catch (UnparsableExpressionException e) {
-                       e.printStackTrace();
-               } catch (UnknownFunctionException e) {
-                       e.printStackTrace();
-               }
-       }
-
-       public static void main(String[] args) {
-               if (args.length != 1) {
-                       printUsage();
-               } else {
-                       calculateExpression(args[0]);
-               }
-       }
-
-       private static void printUsage() {
-               final StringBuilder usage = new StringBuilder();
-               usage.append("Commandline Expression Parser\n\n").append("Example: ").append("\n").append("java -jar exp4j.jar \"2.12 * log(23) * (12 - 4)\"\n\n")
-                               .append("written by fas@congrace.de");
-               System.err.println(usage.toString());
-       }
-}
diff --git a/core/src/net/sf/openrocket/util/exp4j/CustomFunction.java b/core/src/net/sf/openrocket/util/exp4j/CustomFunction.java
deleted file mode 100644 (file)
index 8a437af..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-package net.sf.openrocket.util.exp4j;
-import java.util.Map;
-import java.util.Stack;
-
-import net.sf.openrocket.util.exp4j.FunctionToken.Function;
-
-/**
- * this classed is used to create custom functions for exp4j<br/>
- * <br/>
- * <b>Example</b><br/>
- * <code><pre>{@code 
- * CustomFunction fooFunc = new CustomFunction("foo") {
- *             public double applyFunction(double value) {
- *                     return value*Math.E;
- *             }
- * };
- * double varX=12d;
- * Calculable calc = new ExpressionBuilder("foo(x)").withCustomFunction(fooFunc).withVariable("x",varX).build();
- * assertTrue(calc.calculate() == Math.E * varX);
- * }</pre></code>
- * 
- * @author ruckus
- * 
- */
-public abstract class CustomFunction extends CalculationToken {
-    private int argc=1;
-
-       /**
-        * create a new single value input CustomFunction with a set name
-        * 
-        * @param value
-        *            the name of the function (e.g. foo)
-        */
-       protected CustomFunction(String value) throws InvalidCustomFunctionException{
-               super(value);
-               for (Function f:Function.values()) {
-                       if (value.equalsIgnoreCase(f.toString())){
-                               throw new InvalidCustomFunctionException(value + " is already reserved as a function name");
-                       }
-               }
-       }
-
-    /**
-     * create a new single value input CustomFunction with a set name
-     * 
-     * @param value
-     *            the name of the function (e.g. foo)
-     */
-    protected CustomFunction(String value,int argumentCount) throws InvalidCustomFunctionException{
-        super(value);
-        this.argc=argumentCount;
-        for (Function f:Function.values()) {
-            if (value.equalsIgnoreCase(f.toString())){
-                throw new InvalidCustomFunctionException(value + " is already reserved as a function name");
-            }
-        }
-    }
-
-    /**
-        * apply the function to a value
-        * 
-        * @param values
-        *            the values to which the function should be applied.
-        * @return the function value
-        */
-       public abstract double applyFunction(double[] values);
-
-    @Override
-       void mutateStackForCalculation(Stack<Double> stack, Map<String, Double> variableValues) {
-           double[] args=new double[argc];
-           for (int i=0;i<argc;i++) {
-               args[i]=stack.pop();
-           }
-               stack.push(this.applyFunction(args));
-       }
-
-       @Override
-       void mutateStackForInfixTranslation(Stack<Token> operatorStack, StringBuilder output) {
-               operatorStack.push(this);
-       }
-       public int getArgumentCount() {
-           return argc;
-       }
-}
diff --git a/core/src/net/sf/openrocket/util/exp4j/ExpressionBuilder.java b/core/src/net/sf/openrocket/util/exp4j/ExpressionBuilder.java
deleted file mode 100644 (file)
index 8e4a430..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-package net.sf.openrocket.util.exp4j;
-
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
-/**
- * This is Builder implementation for the exp4j API used to create a Calculable
- * instance for the user
- * 
- * @author ruckus
- * 
- */
-public class ExpressionBuilder {
-       private final Map<String, Double> variables = new LinkedHashMap<String, Double>();
-       private final Set<CustomFunction> customFunctions = new HashSet<CustomFunction>();
-
-       private String expression;
-
-       /**
-        * Create a new ExpressionBuilder
-        * 
-        * @param expression
-        *            the expression to evaluate
-        */
-       public ExpressionBuilder(String expression) {
-               this.expression = expression;
-       }
-
-       /**
-        * build a new {@link Calculable} from the expression using the supplied
-        * variables
-        * 
-        * @return the {@link Calculable} which can be used to evaluate the
-        *         expression
-        * @throws UnknownFunctionException
-        *             when an unrecognized function name is used in the expression
-        * @throws UnparsableExpressionException
-        *             if the expression could not be parsed
-        */
-       public Calculable build() throws UnknownFunctionException, UnparsableExpressionException {
-               if (expression.indexOf('=') == -1 && !variables.isEmpty()) {
-
-                       // User supplied an expression without leading "f(...)="
-                       // so we just append the user function to a proper "f()="
-                       // for PostfixExpression.fromInfix()
-                       StringBuilder function = new StringBuilder("f(");
-                       for (String var : variables.keySet()) {
-                               function.append(var).append(',');
-                       }
-                       expression = function.deleteCharAt(function.length() - 1).toString() + ")=" + expression;
-               }
-               // create the PostfixExpression and return it as a Calculable
-               PostfixExpression delegate = PostfixExpression.fromInfix(expression, customFunctions);
-               for (String var : variables.keySet()) {
-                       if (variables.get(var) != null) {
-                               delegate.setVariable(var, variables.get(var));
-                       }
-                       for (CustomFunction custom:customFunctions){
-                               if (custom.getValue().equals(var)){
-                                       throw new UnparsableExpressionException("variable '" + var + "' cannot have the same name as a custom function " + custom.getValue());
-                               }
-                       }
-               }
-               return delegate;
-       }
-
-       /**
-        * add a custom function instance for the evaluator to recognize
-        * 
-        * @param function
-        *            the {@link CustomFunction} to add
-        * @return the {@link ExpressionBuilder} instance
-        */
-       public ExpressionBuilder withCustomFunction(CustomFunction function) {
-               customFunctions.add(function);
-               return this;
-       }
-
-       public ExpressionBuilder withCustomFunctions(Collection<CustomFunction> functions) {
-               customFunctions.addAll(functions);
-               return this;
-       }
-
-       /**
-        * set the value for a variable
-        * 
-        * @param variableName
-        *            the variable name e.g. "x"
-        * @param value
-        *            the value e.g. 2.32d
-        * @return the {@link ExpressionBuilder} instance
-        */
-       public ExpressionBuilder withVariable(String variableName, double value) {
-               variables.put(variableName, value);
-               return this;
-       }
-
-       /**
-        * set the variables names used in the expression without setting their
-        * values
-        * 
-        * @param variableNames
-        *            vararg {@link String} of the variable names used in the
-        *            expression
-        * @return the ExpressionBuilder instance
-        */
-       public ExpressionBuilder withVariableNames(String... variableNames) {
-               for (String variable : variableNames) {
-                       variables.put(variable, null);
-               }
-               return this;
-       }
-
-       /**
-        * set the values for variables
-        * 
-        * @param variableMap
-        *            a map of variable names to variable values
-        * @return the {@link ExpressionBuilder} instance
-        */
-       public ExpressionBuilder withVariables(Map<String, Double> variableMap) {
-               for (Entry<String, Double> v : variableMap.entrySet()) {
-                       variables.put(v.getKey(), v.getValue());
-               }
-               return this;
-       }
-}
diff --git a/core/src/net/sf/openrocket/util/exp4j/FunctionSeparatorToken.java b/core/src/net/sf/openrocket/util/exp4j/FunctionSeparatorToken.java
deleted file mode 100644 (file)
index 0e69f8a..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-package net.sf.openrocket.util.exp4j;
-
-import java.util.Stack;
-
-public class FunctionSeparatorToken extends Token{
-    public FunctionSeparatorToken() {
-        super(",");
-    }
-    @Override
-    void mutateStackForInfixTranslation(Stack<Token> operatorStack, StringBuilder output) {
-        Token token;
-        while (!((token=operatorStack.peek()) instanceof ParenthesisToken) && !token.getValue().equals("(")){
-            output.append(operatorStack.pop().getValue()).append(" ");
-        }
-    }
-
-}
diff --git a/core/src/net/sf/openrocket/util/exp4j/FunctionToken.java b/core/src/net/sf/openrocket/util/exp4j/FunctionToken.java
deleted file mode 100644 (file)
index 10c4ba4..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
-Copyright 2011 frank asseg
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
- */
-package net.sf.openrocket.util.exp4j;
-
-import java.util.Map;
-import java.util.Stack;
-
-/**
- * A {@link Token} for functions
- * 
- * @author fas@congrace.de
- * 
- */
-class FunctionToken extends CalculationToken {
-       /**
-        * the functionNames that can be used in an expression
-        * 
-        * @author ruckus
-        * 
-        */
-       enum Function {
-               ABS, ACOS, ASIN, ATAN, CBRT, CEIL, COS, COSH, EXP, EXPM1, FLOOR, LOG, SIN, SINH, SQRT, TAN, TANH
-       }
-
-       private Function function;
-
-       /**
-        * construct a new {@link FunctionToken}
-        * 
-        * @param value
-        *            the name of the function
-        * @throws UnknownFunctionException
-        *             if an unknown function name is encountered
-        */
-       FunctionToken(String value) throws UnknownFunctionException {
-               super(value);
-               try {
-                       function = Function.valueOf(value.toUpperCase());
-               } catch (IllegalArgumentException e) {
-                       throw new UnknownFunctionException(value);
-               }
-               if (function == null) {
-                       throw new UnknownFunctionException(value);
-               }
-       }
-
-       /**
-        * apply a function to a value x
-        * 
-        * @param x
-        *            the value the function should be applied to
-        * @return the result of the function
-        */
-       double applyFunction(double x) {
-               switch (function) {
-               case ABS:
-                       return Math.abs(x);
-               case ACOS:
-                       return Math.acos(x);
-               case ASIN:
-                       return Math.asin(x);
-               case ATAN:
-                       return Math.atan(x);
-               case CBRT:
-                       return Math.cbrt(x);
-               case CEIL:
-                       return Math.ceil(x);
-               case COS:
-                       return Math.cos(x);
-               case COSH:
-                       return Math.cosh(x);
-               case EXP:
-                       return Math.exp(x);
-               case EXPM1:
-                       return Math.expm1(x);
-               case FLOOR:
-                       return Math.floor(x);
-               case LOG:
-                       return Math.log(x);
-               case SIN:
-                       return Math.sin(x);
-               case SINH:
-                       return Math.sinh(x);
-               case SQRT:
-                       return Math.sqrt(x);
-               case TAN:
-                       return Math.tan(x);
-               case TANH:
-                       return Math.tanh(x);
-               default:
-                       return Double.NaN; // should not happen ;)
-               }
-       }
-
-       /**
-        * get the {@link Function}
-        * 
-        * @return the correspoding {@link Function}
-        */
-       Function getFunction() {
-               return function;
-       }
-
-       @Override
-       void mutateStackForCalculation(Stack<Double> stack, Map<String, Double> variableValues) {
-               stack.push(this.applyFunction(stack.pop()));
-       }
-
-       @Override
-       void mutateStackForInfixTranslation(Stack<Token> operatorStack, StringBuilder output) {
-               operatorStack.push(this);
-       }
-}
diff --git a/core/src/net/sf/openrocket/util/exp4j/InfixTranslator.java b/core/src/net/sf/openrocket/util/exp4j/InfixTranslator.java
deleted file mode 100644 (file)
index a107009..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
-   Copyright 2011 frank asseg
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-
- */
-package net.sf.openrocket.util.exp4j;
-
-import java.util.Set;
-import java.util.Stack;
-
-/**
- * Translate a mathematical expression in human readable infix notation to a
- * Reverse Polish Notation (postfix) expression for easier parsing. by
- * implementing the shunting yard algorithm by dijkstra
- * 
- * @author fas@congrace.de
- */
-class InfixTranslator {
-
-       private static String substituteUnaryOperators(String expr) {
-               final StringBuilder exprBuilder = new StringBuilder(expr.length());
-               final char[] data = expr.toCharArray();
-               char lastChar = ' ';
-               for (int i = 0; i < expr.length(); i++) {
-                       if (exprBuilder.length() > 0) {
-                               lastChar = exprBuilder.charAt(exprBuilder.length() - 1);
-                       }
-                       final char c = data[i];
-                       switch (c) {
-                       case '+':
-                               if (i > 0 && lastChar != '(' && !(OperatorToken.isOperator(lastChar))) {
-                                       exprBuilder.append(c);
-                               }
-                               break;
-                       case '-':
-                               if (i > 0 && lastChar != '(' && !(OperatorToken.isOperator(lastChar))) {
-                                       exprBuilder.append(c);
-                               } else {
-                                       exprBuilder.append('#');
-                               }
-                               break;
-                       default:
-                               if (!Character.isWhitespace(c)) {
-                                       exprBuilder.append(c);
-                               }
-                       }
-               }
-               return exprBuilder.toString();
-       }
-
-       /**
-        * Delegation method for simple expression without variables or custom
-        * functions
-        * 
-        * @param infixExpression
-        *            the infix expression to be translated
-        * @return translated RNP postfix expression
-        * @throws UnparsableExpressionException
-        *             when the expression is invalid
-        * @throws UnknownFunctionException
-        *             when an unknown function has been used in the input.
-        */
-       static String toPostfixExpression(String infixExpression) throws UnparsableExpressionException, UnknownFunctionException {
-               return toPostfixExpression(infixExpression, null, null);
-       }
-
-       /**
-        * implement the shunting yard algorithm
-        * 
-        * @param infixExpression
-        *            the human readable expression which should be translated to
-        *            RPN
-        * @param variableNames
-        *            the variable names used in the expression
-        * @param customFunctions
-        *            the CustomFunction implementations used
-        * @return the expression in postfix format
-        * @throws UnparsableExpressionException
-        *             if the expression could not be translated to RPN
-        * @throws UnknownFunctionException
-        *             if an unknown function was encountered
-        */
-       static String toPostfixExpression(String infixExpression, String[] variableNames, Set<CustomFunction> customFunctions)
-                       throws UnparsableExpressionException, UnknownFunctionException {
-               infixExpression = substituteUnaryOperators(infixExpression);
-               final Token[] tokens = new Tokenizer(variableNames, customFunctions).tokenize(infixExpression);
-               final StringBuilder output = new StringBuilder(tokens.length);
-               final Stack<Token> operatorStack = new Stack<Token>();
-               for (final Token token : tokens) {
-                       token.mutateStackForInfixTranslation(operatorStack, output);
-               }
-               // all tokens read, put the rest of the operations on the output;
-               while (operatorStack.size() > 0) {
-                       output.append(operatorStack.pop().getValue()).append(" ");
-               }
-               return output.toString().trim();
-       }
-}
diff --git a/core/src/net/sf/openrocket/util/exp4j/InvalidCustomFunctionException.java b/core/src/net/sf/openrocket/util/exp4j/InvalidCustomFunctionException.java
deleted file mode 100644 (file)
index de2da3c..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-package net.sf.openrocket.util.exp4j;
-
-public class InvalidCustomFunctionException extends Exception{
-       private static final long serialVersionUID = 1L;
-
-       public InvalidCustomFunctionException(String message) {
-               super(message);
-       }
-}
diff --git a/core/src/net/sf/openrocket/util/exp4j/NumberToken.java b/core/src/net/sf/openrocket/util/exp4j/NumberToken.java
deleted file mode 100644 (file)
index 97017bd..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
-   Copyright 2011 frank asseg
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-
- */
-package net.sf.openrocket.util.exp4j;
-
-import java.util.Map;
-import java.util.Stack;
-
-/**
- * A {@link Token} for Numbers
- * 
- * @author fas@congrace.de
- * 
- */
-class NumberToken extends CalculationToken {
-
-       private final double doubleValue;
-
-       /**
-        * construct a new {@link NumberToken}
-        * 
-        * @param value
-        *            the value of the number as a {@link String}
-        */
-       NumberToken(String value) {
-               super(value);
-               this.doubleValue = Double.parseDouble(value);
-       }
-
-       @Override
-       public boolean equals(Object obj) {
-               if (obj instanceof NumberToken) {
-                       final NumberToken t = (NumberToken) obj;
-                       return t.getValue().equals(this.getValue());
-               }
-               return false;
-       }
-
-       @Override
-       public int hashCode() {
-               return getValue().hashCode();
-       }
-
-       @Override
-       void mutateStackForCalculation(Stack<Double> stack, Map<String, Double> variableValues) {
-               stack.push(this.doubleValue);
-       }
-
-       @Override
-       void mutateStackForInfixTranslation(Stack<Token> operatorStack, StringBuilder output) {
-               output.append(this.getValue()).append(' ');
-       }
-}
diff --git a/core/src/net/sf/openrocket/util/exp4j/OperatorToken.java b/core/src/net/sf/openrocket/util/exp4j/OperatorToken.java
deleted file mode 100644 (file)
index cff7884..0000000
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
-   Copyright 2011 frank asseg
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-
- */
-package net.sf.openrocket.util.exp4j;
-
-import java.util.Map;
-import java.util.Stack;
-
-/**
- * {@link Token} for Operations like +,-,*,/,% and ^
- * 
- * @author fas@congrace.de
- */
-class OperatorToken extends CalculationToken {
-
-       /**
-        * the valid {@link Operation}s for the {@link OperatorToken}
-        * 
-        * @author fas@congrace.de
-        */
-       enum Operation {
-               ADDITION(1, true), SUBTRACTION(1, true), MULTIPLICATION(2, true), DIVISION(2, true), MODULO(2, true), EXPONENTIATION(3, false), UNARY_MINUS(4, false), UNARY_PLUS(
-                               4, false);
-               private final int precedence;
-               private final boolean leftAssociative;
-
-               private Operation(int precedence, boolean leftAssociative) {
-                       this.precedence = precedence;
-                       this.leftAssociative = leftAssociative;
-               }
-       }
-
-       /**
-        * return a corresponding {@link Operation} for a symbol
-        * 
-        * @param c
-        *            the symbol of the operation
-        * @return the corresponding {@link Operation}
-        */
-       static Operation getOperation(char c) {
-               switch (c) {
-               case '+':
-                       return Operation.ADDITION;
-               case '-':
-                       return Operation.SUBTRACTION;
-               case '*':
-                       return Operation.MULTIPLICATION;
-               case '/':
-                       return Operation.DIVISION;
-               case '^':
-                       return Operation.EXPONENTIATION;
-               case '#':
-                       return Operation.UNARY_MINUS;
-               case '%':
-                       return Operation.MODULO;
-               default:
-                       return null;
-               }
-       }
-
-       static boolean isOperator(char c) {
-               return getOperation(c) != null;
-       }
-
-       private final Operation operation;
-
-       /**
-        * construct a new {@link OperatorToken}
-        * 
-        * @param value
-        *            the symbol (e.g.: '+')
-        * @param operation
-        *            the {@link Operation} of this {@link Token}
-        */
-       OperatorToken(String value, Operation operation) {
-               super(value);
-               this.operation = operation;
-       }
-
-       /**
-        * apply the {@link Operation}
-        * 
-        * @param values
-        *            the doubles to operate on
-        * @return the result of the {@link Operation}
-        */
-       double applyOperation(double... values) {
-               switch (operation) {
-               case ADDITION:
-                       return values[0] + values[1];
-               case SUBTRACTION:
-                       return values[0] - values[1];
-               case MULTIPLICATION:
-                       return values[0] * values[1];
-               case EXPONENTIATION:
-                       return Math.pow(values[0], values[1]);
-               case DIVISION:
-                       return values[0] / values[1];
-               case UNARY_MINUS:
-                       return -values[0];
-               case UNARY_PLUS:
-                       return values[0];
-               case MODULO:
-                       return values[0] % values[1];
-               default:
-                       return 0;
-               }
-       }
-
-       @Override
-       public boolean equals(Object obj) {
-               if (obj instanceof OperatorToken) {
-                       final OperatorToken t = (OperatorToken) obj;
-                       return t.getValue().equals(this.getValue());
-               }
-               return false;
-       }
-
-       int getOperandCount() {
-               switch (operation) {
-               case ADDITION:
-               case SUBTRACTION:
-               case MULTIPLICATION:
-               case DIVISION:
-               case EXPONENTIATION:
-               case MODULO:
-                       return 2;
-               case UNARY_MINUS:
-               case UNARY_PLUS:
-                       return 1;
-               default:
-                       return 0;
-               }
-       }
-
-       /**
-        * get the {@link Operation} of this {@link Token}
-        * 
-        * @return the {@link Operation}
-        */
-       Operation getOperation() {
-               return operation;
-       }
-
-       int getPrecedence() {
-               return operation.precedence;
-       }
-
-       @Override
-       public int hashCode() {
-               return getValue().hashCode();
-       }
-
-       /**
-        * check if the operation is left associative
-        * 
-        * @return true if left associative, otherwise false
-        */
-       boolean isLeftAssociative() {
-               return operation.leftAssociative;
-       }
-
-       @Override
-       void mutateStackForCalculation(Stack<Double> stack, Map<String, Double> variableValues) {
-               if (this.getOperandCount() == 2) {
-                       final double n2 = stack.pop();
-                       final double n1 = stack.pop();
-                       stack.push(this.applyOperation(n1, n2));
-               } else if (this.getOperandCount() == 1) {
-                       final double n1 = stack.pop();
-                       stack.push(this.applyOperation(n1));
-               }
-       }
-
-       @Override
-       void mutateStackForInfixTranslation(Stack<Token> operatorStack, StringBuilder output) {
-               Token before;
-               while (!operatorStack.isEmpty() && (before = operatorStack.peek()) != null && (before instanceof OperatorToken || before instanceof FunctionToken)) {
-                       if (before instanceof FunctionToken) {
-                               operatorStack.pop();
-                               output.append(before.getValue()).append(" ");
-                       } else {
-                               final OperatorToken stackOperator = (OperatorToken) before;
-                               if (this.isLeftAssociative() && this.getPrecedence() <= stackOperator.getPrecedence()) {
-                                       output.append(operatorStack.pop().getValue()).append(" ");
-                               } else if (!this.isLeftAssociative() && this.getPrecedence() < stackOperator.getPrecedence()) {
-                                       output.append(operatorStack.pop().getValue()).append(" ");
-                               } else {
-                                       break;
-                               }
-                       }
-               }
-               operatorStack.push(this);
-       }
-}
diff --git a/core/src/net/sf/openrocket/util/exp4j/ParenthesisToken.java b/core/src/net/sf/openrocket/util/exp4j/ParenthesisToken.java
deleted file mode 100644 (file)
index 2a36581..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
-   Copyright 2011 frank asseg
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-
- */
-package net.sf.openrocket.util.exp4j;
-
-import java.util.Stack;
-
-/**
- * Token for parenthesis
- * 
- * @author fas@congrace.de
- */
-class ParenthesisToken extends Token {
-
-       ParenthesisToken(String value) {
-               super(value);
-       }
-
-       @Override
-       public boolean equals(Object obj) {
-               if (obj instanceof ParenthesisToken) {
-                       final ParenthesisToken t = (ParenthesisToken) obj;
-                       return t.getValue().equals(this.getValue());
-               }
-               return false;
-       }
-
-       @Override
-       public int hashCode() {
-               return getValue().hashCode();
-       }
-
-       /**
-        * check the direction of the parenthesis
-        * 
-        * @return true if it's a left parenthesis (open) false if it is a right
-        *         parenthesis (closed)
-        */
-       boolean isOpen() {
-               return getValue().equals("(") || getValue().equals("[") || getValue().equals("{");
-       }
-
-       @Override
-       void mutateStackForInfixTranslation(Stack<Token> operatorStack, StringBuilder output) {
-               if (this.isOpen()) {
-                       operatorStack.push(this);
-               } else {
-                       Token next;
-                       while ((next = operatorStack.peek()) instanceof OperatorToken || next instanceof FunctionToken || next instanceof CustomFunction
-                                       || (next instanceof ParenthesisToken && !((ParenthesisToken) next).isOpen())) {
-                               output.append(operatorStack.pop().getValue()).append(" ");
-                       }
-                       operatorStack.pop();
-               }
-       }
-}
diff --git a/core/src/net/sf/openrocket/util/exp4j/PostfixExpression.java b/core/src/net/sf/openrocket/util/exp4j/PostfixExpression.java
deleted file mode 100644 (file)
index 36ef579..0000000
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
-   Copyright 2011 frank asseg
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-
- */
-package net.sf.openrocket.util.exp4j;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.Stack;
-
-/**
- * Class for calculating values from a RPN postfix expression.<br/>
- * The default way to create a new instance of {@link PostfixExpression} is by
- * using the static factory method fromInfix()
- * 
- * @author fas@congrace.de
- */
-public final class PostfixExpression extends AbstractExpression implements Calculable {
-       /**
-        * Factory method for creating {@link PostfixExpression}s from human
-        * readable infix expressions
-        * 
-        * @param expression
-        *            the infix expression to be used
-        * @return an equivalent {@link PostfixExpression}
-        * @throws UnparsableExpressionException
-        *             if the expression was invalid
-        * @throws UnknownFunctionException
-        *             if an unknown function has been used
-        * @deprecated please use {@link ExpressionBuilder} API
-        */
-    @Deprecated
-       public static PostfixExpression fromInfix(String expression) throws UnparsableExpressionException, UnknownFunctionException {
-               return fromInfix(expression, null);
-       }
-
-       /**
-        * Factory method for creating {@link PostfixExpression}s from human
-        * readable infix expressions
-        * 
-        * @param expression
-        *            the infix expression to be used
-        * @param customFunctions
-        *            the CustomFunction implementations used
-        * @return an equivalent {@link PostfixExpression}
-        * @throws UnparsableExpressionException
-        *             if the expression was invalid
-        * @throws UnknownFunctionException
-        *             if an unknown function has been used
-        * @deprecated please use {@link ExpressionBuilder}
-        */
-    @Deprecated
-       public static PostfixExpression fromInfix(String expression, Set<CustomFunction> customFunctions) throws UnparsableExpressionException,
-                       UnknownFunctionException {
-               String[] variables = null;
-               int posStart, posEnd;
-               if ((posStart = expression.indexOf('=')) > 0) {
-                       String functionDef = expression.substring(0, posStart);
-                       expression = expression.substring(posStart + 1);
-                       if ((posStart = functionDef.indexOf('(')) > 0 && (posEnd = functionDef.indexOf(')')) > 0) {
-                               variables = functionDef.substring(posStart + 1, posEnd).split(",");
-                       }
-               }
-               return new PostfixExpression(InfixTranslator.toPostfixExpression(expression, variables, customFunctions), variables, customFunctions);
-       }
-
-       private final Map<String, Double> variableValues = new HashMap<String, Double>();
-
-       /**
-        * Construct a new simple {@link PostfixExpression}
-        * 
-        * @param expression
-        *            the postfix expression to be calculated
-        * @param variableNames
-        *            the variable names in the expression
-        * @param customFunctions
-        *            the CustomFunction implementations used
-        * @throws UnparsableExpressionException
-        *             when expression is invalid
-        * @throws UnknownFunctionException
-        *             when an unknown function has been used
-        */
-       private PostfixExpression(String expression, String[] variableNames, Set<CustomFunction> customFunctions) throws UnparsableExpressionException,
-                       UnknownFunctionException {
-               super(expression, new Tokenizer(variableNames, customFunctions).tokenize(expression), variableNames);
-       }
-
-       /**
-        * delegate the calculation of a simple expression without variables
-        * 
-        * @return the result
-        */
-       public double calculate() {
-               return calculate(null);
-       }
-
-       /**
-        * calculate the result of the expression and substitute the variables by
-        * their values beforehand
-        * 
-        * @param values
-        *            the variable values to be substituted
-        * @return the result of the calculation
-        * @throws IllegalArgumentException
-        *             if the variables are invalid
-        */
-       public double calculate(double... values) throws IllegalArgumentException {
-               if (getVariableNames() == null && values != null) {
-                       throw new IllegalArgumentException("there are no variables to set values");
-               } else if (getVariableNames() != null && values == null && variableValues.isEmpty()) {
-                       throw new IllegalAccessError("variable values have to be set");
-               } else if (values != null && values.length != getVariableNames().length) {
-                       throw new IllegalArgumentException("The are an unequal number of variables and arguments");
-               }
-               int i = 0;
-               if (getVariableNames() != null && values != null) {
-                       for (double val : values) {
-                               variableValues.put(getVariableNames()[i++], val);
-                       }
-               }
-               final Stack<Double> stack = new Stack<Double>();
-               for (final Token t : getTokens()) {
-                       ((CalculationToken) t).mutateStackForCalculation(stack, variableValues);
-               }
-               return stack.pop();
-       }
-
-       public void setVariable(String name, double value) {
-               variableValues.put(name, value);
-       }
-}
diff --git a/core/src/net/sf/openrocket/util/exp4j/Token.java b/core/src/net/sf/openrocket/util/exp4j/Token.java
deleted file mode 100644 (file)
index 3e0024c..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
-   Copyright 2011 frank asseg
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-
- */
-package net.sf.openrocket.util.exp4j;
-
-import java.util.Stack;
-
-/**
- * Superclass for tokenized Strings
- * 
- * @author fas@congrace.de
- */
-abstract class Token {
-       private final String value;
-
-       /**
-        * construct a new {@link Token}
-        * 
-        * @param value
-        *            the value of the {@link Token}
-        */
-       Token(String value) {
-               super();
-               this.value = value;
-       }
-
-       /**
-        * get the value (String representation) of the token
-        * 
-        * @return the value
-        */
-       String getValue() {
-               return value;
-       }
-
-       abstract void mutateStackForInfixTranslation(Stack<Token> operatorStack, StringBuilder output);
-}
diff --git a/core/src/net/sf/openrocket/util/exp4j/Tokenizer.java b/core/src/net/sf/openrocket/util/exp4j/Tokenizer.java
deleted file mode 100644 (file)
index 8290de3..0000000
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
-   Copyright 2011 frank asseg
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-
- */
-package net.sf.openrocket.util.exp4j;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import net.sf.openrocket.util.exp4j.FunctionToken.Function;
-
-/**
- * Class for tokenizing mathematical expressions by breaking an expression up
- * into multiple different {@link Token}s
- * 
- * @author fas@congrace.de
- */
-class Tokenizer {
-       private String[] variableNames;
-       private final Set<String> functionNames = new HashSet<String>();
-       private final Set<CustomFunction> customFunctions;
-
-       {
-               functionNames.add("abs");
-               functionNames.add("acos");
-               functionNames.add("asin");
-               functionNames.add("atan");
-               functionNames.add("cbrt");
-               functionNames.add("ceil");
-               functionNames.add("cos");
-               functionNames.add("cosh");
-               functionNames.add("exp");
-               functionNames.add("expm1");
-               functionNames.add("floor");
-               functionNames.add("log");
-               functionNames.add("sin");
-               functionNames.add("sinh");
-               functionNames.add("sqrt");
-               functionNames.add("tan");
-               functionNames.add("tanh");
-       }
-
-       Tokenizer() {
-               super();
-               customFunctions = null;
-       }
-
-       /**
-        * construct a new Tokenizer that recognizes variable names
-        * 
-        * @param variableNames
-        *            the variable names in the expression
-        * @throws IllegalArgumentException
-        *             if a variable has the name as a function
-        * @param customFunctions
-        *            the CustomFunction implementations used if the variableNames
-        *            are not valid
-        */
-       Tokenizer(String[] variableNames, Set<CustomFunction> customFunctions) throws IllegalArgumentException {
-               super();
-               this.variableNames = variableNames;
-               if (variableNames != null) {
-                       for (String varName : variableNames) {
-                               if (functionNames.contains(varName.toLowerCase())) {
-                                       throw new IllegalArgumentException("Variable '" + varName + "' can not have the same name as a function");
-                               }
-                       }
-               }
-               this.customFunctions = customFunctions;
-       }
-
-       private Token getCustomFunctionToken(String name) throws UnknownFunctionException {
-               for (CustomFunction func : customFunctions) {
-                       if (func.getValue().equals(name)) {
-                               return func;
-                       }
-               }
-               throw new UnknownFunctionException(name);
-       }
-
-       private boolean isCustomFunction(String name) {
-               if (customFunctions == null) {
-                       return false;
-               }
-               for (CustomFunction func : customFunctions) {
-                       if (func.getValue().equals(name)) {
-                               return true;
-                       }
-               }
-               return false;
-       }
-
-       /**
-        * check if a char is part of a number
-        * 
-        * @param c
-        *            the char to be checked
-        * @return true if the char is part of a number
-        */
-       private boolean isDigit(char c) {
-               return Character.isDigit(c) || c == '.';
-       }
-
-       private boolean isFunction(String name) {
-               for (Function fn : Function.values()) {
-                       if (fn.name().equals(name.toUpperCase())) {
-                               return true;
-                       }
-               }
-               return false;
-       }
-
-       /**
-        * check if a String is a variable name
-        * 
-        * @param name
-        *            the variable name which is checked to be valid the char to be
-        *            checked
-        * @return true if the char is a variable name (e.g. x)
-        */
-       private boolean isVariable(String name) {
-               if (variableNames != null) {
-                       for (String var : variableNames) {
-                               if (name.equals(var)) {
-                                       return true;
-                               }
-                       }
-               }
-               return false;
-       }
-
-       /**
-        * tokenize an infix expression by breaking it up into different
-        * {@link Token} that can represent operations,functions,numbers,
-        * parenthesis or variables
-        * 
-        * @param infix
-        *            the infix expression to be tokenized
-        * @return the {@link Token}s representing the expression
-        * @throws UnparsableExpressionException
-        *             when the expression is invalid
-        * @throws UnknownFunctionException
-        *             when an unknown function name has been used.
-        */
-       Token[] tokenize(String infix) throws UnparsableExpressionException, UnknownFunctionException {
-               final List<Token> tokens = new ArrayList<Token>();
-               final char[] chars = infix.toCharArray();
-               // iterate over the chars and fork on different types of input
-               Token lastToken;
-               for (int i = 0; i < chars.length; i++) {
-                       char c = chars[i];
-                       if (c == ' ')
-                               continue;
-                       if (isDigit(c)) {
-                               final StringBuilder valueBuilder = new StringBuilder(1);
-                               // handle the numbers of the expression
-                               valueBuilder.append(c);
-                               int numberLen = 1;
-                               while (chars.length > i + numberLen && isDigit(chars[i + numberLen])) {
-                                       valueBuilder.append(chars[i + numberLen]);
-                                       numberLen++;
-                               }
-                               i += numberLen - 1;
-                               lastToken = new NumberToken(valueBuilder.toString());
-                       } else if (Character.isLetter(c) || c == '_') {
-                               // can be a variable or function
-                               final StringBuilder nameBuilder = new StringBuilder();
-                               nameBuilder.append(c);
-                               int offset = 1;
-                               while (chars.length > i + offset && (Character.isLetter(chars[i + offset]) || Character.isDigit(chars[i + offset]) || chars[i + offset] == '_')) {
-                                       nameBuilder.append(chars[i + offset++]);
-                               }
-                               String name = nameBuilder.toString();
-                               if (this.isVariable(name)) {
-                                       // a variable
-                                       i += offset - 1;
-                                       lastToken = new VariableToken(name);
-                               } else if (this.isFunction(name)) {
-                                       // might be a function
-                                       i += offset - 1;
-                                       lastToken = new FunctionToken(name);
-                               } else if (this.isCustomFunction(name)) {
-                                       // a custom function
-                                       i += offset - 1;
-                                       lastToken = getCustomFunctionToken(name);
-                               } else {
-                                       // an unknown symbol was encountered
-                                       throw new UnparsableExpressionException(c, i);
-                               }
-                       }else if (c == ',') {
-                           // a function separator, hopefully
-                           lastToken=new FunctionSeparatorToken();
-                       } else if (OperatorToken.isOperator(c)) {
-                               lastToken = new OperatorToken(String.valueOf(c), OperatorToken.getOperation(c));
-                       } else if (c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}') {
-                               lastToken = new ParenthesisToken(String.valueOf(c));
-                       } else {
-                               // an unknown symbol was encountered
-                               throw new UnparsableExpressionException(c, i);
-                       }
-                       tokens.add(lastToken);
-               }
-               return tokens.toArray(new Token[tokens.size()]);
-       }
-}
diff --git a/core/src/net/sf/openrocket/util/exp4j/UnknownFunctionException.java b/core/src/net/sf/openrocket/util/exp4j/UnknownFunctionException.java
deleted file mode 100644 (file)
index a7a6beb..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
-   Copyright 2011 frank asseg
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-
- */
-package net.sf.openrocket.util.exp4j;
-
-/**
- * Exception for handling unknown Functions.
- * 
- * @see FunctionToken
- * @author fas@congrace.de
- */
-public class UnknownFunctionException extends Exception {
-       private static final long serialVersionUID = 1L;
-
-       /**
-        * construct a new {@link UnknownFunctionException}
-        * 
-        * @param functionName
-        *            the function name which could not be found
-        */
-       public UnknownFunctionException(String functionName) {
-               super("Unknown function: " + functionName);
-       }
-}
diff --git a/core/src/net/sf/openrocket/util/exp4j/UnparsableExpressionException.java b/core/src/net/sf/openrocket/util/exp4j/UnparsableExpressionException.java
deleted file mode 100644 (file)
index deddcb9..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
-   Copyright 2011 frank asseg
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-
- */
-package net.sf.openrocket.util.exp4j;
-
-/**
- * Exception for invalid expressions
- * 
- * @author fas@congrace.de
- */
-public class UnparsableExpressionException extends Exception {
-       private static final long serialVersionUID = 1L;
-
-       /**
-        * construct a new {@link UnparsableExpressionException}
-        * 
-        * @param c
-        *            the character which could not be parsed
-        * @param pos
-        *            the position of the character in the expression
-        */
-       public UnparsableExpressionException(char c, int pos) {
-               super("Unable to parse character at position " + pos + ": '" + String.valueOf(c) + "'");
-       }
-       /**
-        * construct a new {@link UnparsableExpressionException}
-        * 
-        * @param msg 
-        *            the error message
-        */
-       public UnparsableExpressionException(String msg) {
-               super(msg);
-       }
-}
diff --git a/core/src/net/sf/openrocket/util/exp4j/VariableToken.java b/core/src/net/sf/openrocket/util/exp4j/VariableToken.java
deleted file mode 100644 (file)
index 1d6a336..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
-   Copyright 2011 frank asseg
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-
- */
-package net.sf.openrocket.util.exp4j;
-
-import java.util.Map;
-import java.util.Stack;
-
-/**
- * A {@link Token} for representing variables
- * 
- * @author fas
- */
-class VariableToken extends CalculationToken {
-       /**
-        * construct a new {@link VariableToken}
-        * 
-        * @param value
-        *            the value of the token
-        */
-       VariableToken(String value) {
-               super(value);
-       }
-
-       @Override
-       void mutateStackForCalculation(Stack<Double> stack, Map<String, Double> variableValues) {
-               double value = variableValues.get(this.getValue());
-               stack.push(value);
-       }
-
-       @Override
-       void mutateStackForInfixTranslation(Stack<Token> operatorStack, StringBuilder output) {
-               output.append(this.getValue()).append(" ");
-       }
-}
diff --git a/core/test/net/sf/openrocket/util/ExpressionParserTest.java b/core/test/net/sf/openrocket/util/ExpressionParserTest.java
new file mode 100644 (file)
index 0000000..738f5b7
--- /dev/null
@@ -0,0 +1,68 @@
+package net.sf.openrocket.util;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+public class ExpressionParserTest {
+       
+       private static final double EPS = 1e-10;
+       
+       private ExpressionParser parser = new ExpressionParser();
+       
+       @Test
+       public void testPlainNumber() throws InvalidExpressionException {
+               assertEquals(1.0, parser.parse("1"), EPS);
+               assertEquals(1.0, parser.parse("\t 1 "), EPS);
+               assertEquals(0.9, parser.parse(".9"), EPS);
+               assertEquals(1.0, parser.parse("1."), EPS);
+               assertEquals(1.2, parser.parse("1.2"), EPS);
+               assertEquals(1.2, parser.parse("01.200"), EPS);
+       }
+       
+       
+       @Test
+       public void testDecimalComma() throws InvalidExpressionException {
+               assertEquals(1.0, parser.parse("1,"), EPS);
+               assertEquals(1.2, parser.parse("1,2"), EPS);
+               assertEquals(1.2, parser.parse("01,200"), EPS);
+               assertEquals(0.9, parser.parse(",9"), EPS);
+       }
+       
+       
+       @Test
+       public void testSimpleExpression() throws InvalidExpressionException {
+               assertEquals(3.0, parser.parse("1+2"), EPS);
+               assertEquals(6.0, parser.parse("1+2.5*2"), EPS);
+               assertEquals(7.0, parser.parse("(1+2.5) * 2"), EPS);
+               assertEquals(1.0 + 2.0 / 3.0, parser.parse("1+2/3"), EPS);
+       }
+       
+       @Test
+       public void testFraction() throws InvalidExpressionException {
+               assertEquals(1.5, parser.parse("1 1/2"), EPS);
+               assertEquals(1.5, parser.parse("  1    1 / 2"), EPS);
+               assertEquals(2.0 + 3.0 / 7.0, parser.parse("1 + 1 3/7"), EPS);
+               assertEquals(3.0, parser.parse("1 1/2 * 2"), EPS);
+       }
+       
+       @Test
+       public void testInvalidExpression() {
+               expectInvalid("1+");
+               expectInvalid("1+2/");
+               expectInvalid("1 2");
+               expectInvalid("12 2.5");
+               expectInvalid("1 2.5/4");
+               expectInvalid("1. 2");
+               expectInvalid("1 .2");
+       }
+       
+       private void expectInvalid(String exp) {
+               try {
+                       double value = parser.parse(exp);
+                       fail("Expression '" + exp + "' evaluated to " + value + ", expected failure");
+               } catch (InvalidExpressionException e) {
+                       // expected
+               }
+       }
+}