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 ExpressionNodeBinaryOperator implements ExpressionNode {
011
012    private final TokenType _tokenType;
013    private final ExpressionNode _leftSide;
014    private final ExpressionNode _rightSide;
015    
016    public ExpressionNodeBinaryOperator(TokenType tokenType, ExpressionNode leftSide, ExpressionNode rightSide) {
017        _tokenType = tokenType;
018        _leftSide = leftSide;
019        _rightSide = rightSide;
020        
021        if (_rightSide == null) {
022            throw new IllegalArgumentException("rightSide must not be null");
023        }
024        
025        // Verify that the token is of the correct type
026        switch (_tokenType) {
027            case BINARY_OR:
028            case BINARY_XOR:
029            case BINARY_AND:
030                if (_leftSide == null) {
031                    throw new IllegalArgumentException("leftSide must not be null for operators BINARY AND, BINARY OR and BINARY XOR");
032                }
033                break;
034                
035            case BINARY_NOT:
036                if (_leftSide != null) {
037                    throw new IllegalArgumentException("leftSide must be null for operator BINARY NOT");
038                }
039                break;
040                
041            default:
042                throw new IllegalArgumentException("Unsupported binary operator: "+_tokenType.name());
043        }
044    }
045    
046    @Override
047    public Object calculate(SymbolTable symbolTable) throws JmriException {
048        
049        Object leftValue = null;
050        if (_tokenType != TokenType.BINARY_NOT) {
051            // Left value must be calculated _before_ right value is calculated.
052            // When a value is calculated, a method might be called, and the
053            // order of these calls must be correct.
054            leftValue = _leftSide.calculate(symbolTable);
055        }
056        if (leftValue == null) leftValue = false;
057        
058        Object rightValue = _rightSide.calculate(symbolTable);
059        if (rightValue == null) rightValue = false;
060        
061        if (!TypeConversionUtil.isIntegerNumber(rightValue)) {
062            throw new CalculateException(Bundle.getMessage("ArithmeticNotIntegerNumberError", rightValue));
063        }
064        long right = TypeConversionUtil.convertToLong(rightValue);
065        
066        if (_tokenType == TokenType.BINARY_NOT) {
067            return ~ right;
068        }
069        
070        if (!TypeConversionUtil.isIntegerNumber(leftValue)) {
071            throw new CalculateException(Bundle.getMessage("ArithmeticNotIntegerNumberError", leftValue));
072        }
073        long left = TypeConversionUtil.convertToLong(leftValue);
074        
075        switch (_tokenType) {
076            case BINARY_OR:
077                return left | right;
078                
079            case BINARY_XOR:
080                return left ^ right;
081                
082            case BINARY_AND:
083                return left & right;
084                
085            default:
086                throw new CalculateException("Unknown binary operator: "+_tokenType.name());
087        }
088    }
089    
090    /** {@inheritDoc} */
091    @Override
092    public String getDefinitionString() {
093        String operStr;
094        switch (_tokenType) {
095            case BINARY_OR:
096                operStr = "|";
097                break;
098                
099            case BINARY_XOR:
100                operStr = "^";
101                break;
102                
103            case BINARY_AND:
104                operStr = "&";
105                break;
106                
107            case BINARY_NOT:
108                operStr = "~";
109                break;
110                
111            default:
112                throw new UnsupportedOperationException("Unknown arithmetic operator: "+_tokenType.name());
113        }
114        if (_leftSide != null) {
115            return "("+_leftSide.getDefinitionString()+")" + operStr + "("+_rightSide.getDefinitionString()+")";
116        } else {
117            return operStr + "("+_rightSide.getDefinitionString()+")";
118        }
119    }
120    
121}