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}