Added ability for doublemodel to evaluate math expressions using exp4j, fixed typeove...
[debian/openrocket] / core / src / net / sf / openrocket / util / exp4j / OperatorToken.java
1 /*
2    Copyright 2011 frank asseg
3
4    Licensed under the Apache License, Version 2.0 (the "License");
5    you may not use this file except in compliance with the License.
6    You may obtain a copy of the License at
7
8        http://www.apache.org/licenses/LICENSE-2.0
9
10    Unless required by applicable law or agreed to in writing, software
11    distributed under the License is distributed on an "AS IS" BASIS,
12    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13    See the License for the specific language governing permissions and
14    limitations under the License.
15
16  */
17 package net.sf.openrocket.util.exp4j;
18
19 import java.util.Map;
20 import java.util.Stack;
21
22 /**
23  * {@link Token} for Operations like +,-,*,/,% and ^
24  * 
25  * @author fas@congrace.de
26  */
27 class OperatorToken extends CalculationToken {
28
29         /**
30          * the valid {@link Operation}s for the {@link OperatorToken}
31          * 
32          * @author fas@congrace.de
33          */
34         enum Operation {
35                 ADDITION(1, true), SUBTRACTION(1, true), MULTIPLICATION(2, true), DIVISION(2, true), MODULO(2, true), EXPONENTIATION(3, false), UNARY_MINUS(4, false), UNARY_PLUS(
36                                 4, false);
37                 private final int precedence;
38                 private final boolean leftAssociative;
39
40                 private Operation(int precedence, boolean leftAssociative) {
41                         this.precedence = precedence;
42                         this.leftAssociative = leftAssociative;
43                 }
44         }
45
46         /**
47          * return a corresponding {@link Operation} for a symbol
48          * 
49          * @param c
50          *            the symbol of the operation
51          * @return the corresponding {@link Operation}
52          */
53         static Operation getOperation(char c) {
54                 switch (c) {
55                 case '+':
56                         return Operation.ADDITION;
57                 case '-':
58                         return Operation.SUBTRACTION;
59                 case '*':
60                         return Operation.MULTIPLICATION;
61                 case '/':
62                         return Operation.DIVISION;
63                 case '^':
64                         return Operation.EXPONENTIATION;
65                 case '#':
66                         return Operation.UNARY_MINUS;
67                 case '%':
68                         return Operation.MODULO;
69                 default:
70                         return null;
71                 }
72         }
73
74         static boolean isOperator(char c) {
75                 return getOperation(c) != null;
76         }
77
78         private final Operation operation;
79
80         /**
81          * construct a new {@link OperatorToken}
82          * 
83          * @param value
84          *            the symbol (e.g.: '+')
85          * @param operation
86          *            the {@link Operation} of this {@link Token}
87          */
88         OperatorToken(String value, Operation operation) {
89                 super(value);
90                 this.operation = operation;
91         }
92
93         /**
94          * apply the {@link Operation}
95          * 
96          * @param values
97          *            the doubles to operate on
98          * @return the result of the {@link Operation}
99          */
100         double applyOperation(double... values) {
101                 switch (operation) {
102                 case ADDITION:
103                         return values[0] + values[1];
104                 case SUBTRACTION:
105                         return values[0] - values[1];
106                 case MULTIPLICATION:
107                         return values[0] * values[1];
108                 case EXPONENTIATION:
109                         return Math.pow(values[0], values[1]);
110                 case DIVISION:
111                         return values[0] / values[1];
112                 case UNARY_MINUS:
113                         return -values[0];
114                 case UNARY_PLUS:
115                         return values[0];
116                 case MODULO:
117                         return values[0] % values[1];
118                 default:
119                         return 0;
120                 }
121         }
122
123         @Override
124         public boolean equals(Object obj) {
125                 if (obj instanceof OperatorToken) {
126                         final OperatorToken t = (OperatorToken) obj;
127                         return t.getValue().equals(this.getValue());
128                 }
129                 return false;
130         }
131
132         int getOperandCount() {
133                 switch (operation) {
134                 case ADDITION:
135                 case SUBTRACTION:
136                 case MULTIPLICATION:
137                 case DIVISION:
138                 case EXPONENTIATION:
139                 case MODULO:
140                         return 2;
141                 case UNARY_MINUS:
142                 case UNARY_PLUS:
143                         return 1;
144                 default:
145                         return 0;
146                 }
147         }
148
149         /**
150          * get the {@link Operation} of this {@link Token}
151          * 
152          * @return the {@link Operation}
153          */
154         Operation getOperation() {
155                 return operation;
156         }
157
158         int getPrecedence() {
159                 return operation.precedence;
160         }
161
162         @Override
163         public int hashCode() {
164                 return getValue().hashCode();
165         }
166
167         /**
168          * check if the operation is left associative
169          * 
170          * @return true if left associative, otherwise false
171          */
172         boolean isLeftAssociative() {
173                 return operation.leftAssociative;
174         }
175
176         @Override
177         void mutateStackForCalculation(Stack<Double> stack, Map<String, Double> variableValues) {
178                 if (this.getOperandCount() == 2) {
179                         final double n2 = stack.pop();
180                         final double n1 = stack.pop();
181                         stack.push(this.applyOperation(n1, n2));
182                 } else if (this.getOperandCount() == 1) {
183                         final double n1 = stack.pop();
184                         stack.push(this.applyOperation(n1));
185                 }
186         }
187
188         @Override
189         void mutateStackForInfixTranslation(Stack<Token> operatorStack, StringBuilder output) {
190                 Token before;
191                 while (!operatorStack.isEmpty() && (before = operatorStack.peek()) != null && (before instanceof OperatorToken || before instanceof FunctionToken)) {
192                         if (before instanceof FunctionToken) {
193                                 operatorStack.pop();
194                                 output.append(before.getValue()).append(" ");
195                         } else {
196                                 final OperatorToken stackOperator = (OperatorToken) before;
197                                 if (this.isLeftAssociative() && this.getPrecedence() <= stackOperator.getPrecedence()) {
198                                         output.append(operatorStack.pop().getValue()).append(" ");
199                                 } else if (!this.isLeftAssociative() && this.getPrecedence() < stackOperator.getPrecedence()) {
200                                         output.append(operatorStack.pop().getValue()).append(" ");
201                                 } else {
202                                         break;
203                                 }
204                         }
205                 }
206                 operatorStack.push(this);
207         }
208 }