Remove dependency on awt.Window from CustomExpression since this makes the android...
[debian/openrocket] / core / src / net / sf / openrocket / simulation / CustomExpression.java
1 package net.sf.openrocket.simulation;
2
3 import java.util.SortedMap;
4 import java.util.TreeMap;
5
6 import net.sf.openrocket.document.Simulation;
7 import net.sf.openrocket.logging.LogHelper;
8 import net.sf.openrocket.startup.Application;
9 import net.sf.openrocket.unit.FixedUnitGroup;
10 import net.sf.openrocket.unit.UnitGroup;
11 import net.sf.openrocket.util.ArrayList;
12 import de.congrace.exp4j.Calculable;
13 import de.congrace.exp4j.ExpressionBuilder;
14
15
16 /**
17  * Represents a single custom expression
18  * @author Richard Graham
19  *
20  */
21 public class CustomExpression implements Cloneable{
22         
23         private static final LogHelper log = Application.getLogger();
24         
25         private String name, symbol, unit, expression;
26         private ExpressionBuilder builder;
27         private static Simulation sim = null;
28         
29         // A map of available operator strings (keys) and description of function (value)
30         public static final SortedMap<String, String> AVAILABLE_OPERATORS = new TreeMap<String, String>() {{
31             put("+"             , "Addition");
32             put("-"                     , "Subtraction");
33             put("*"                     , "Multiplication");
34             put("/"                     , "Divison");
35             put("%"                     , "Modulo");
36             put("^"                     , "Exponentiation");
37             put("abs()"         , "Absolute value");
38             put("ceil()"        , "Ceiling (next integer value");
39             put("floor()"       , "Floor (previous integer value");
40             put("sqrt()"        , "Square root");
41             put("cbrt()"        , "Cubic root");
42             put("exp()"         , "Euler\'s number raised to the value (e^x)");
43             put("log()"         , "Natural logarithm");
44             put("sin()"         , "Sine");
45             put("cos()"         , "Cosine");
46             put("tan()"         , "Tangent");
47             put("asin()"        , "Arc sine");
48             put("acos()"        , "Arc cosine");
49             put("atan()"        , "Arc tangent");
50             put("sinh()"        , "Hyerbolic sine");
51             put("cosh()"        , "Hyperbolic cosine");
52             put("tanh()"        , "Hyperbolic tangent");
53         }};  
54         
55         
56         public CustomExpression(){
57                 setName("");
58                 setSymbol("");
59                 setUnit("");
60                 setExpression("");
61         }
62         
63         public CustomExpression(Simulation sim){
64                 this();
65                 setSimulation(sim);
66         }
67         
68         public CustomExpression(Simulation sim, String name, String symbol, String unit, String expression) {
69                 
70                 setName(name);
71                 setSymbol(symbol);
72                 setUnit(unit);
73                 setExpression(expression);
74                 setSimulation(sim);
75         }
76         
77         /*
78          * Use this to update the simulation this is associated with
79          */
80         public void setSimulation(Simulation sim){
81                 CustomExpression.sim = sim;
82         }
83         
84         public Simulation getSimulation() {
85                 return CustomExpression.sim;
86         }
87         
88         /*
89          * Returns the flight data branch 0 for this simulation, or an empty branch
90          * if no simulated data exists
91          */
92         private FlightDataBranch getBranch() {
93                 if ( sim == null || sim.getSimulatedData().getBranch(0) == null) {
94                         return new FlightDataBranch();
95                 }
96                 else {
97                         return sim.getSimulatedData().getBranch(0);
98                 }
99         }
100         
101         
102         public void setName(String name){
103                 this.name = name;
104         }
105         
106         public void setUnit(String unit){
107                 this.unit = unit;
108         }
109         
110         public void setSymbol(String symbol){
111                 this.symbol = symbol;
112         }
113         
114         public void setExpression(String expression){
115                 this.expression = expression;
116                 builder = new ExpressionBuilder(expression);
117         }
118         
119         // get a list of all the names of all the available variables
120         private ArrayList<String> getAllNames(){
121                 ArrayList<String> names = new ArrayList<String>();
122                 for (FlightDataType type : FlightDataType.ALL_TYPES)
123                         names.add(type.getName());
124                 for (CustomExpression exp : sim.getCustomExpressions() ){
125                         if (exp != this)
126                                 names.add(exp.getName());
127                 }
128                 return names;
129         }
130         
131         // get a list of all the symbols of the available variables ignoring this one
132         private ArrayList<String> getAllSymbols(){
133                 ArrayList<String> symbols = new ArrayList<String>();
134                 for (FlightDataType type : FlightDataType.ALL_TYPES)
135                         symbols.add(type.getSymbol());
136                 for (CustomExpression exp : sim.getCustomExpressions() ){
137                         if (exp != this)
138                                 symbols.add(exp.getSymbol());
139                 }
140                 return symbols;
141         }
142         
143         public boolean checkSymbol(){
144                 if (symbol.trim().isEmpty())
145                         return false;
146                 
147                 // No bad characters
148                 for (char c : "0123456789.()[]{}".toCharArray())
149                         if (symbol.indexOf(c) != -1 )
150                                 return false;
151                 
152                 // No operators (ignoring brackets)
153                 for (String s : CustomExpression.AVAILABLE_OPERATORS.keySet()){
154                         if (symbol.contains(s.replaceAll("\\(|\\)", "")))
155                                 return false;
156                 }
157                 
158                 // No already defined symbols
159                 ArrayList<String> symbols = getAllSymbols().clone();
160                 if (symbols.contains(symbol.trim())){
161                         int index = symbols.indexOf(symbol.trim());
162                         log.user("Symbol "+symbol+" already exists, found "+symbols.get(index));
163                         return false;
164                 }
165                 
166                 return true;
167         }
168         
169         public boolean checkName(){
170                 if (name.trim().isEmpty())
171                         return false;
172                 
173                 ArrayList<String> names = getAllNames().clone();
174                 if (names.contains(name.trim())){
175                         int index = names.indexOf(name.trim());
176                         log.user("Symbol "+symbol+" already exists, found "+names.get(index));
177                         return false;
178                 }
179                 
180                 return true;
181         }
182         
183         // Currently no restrictions on unit
184         public boolean checkUnit(){
185                 return true;
186         }
187         
188         public boolean checkAll(){
189                 return checkUnit() && checkSymbol() && checkName();
190         }
191         
192         public String getName(){
193                 return name;
194         }
195         
196         public String getSymbol(){
197                 return symbol;
198         }
199         
200         public String getUnit(){
201                 return unit;
202         }
203         
204         public String getExpressionString(){
205                 return expression;
206         }
207         
208         
209         /*
210          * Check if the current expression is valid
211          */
212         public boolean checkExpression(){
213                 
214                 if (expression.trim().isEmpty()){
215                         return false;
216                 }
217                 
218                 // Define the available variables as 0
219                 for (FlightDataType type : getBranch().getTypes()){
220                         builder.withVariable(type.getSymbol(), 0.0);
221                 }
222                 
223                 for (String symb : getAllSymbols()){
224                         builder.withVariable(symb, 0.0);
225                 }
226                 
227                 // Try to build
228                 try {
229                         builder.build();
230                 } catch (Exception e) {
231                         log.user("Custom expression invalid : " + e.toString());
232                         return false;
233                 }
234                 
235                 // Otherwise, all OK
236                 return true;
237         }
238         
239         /*
240          * Evaluate the expression using the last variable values from the simulation status.
241          * Returns NaN on any error.
242          */
243         public Double evaluate(SimulationStatus status){
244                 
245                 for (FlightDataType type : status.getFlightData().getTypes()){
246                         builder.withVariable(type.getSymbol(), status.getFlightData().getLast(type) );
247                 }
248                 
249                 Calculable calc;
250                 try {
251                         calc = builder.build();
252                         return new Double(calc.calculate());
253                 } catch (Exception e) {
254                         log.user("Could not calculate custom expression "+name);
255                         return Double.NaN;
256                 }
257         }
258
259         /*
260          * Returns the new flight data type corresponding to this calculated data
261          */
262         public FlightDataType getType(){
263                 UnitGroup ug = new FixedUnitGroup(unit);
264                 return FlightDataType.getType(name, symbol, ug);
265         }
266         
267         /*
268          * Add this expression to the simulation if not already added
269          */
270         public void addToSimulation(){
271                 if (! sim.getCustomExpressions().contains(this))
272                         sim.addCustomExpression( this );
273         }
274         
275         /*
276          * Removes this expression from the simulation, replacing it with a given new expression
277          */
278         public void overwrite(CustomExpression newExpression){
279                 if (!sim.getCustomExpressions().contains(this)) 
280                         return;
281                 else {
282                         int index = sim.getCustomExpressions().indexOf(this);
283                         sim.getCustomExpressions().set(index, newExpression);
284                 }
285         }
286         
287         @Override
288         public String toString(){
289                 return "Custom expression : "+this.name.toString()+ " " + this.expression.toString();
290         }
291         
292         @Override
293         /*
294          * Clone method makes a deep copy of everything except the simulation
295          * @see java.lang.Object#clone()
296          */
297         public Object clone() {
298               try {
299                   return super.clone();
300               }
301               catch( CloneNotSupportedException e )
302               {
303                   return new CustomExpression(  sim  , 
304                                 new String(this.getName()), 
305                                 new String(this.getSymbol()),
306                                 new String(this.getUnit()),
307                                 new String(this.getExpressionString()));
308               }
309           } 
310         
311 }