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 ExpressionNodeBooleanOperator implements ExpressionNode { 011 012 private final TokenType _tokenType; 013 private final ExpressionNode _leftSide; 014 private final ExpressionNode _rightSide; 015 016 public ExpressionNodeBooleanOperator(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 BOOLEAN_OR: 028 case BOOLEAN_XOR: 029 case BOOLEAN_AND: 030 if (_leftSide == null) { 031 throw new IllegalArgumentException("leftSide must not be null for operators AND, OR and XOR"); 032 } 033 break; 034 035 case BOOLEAN_NOT: 036 if (_leftSide != null) { 037 throw new IllegalArgumentException("leftSide must be null for operator NOT"); 038 } 039 break; 040 041 default: 042 throw new IllegalArgumentException("Unsupported boolean 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.BOOLEAN_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 // For example, if myArray is an array, the formula might be: 055 // myArray.add("Hello") || myArray.add(" ") || myArray.add("World!") 056 leftValue = _leftSide.calculate(symbolTable); 057 } 058 if (leftValue == null) leftValue = false; 059 060 Object rightValue = _rightSide.calculate(symbolTable); 061 if (rightValue == null) rightValue = false; 062 063 if (!(rightValue instanceof Boolean)) { 064 if (TypeConversionUtil.isIntegerNumber(rightValue)) { 065 // Convert to true or false 066 rightValue = ((Number)rightValue).longValue() != 0; 067 } else { 068 throw new CalculateException(Bundle.getMessage("ArithmeticNotBooleanOrIntegerNumberError", rightValue)); 069 } 070 } 071 boolean right = (Boolean)rightValue; 072 073 if (_tokenType == TokenType.BOOLEAN_NOT) { 074 return ! right; 075 } 076 077 if (!(leftValue instanceof Boolean)) { 078 if (TypeConversionUtil.isIntegerNumber(leftValue)) { 079 // Convert to true or false 080 leftValue = ((Number)leftValue).longValue() != 0; 081 } else { 082 throw new CalculateException(Bundle.getMessage("ArithmeticNotBooleanOrIntegerNumberError", leftValue)); 083 } 084 } 085 boolean left = (Boolean)leftValue; 086 087 switch (_tokenType) { 088 case BOOLEAN_OR: 089 return left || right; 090 091 case BOOLEAN_XOR: 092 return (left && !right) || (!left && right); 093 094 case BOOLEAN_AND: 095 return left && right; 096 097 default: 098 throw new CalculateException("Unknown boolean operator: "+_tokenType.name()); 099 } 100 } 101 102 /** {@inheritDoc} */ 103 @Override 104 public String getDefinitionString() { 105 String operStr; 106 switch (_tokenType) { 107 case BOOLEAN_OR: 108 operStr = "||"; 109 break; 110 111 case BOOLEAN_XOR: 112 operStr = "^^"; 113 break; 114 115 case BOOLEAN_AND: 116 operStr = "&&"; 117 break; 118 119 case BOOLEAN_NOT: 120 operStr = "!"; 121 break; 122 123 default: 124 throw new UnsupportedOperationException("Unknown arithmetic operator: "+_tokenType.name()); 125 } 126 if (_leftSide != null) { 127 return "("+_leftSide.getDefinitionString()+")" + operStr + "("+_rightSide.getDefinitionString()+")"; 128 } else { 129 return operStr + "("+_rightSide.getDefinitionString()+")"; 130 } 131 } 132 133}