001package jmri.jmrit.logixng.util.parser;
002
003import jmri.JmriException;
004import jmri.jmrit.logixng.SymbolTable;
005import jmri.util.TypeConversionUtil;
006
007/**
008 * A parsed expression
009 */
010public class ExpressionNodeArithmeticOperator implements ExpressionNode {
011
012    private final TokenType _tokenType;
013    private final ExpressionNode _leftSide;
014    private final ExpressionNode _rightSide;
015    
016    
017    public ExpressionNodeArithmeticOperator(TokenType tokenType, ExpressionNode leftSide, ExpressionNode rightSide) {
018        _tokenType = tokenType;
019        _leftSide = leftSide;
020        _rightSide = rightSide;
021        
022        if (_rightSide == null) {
023            throw new IllegalArgumentException("rightSide must not be null");
024        }
025        
026        // Verify that the token is of the correct type
027        switch (_tokenType) {
028            case ADD:
029            case SUBTRACKT:
030            case BINARY_NOT:
031                break;
032                
033            case MULTIPLY:
034            case DIVIDE:
035            case MODULO:
036            case SHIFT_LEFT:
037            case SHIFT_RIGHT:
038            case UNSIGNED_SHIFT_RIGHT:
039                if (_leftSide == null) {
040                    throw new IllegalArgumentException("leftSide must not be null for operators *, / and %");
041                }
042                break;
043                
044            default:
045                throw new IllegalArgumentException("Unknown arithmetic operator: "+_tokenType.name());
046        }
047    }
048    
049    
050    private Object add(Object left, Object right) throws CalculateException {
051        if (TypeConversionUtil.isIntegerNumber(left)
052                && TypeConversionUtil.isIntegerNumber(right)) {
053            return ((Number)left).longValue() + ((Number)right).longValue();
054
055        } else if (TypeConversionUtil.isFloatingNumber(left)
056                && TypeConversionUtil.isFloatingNumber(right)) {
057            return ((Number)left).doubleValue() + ((Number)right).doubleValue();
058
059        } else {
060            if (TypeConversionUtil.isString(left) && TypeConversionUtil.isString(right)) {
061                return ((String)left) + ((String)right);
062            } else {
063                throw new CalculateException(Bundle.getMessage("ArithmeticNotCompatibleOperands", left, right));
064            }
065        }
066    }
067    
068    
069    private Object subtract(Object left, Object right) throws CalculateException {
070        if (TypeConversionUtil.isIntegerNumber(left)) {
071            if (TypeConversionUtil.isIntegerNumber(right)) {
072                return ((Number)left).longValue() - ((Number)right).longValue();
073            } else if (TypeConversionUtil.isFloatingNumber(right)) {
074                return ((Number)left).doubleValue() - ((Number)right).doubleValue();
075            } else {
076                throw new CalculateException(Bundle.getMessage("ArithmeticNotNumberError", right));
077            }
078        } else if (TypeConversionUtil.isFloatingNumber(left)) {
079            if (TypeConversionUtil.isFloatingNumber(right)) {
080                return ((Number)left).doubleValue() - ((Number)right).doubleValue();
081            } else {
082                throw new CalculateException(Bundle.getMessage("ArithmeticNotNumberError", right));
083            }
084        } else {
085            throw new CalculateException(Bundle.getMessage("ArithmeticNotNumberError", left));
086        }
087    }
088    
089    
090    private Object multiply(Object left, Object right) throws CalculateException {
091        if (TypeConversionUtil.isIntegerNumber(left)) {
092            if (TypeConversionUtil.isIntegerNumber(right)) {
093                return ((Number)left).longValue() * ((Number)right).longValue();
094            } else if (TypeConversionUtil.isFloatingNumber(right)) {
095                return ((Number)left).doubleValue() * ((Number)right).doubleValue();
096            } else {
097                throw new CalculateException(Bundle.getMessage("ArithmeticNotNumberError", right));
098            }
099        } else if (TypeConversionUtil.isFloatingNumber(left)) {
100            if (TypeConversionUtil.isFloatingNumber(right)) {
101                return ((Number)left).doubleValue() * ((Number)right).doubleValue();
102            } else {
103                throw new CalculateException(Bundle.getMessage("ArithmeticNotNumberError", right));
104            }
105        } else {
106            throw new CalculateException(Bundle.getMessage("ArithmeticNotNumberError", left));
107        }
108    }
109    
110    
111    private Object divide(Object left, Object right) throws CalculateException {
112        if (TypeConversionUtil.isIntegerNumber(left)) {
113            if (TypeConversionUtil.isIntegerNumber(right)) {
114                return ((Number)left).longValue() / ((Number)right).longValue();
115            } else if (TypeConversionUtil.isFloatingNumber(right)) {
116                return ((Number)left).doubleValue() / ((Number)right).doubleValue();
117            } else {
118                throw new CalculateException(Bundle.getMessage("ArithmeticNotNumberError", right));
119            }
120        } else if (TypeConversionUtil.isFloatingNumber(left)) {
121            if (TypeConversionUtil.isFloatingNumber(right)) {
122                return ((Number)left).doubleValue() / ((Number)right).doubleValue();
123            } else {
124                throw new CalculateException(Bundle.getMessage("ArithmeticNotNumberError", right));
125            }
126        } else {
127            throw new CalculateException(Bundle.getMessage("ArithmeticNotNumberError", left));
128        }
129    }
130    
131    
132    private Object modulo(Object left, Object right) throws CalculateException {
133        if (TypeConversionUtil.isIntegerNumber(left)) {
134            if (TypeConversionUtil.isIntegerNumber(right)) {
135                return ((Number)left).longValue() % ((Number)right).longValue();
136            } else {
137                throw new CalculateException(Bundle.getMessage("ArithmeticNotIntegerNumberError", right));
138            }
139        } else {
140            throw new CalculateException(Bundle.getMessage("ArithmeticNotIntegerNumberError", left));
141        }
142    }
143    
144    
145    private Object shiftLeft(Object left, Object right) throws CalculateException {
146        if (TypeConversionUtil.isIntegerNumber(left)) {
147            if (TypeConversionUtil.isIntegerNumber(right)) {
148                return ((Number)left).longValue() << ((Number)right).longValue();
149            } else {
150                throw new CalculateException(Bundle.getMessage("ArithmeticNotIntegerNumberError", right));
151            }
152        } else {
153            throw new CalculateException(Bundle.getMessage("ArithmeticNotIntegerNumberError", left));
154        }
155    }
156    
157    
158    private Object shiftRight(Object left, Object right) throws CalculateException {
159        if (TypeConversionUtil.isIntegerNumber(left)) {
160            if (TypeConversionUtil.isIntegerNumber(right)) {
161                return ((Number)left).longValue() >> ((Number)right).longValue();
162            } else {
163                throw new CalculateException(Bundle.getMessage("ArithmeticNotIntegerNumberError", right));
164            }
165        } else {
166            throw new CalculateException(Bundle.getMessage("ArithmeticNotIntegerNumberError", left));
167        }
168    }
169    
170    
171    private Object unsignedShiftRight(Object left, Object right) throws CalculateException {
172        if (TypeConversionUtil.isIntegerNumber(left)) {
173            if (TypeConversionUtil.isIntegerNumber(right)) {
174                return ((Number)left).longValue() >>> ((Number)right).longValue();
175            } else {
176                throw new CalculateException(Bundle.getMessage("ArithmeticNotIntegerNumberError", right));
177            }
178        } else {
179            throw new CalculateException(Bundle.getMessage("ArithmeticNotIntegerNumberError", left));
180        }
181    }
182    
183    
184    @Override
185    public Object calculate(SymbolTable symbolTable) throws JmriException {
186        
187        Object left = _leftSide != null ? _leftSide.calculate(symbolTable) : null;
188        Object right = _rightSide.calculate(symbolTable);
189        
190        if ((left == null) && ((_tokenType == TokenType.ADD) || (_tokenType == TokenType.SUBTRACKT))) {
191            left = 0;
192        }
193        
194        // Convert a boolean value to an integer value
195        if (left instanceof Boolean) {
196            left = ((Boolean)left) ? 1 : 0;
197        }
198        if (right instanceof Boolean) {
199            right = ((Boolean)right) ? 1 : 0;
200        }
201        
202        if (_tokenType == TokenType.BINARY_NOT) {
203            if (! TypeConversionUtil.isIntegerNumber(right)) {
204                return 0;
205            }
206            return ~ TypeConversionUtil.convertToLong(right);
207        }
208        
209        if (_tokenType == TokenType.ADD) {
210            // Add can handle String concatenation
211            return add(left, right);
212        } else {
213            // For the other arithmetic operators, except add, only numbers can
214            // be handled. For other types, return 0.
215            if (! TypeConversionUtil.isFloatingNumber(left)) {
216                return 0;
217            }
218            if (! TypeConversionUtil.isFloatingNumber(right)) {
219                return 0;
220            }
221            
222            switch (_tokenType) {
223                case SUBTRACKT:
224                    return subtract(left, right);
225                case MULTIPLY:
226                    return multiply(left, right);
227                case DIVIDE:
228                    return divide(left, right);
229                case MODULO:
230                    return modulo(left, right);
231                case SHIFT_LEFT:
232                    return shiftLeft(left, right);
233                case SHIFT_RIGHT:
234                    return shiftRight(left, right);
235                case UNSIGNED_SHIFT_RIGHT:
236                    return unsignedShiftRight(left, right);
237
238                default:
239                    throw new CalculateException("Unknown arithmetic operator: "+_tokenType.name());
240            }
241        }
242        
243    }
244    
245    
246    /** {@inheritDoc} */
247    @Override
248    public String getDefinitionString() {
249        String operStr;
250        switch (_tokenType) {
251            case ADD:
252                operStr = "+";
253                break;
254                
255            case SUBTRACKT:
256                operStr = "-";
257                break;
258                
259            case BINARY_NOT:
260                operStr = "~";
261                break;
262                
263            case MULTIPLY:
264                operStr = "*";
265                break;
266                
267            case DIVIDE:
268                operStr = "/";
269                break;
270                
271            case MODULO:
272                operStr = "%";
273                break;
274                
275            case SHIFT_LEFT:
276                operStr = "<<";
277                break;
278                
279            case SHIFT_RIGHT:
280                operStr = ">>";
281                break;
282                
283            case UNSIGNED_SHIFT_RIGHT:
284                operStr = ">>>";
285                break;
286                
287            default:
288                throw new UnsupportedOperationException("Unknown arithmetic operator: "+_tokenType.name());
289        }
290        
291        String leftSideString = _leftSide != null ? "(" + _leftSide.getDefinitionString() + ")" : "";
292        String rightSideString = "(" + _rightSide.getDefinitionString() + ")";
293        return leftSideString + operStr + rightSideString;
294    }
295    
296}