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 ExpressionNodeAssignmentOperator implements ExpressionNode {
011
012    private final TokenType _tokenType;
013    private final ExpressionNode _leftSide;
014    private final ExpressionNode _rightSide;
015    
016    
017    public ExpressionNodeAssignmentOperator(TokenType tokenType, ExpressionNode leftSide, ExpressionNode rightSide) {
018        _tokenType = tokenType;
019        _leftSide = leftSide;
020        _rightSide = rightSide;
021        
022        if (_leftSide == null) {
023            throw new IllegalArgumentException("leftSide must not be null");
024        }
025        
026        if (! _leftSide.canBeAssigned()) {
027            throw new IllegalArgumentException("leftSide must assignable");
028        }
029        
030        // Verify that the token is of the correct type
031        switch (_tokenType) {
032            case ASSIGN:
033            case ASSIGN_ADD:
034            case ASSIGN_SUBTRACKT:
035                break;
036                
037            case ASSIGN_MULTIPLY:
038            case ASSIGN_DIVIDE:
039            case ASSIGN_MODULO:
040            case ASSIGN_AND:
041            case ASSIGN_OR:
042            case ASSIGN_XOR:
043            case ASSIGN_SHIFT_LEFT:
044            case ASSIGN_SHIFT_RIGHT:
045            case ASSIGN_UNSIGNED_SHIFT_RIGHT:
046                if (_leftSide == null) {
047                    throw new IllegalArgumentException("leftSide must not be null for operators *, /, %, <<, >>, >>>");
048                }
049                break;
050                
051            default:
052                throw new IllegalArgumentException("Unknown arithmetic operator: "+_tokenType.name());
053        }
054    }
055    
056    
057    private Object add(Object left, Object right) throws CalculateException {
058        if (TypeConversionUtil.isIntegerNumber(left)
059                && TypeConversionUtil.isIntegerNumber(right)) {
060            return ((Number)left).longValue() + ((Number)right).longValue();
061
062        } else if (TypeConversionUtil.isFloatingNumber(left)
063                && TypeConversionUtil.isFloatingNumber(right)) {
064            return ((Number)left).doubleValue() + ((Number)right).doubleValue();
065
066        } else {
067            if (TypeConversionUtil.isString(left) && TypeConversionUtil.isString(right)) {
068                return ((String)left) + ((String)right);
069            } else {
070                throw new CalculateException(Bundle.getMessage("ArithmeticNotCompatibleOperands", left, right));
071            }
072        }
073    }
074    
075    
076    private Object subtract(Object left, Object right) throws CalculateException {
077        if (TypeConversionUtil.isIntegerNumber(left)) {
078            if (TypeConversionUtil.isIntegerNumber(right)) {
079                return ((Number)left).longValue() - ((Number)right).longValue();
080            } else if (TypeConversionUtil.isFloatingNumber(right)) {
081                return ((Number)left).doubleValue() - ((Number)right).doubleValue();
082            } else {
083                throw new CalculateException(Bundle.getMessage("ArithmeticNotNumberError", right));
084            }
085        } else if (TypeConversionUtil.isFloatingNumber(left)) {
086            if (TypeConversionUtil.isFloatingNumber(right)) {
087                return ((Number)left).doubleValue() - ((Number)right).doubleValue();
088            } else {
089                throw new CalculateException(Bundle.getMessage("ArithmeticNotNumberError", right));
090            }
091        } else {
092            throw new CalculateException(Bundle.getMessage("ArithmeticNotNumberError", left));
093        }
094    }
095    
096    
097    private Object multiply(Object left, Object right) throws CalculateException {
098        if (TypeConversionUtil.isIntegerNumber(left)) {
099            if (TypeConversionUtil.isIntegerNumber(right)) {
100                return ((Number)left).longValue() * ((Number)right).longValue();
101            } else if (TypeConversionUtil.isFloatingNumber(right)) {
102                return ((Number)left).doubleValue() * ((Number)right).doubleValue();
103            } else {
104                throw new CalculateException(Bundle.getMessage("ArithmeticNotNumberError", right));
105            }
106        } else if (TypeConversionUtil.isFloatingNumber(left)) {
107            if (TypeConversionUtil.isFloatingNumber(right)) {
108                return ((Number)left).doubleValue() * ((Number)right).doubleValue();
109            } else {
110                throw new CalculateException(Bundle.getMessage("ArithmeticNotNumberError", right));
111            }
112        } else {
113            throw new CalculateException(Bundle.getMessage("ArithmeticNotNumberError", left));
114        }
115    }
116    
117    
118    private Object divide(Object left, Object right) throws CalculateException {
119        if (TypeConversionUtil.isIntegerNumber(left)) {
120            if (TypeConversionUtil.isIntegerNumber(right)) {
121                return ((Number)left).longValue() / ((Number)right).longValue();
122            } else if (TypeConversionUtil.isFloatingNumber(right)) {
123                return ((Number)left).doubleValue() / ((Number)right).doubleValue();
124            } else {
125                throw new CalculateException(Bundle.getMessage("ArithmeticNotNumberError", right));
126            }
127        } else if (TypeConversionUtil.isFloatingNumber(left)) {
128            if (TypeConversionUtil.isFloatingNumber(right)) {
129                return ((Number)left).doubleValue() / ((Number)right).doubleValue();
130            } else {
131                throw new CalculateException(Bundle.getMessage("ArithmeticNotNumberError", right));
132            }
133        } else {
134            throw new CalculateException(Bundle.getMessage("ArithmeticNotNumberError", left));
135        }
136    }
137    
138    
139    private Object modulo(Object left, Object right) throws CalculateException {
140        if (TypeConversionUtil.isIntegerNumber(left)) {
141            if (TypeConversionUtil.isIntegerNumber(right)) {
142                return ((Number)left).longValue() % ((Number)right).longValue();
143            } else {
144                throw new CalculateException(Bundle.getMessage("ArithmeticNotIntegerNumberError", right));
145            }
146        } else {
147            throw new CalculateException(Bundle.getMessage("ArithmeticNotIntegerNumberError", left));
148        }
149    }
150    
151    
152    private Object and(Object left, Object right) throws CalculateException {
153        if (TypeConversionUtil.isIntegerNumber(left)) {
154            if (TypeConversionUtil.isIntegerNumber(right)) {
155                return ((Number)left).longValue() & ((Number)right).longValue();
156            } else {
157                throw new CalculateException(Bundle.getMessage("ArithmeticNotIntegerNumberError", right));
158            }
159        } else {
160            throw new CalculateException(Bundle.getMessage("ArithmeticNotIntegerNumberError", left));
161        }
162    }
163    
164    
165    private Object or(Object left, Object right) throws CalculateException {
166        if (TypeConversionUtil.isIntegerNumber(left)) {
167            if (TypeConversionUtil.isIntegerNumber(right)) {
168                return ((Number)left).longValue() | ((Number)right).longValue();
169            } else {
170                throw new CalculateException(Bundle.getMessage("ArithmeticNotIntegerNumberError", right));
171            }
172        } else {
173            throw new CalculateException(Bundle.getMessage("ArithmeticNotIntegerNumberError", left));
174        }
175    }
176    
177    
178    private Object xor(Object left, Object right) throws CalculateException {
179        if (TypeConversionUtil.isIntegerNumber(left)) {
180            if (TypeConversionUtil.isIntegerNumber(right)) {
181                return ((Number)left).longValue() ^ ((Number)right).longValue();
182            } else {
183                throw new CalculateException(Bundle.getMessage("ArithmeticNotIntegerNumberError", right));
184            }
185        } else {
186            throw new CalculateException(Bundle.getMessage("ArithmeticNotIntegerNumberError", left));
187        }
188    }
189    
190    
191    private Object shiftLeft(Object left, Object right) throws CalculateException {
192        if (TypeConversionUtil.isIntegerNumber(left)) {
193            if (TypeConversionUtil.isIntegerNumber(right)) {
194                return ((Number)left).longValue() << ((Number)right).longValue();
195            } else {
196                throw new CalculateException(Bundle.getMessage("ArithmeticNotIntegerNumberError", right));
197            }
198        } else {
199            throw new CalculateException(Bundle.getMessage("ArithmeticNotIntegerNumberError", left));
200        }
201    }
202    
203    
204    private Object shiftRight(Object left, Object right) throws CalculateException {
205        if (TypeConversionUtil.isIntegerNumber(left)) {
206            if (TypeConversionUtil.isIntegerNumber(right)) {
207                return ((Number)left).longValue() >> ((Number)right).longValue();
208            } else {
209                throw new CalculateException(Bundle.getMessage("ArithmeticNotIntegerNumberError", right));
210            }
211        } else {
212            throw new CalculateException(Bundle.getMessage("ArithmeticNotIntegerNumberError", left));
213        }
214    }
215    
216    
217    private Object unsignedShiftRight(Object left, Object right) throws CalculateException {
218        if (TypeConversionUtil.isIntegerNumber(left)) {
219            if (TypeConversionUtil.isIntegerNumber(right)) {
220                return ((Number)left).longValue() >>> ((Number)right).longValue();
221            } else {
222                throw new CalculateException(Bundle.getMessage("ArithmeticNotIntegerNumberError", right));
223            }
224        } else {
225            throw new CalculateException(Bundle.getMessage("ArithmeticNotIntegerNumberError", left));
226        }
227    }
228    
229    
230    @Override
231    public Object calculate(SymbolTable symbolTable) throws JmriException {
232        
233        if (_tokenType == TokenType.ASSIGN) {
234            Object value = _rightSide.calculate(symbolTable);
235            _leftSide.assignValue(symbolTable, value);
236            return value;
237        }
238        
239        Object left = _leftSide.calculate(symbolTable);
240        Object right = _rightSide.calculate(symbolTable);
241        
242        // Convert a boolean value to an integer value
243        if (left instanceof Boolean) {
244            left = ((Boolean)left) ? 1 : 0;
245        }
246        if (right instanceof Boolean) {
247            right = ((Boolean)right) ? 1 : 0;
248        }
249        
250        Object result;
251        
252        if (_tokenType == TokenType.ASSIGN_ADD) {
253            // Add can handle String concatenation
254            result = add(left, right);
255        } else {
256            // For the other arithmetic operators, except add, only numbers can
257            // be handled. For other types, return 0.
258            if (! TypeConversionUtil.isFloatingNumber(left)) {
259                result = 0;
260            } else if (! TypeConversionUtil.isFloatingNumber(right)) {
261                result = 0;
262            } else {
263                switch (_tokenType) {
264                    case ASSIGN_SUBTRACKT:
265                        result = subtract(left, right);
266                        break;
267                    case ASSIGN_MULTIPLY:
268                        result = multiply(left, right);
269                        break;
270                    case ASSIGN_DIVIDE:
271                        result = divide(left, right);
272                        break;
273                    case ASSIGN_MODULO:
274                        result = modulo(left, right);
275                        break;
276                    case ASSIGN_AND:
277                        result = and(left, right);
278                        break;
279                    case ASSIGN_OR:
280                        result = or(left, right);
281                        break;
282                    case ASSIGN_XOR:
283                        result = xor(left, right);
284                        break;
285                    case ASSIGN_SHIFT_LEFT:
286                        result = shiftLeft(left, right);
287                        break;
288                    case ASSIGN_SHIFT_RIGHT:
289                        result = shiftRight(left, right);
290                        break;
291                    case ASSIGN_UNSIGNED_SHIFT_RIGHT:
292                        result = unsignedShiftRight(left, right);
293                        break;
294                    default:
295                        throw new CalculateException("Unknown arithmetic operator: "+_tokenType.name());
296                }
297            }
298        }
299        
300        _leftSide.assignValue(symbolTable, result);
301        return result;
302    }
303    
304    
305    /** {@inheritDoc} */
306    @Override
307    public String getDefinitionString() {
308        String operStr;
309        switch (_tokenType) {
310            case ADD:
311                operStr = "+";
312                break;
313                
314            case SUBTRACKT:
315                operStr = "-";
316                break;
317                
318            case MULTIPLY:
319                operStr = "*";
320                break;
321                
322            case DIVIDE:
323                operStr = "/";
324                break;
325                
326            case MODULO:
327                operStr = "%";
328                break;
329                
330            case ASSIGN:
331                operStr = "=";
332                break;
333                
334            case ASSIGN_ADD:
335                operStr = "+=";
336                break;
337                
338            case ASSIGN_SUBTRACKT:
339                operStr = "-=";
340                break;
341                
342            case ASSIGN_MULTIPLY:
343                operStr = "*=";
344                break;
345                
346            case ASSIGN_DIVIDE:
347                operStr = "/=";
348                break;
349                
350            case ASSIGN_MODULO:
351                operStr = "%=";
352                break;
353                
354            case ASSIGN_AND:
355                operStr = "&=";
356                break;
357                
358            case ASSIGN_OR:
359                operStr = "|=";
360                break;
361                
362            case ASSIGN_XOR:
363                operStr = "^=";
364                break;
365                
366            case ASSIGN_SHIFT_LEFT:
367                operStr = "<<=";
368                break;
369                
370            case ASSIGN_SHIFT_RIGHT:
371                operStr = ">>=";
372                break;
373                
374            case ASSIGN_UNSIGNED_SHIFT_RIGHT:
375                operStr = ">>>=";
376                break;
377                
378            default:
379                throw new UnsupportedOperationException("Unknown arithmetic operator: "+_tokenType.name());
380        }
381        
382        String leftSideString = _leftSide != null ? _leftSide.getDefinitionString() : "null";
383        String rightSideString = _rightSide != null ? _rightSide.getDefinitionString() : "null";
384        return "("+leftSideString+")" + operStr + "("+rightSideString+")";
385    }
386    
387}