001package jmri.jmrit.logixng.util.parser;
002
003import jmri.JmriException;
004import jmri.jmrit.logixng.SymbolTable;
005
006/**
007 * A parsed expression
008 */
009public class ExpressionNodeComparingOperator implements ExpressionNode {
010
011    private final TokenType _tokenType;
012    private final ExpressionNode _leftSide;
013    private final ExpressionNode _rightSide;
014    
015    public ExpressionNodeComparingOperator(TokenType tokenType, ExpressionNode leftSide, ExpressionNode rightSide) {
016        _tokenType = tokenType;
017        _leftSide = leftSide;
018        _rightSide = rightSide;
019        
020        if (_leftSide == null) {
021            throw new IllegalArgumentException("leftSide must not be null");
022        }
023        if (_rightSide == null) {
024            throw new IllegalArgumentException("rightSide must not be null");
025        }
026        
027        // Verify that the token is of the correct type
028        switch (_tokenType) {
029            case EQUAL:
030            case NOT_EQUAL:
031            case LESS_THAN:
032            case LESS_OR_EQUAL:
033            case GREATER_THAN:
034            case GREATER_OR_EQUAL:
035                break;
036                
037            default:
038                throw new RuntimeException("Unknown comparing operator: "+_tokenType.name());
039        }
040    }
041    
042    public Object calculateNull(Object left, Object right) throws JmriException {
043        if ((left != null) && (right != null)) {
044            throw new RuntimeException("This method is only valid if left and/or right is null");
045        }
046        
047        switch (_tokenType) {
048            case EQUAL:
049                return left == right;
050            case NOT_EQUAL:
051                return left != right;
052            case LESS_THAN:
053                return (left == null) && (right != null);
054            case LESS_OR_EQUAL:
055                return left == null;
056            case GREATER_THAN:
057                return (left != null) && (right == null);
058            case GREATER_OR_EQUAL:
059                return right == null;
060
061            default:
062                throw new RuntimeException("Unknown arithmetic operator: "+_tokenType.name());
063        }
064    }
065    
066    @Override
067    public Object calculate(SymbolTable symbolTable) throws JmriException {
068        Object left = _leftSide.calculate(symbolTable);
069        Object right = _rightSide.calculate(symbolTable);
070        
071        if ((left == null) || (right == null)) return calculateNull(left, right);
072        
073        // Convert a boolean value to an integer value. false = 0 and true = 1.
074        if (left instanceof Boolean) {
075            left = ((Boolean)left) ? 1 : 0;
076        }
077        if (right instanceof Boolean) {
078            right = ((Boolean)right) ? 1 : 0;
079        }
080        
081        // If the operands are not numbers, ensure that they are strings
082        if ((!(left instanceof Number)) && (!(left instanceof String))) {
083            left = left.toString();
084        }
085        if ((!(right instanceof Number)) && (!(right instanceof String))) {
086            right = right.toString();
087        }
088        
089        // Object.toString() might return null
090        if ((left == null) || (right == null)) return calculateNull(left, right);
091        
092        // A number is always less than a String. If one operand is a number
093        // and the other operand is a String, we can change the operands to
094        // two integers to make the check easier.
095        if ((left instanceof Number) && (!(right instanceof Number))) {
096            left = 1;
097            right = 2;
098        } else if (!(left instanceof Number) && ((right instanceof Number))) {
099            left = 2;
100            right = 1;
101        }
102        
103        if ((left instanceof Double) && (!(right instanceof Double))) {
104            right = ((Number)left).doubleValue();
105        }
106        if ((right instanceof Double) && (!(left instanceof Double))) {
107            left = ((Number)left).doubleValue();
108        }
109        
110        if ((left instanceof Long) && (!(right instanceof Long))) {
111            right = ((Number)right).longValue();
112        }
113        if ((right instanceof Long) && (!(left instanceof Long))) {
114            left = ((Number)left).longValue();
115        }
116        
117        if (left instanceof Number) {
118            switch (_tokenType) {
119                case EQUAL:
120                    return left.equals(right);
121                case NOT_EQUAL:
122                    return ! left.equals(right);
123                case LESS_THAN:
124                    return ((Number) left).doubleValue() < ((Number) right).doubleValue();
125                case LESS_OR_EQUAL:
126                    return ((Number) left).doubleValue() <= ((Number) right).doubleValue();
127                case GREATER_THAN:
128                    return ((Number) left).doubleValue() > ((Number) right).doubleValue();
129                case GREATER_OR_EQUAL:
130                    return ((Number) left).doubleValue() >= ((Number) right).doubleValue();
131
132                default:
133                    throw new RuntimeException("Unknown arithmetic operator: "+_tokenType.name());
134            }
135        } else {
136            switch (_tokenType) {
137                case EQUAL:
138                    return left.equals(right);
139                case NOT_EQUAL:
140                    return ! left.equals(right);
141                case LESS_THAN:
142                    return ((String)left).compareTo(((String)right)) < 0;
143                case LESS_OR_EQUAL:
144                    return ((String)left).compareTo(((String)right)) <= 0;
145                case GREATER_THAN:
146                    return ((String)left).compareTo(((String)right)) > 0;
147                case GREATER_OR_EQUAL:
148                    return ((String)left).compareTo(((String)right)) >= 0;
149
150                default:
151                    throw new RuntimeException("Unknown comparing operator: "+_tokenType.name());
152            }
153        }
154    }
155    
156    /** {@inheritDoc} */
157    @Override
158    public String getDefinitionString() {
159        String operStr;
160        switch (_tokenType) {
161            case EQUAL:
162                operStr = "==";
163                break;
164                
165            case NOT_EQUAL:
166                operStr = "!=";
167                break;
168                
169            case LESS_THAN:
170                operStr = "<";
171                break;
172                
173            case LESS_OR_EQUAL:
174                operStr = "<=";
175                break;
176                
177            case GREATER_THAN:
178                operStr = ">";
179                break;
180                
181            case GREATER_OR_EQUAL:
182                operStr = ">=";
183                break;
184                
185            default:
186                throw new RuntimeException("Unknown comparing operator: "+_tokenType.name());
187        }
188        return "("+_leftSide.getDefinitionString()+")" + operStr + "("+_rightSide.getDefinitionString()+")";
189    }
190    
191}