/* * Copyright 2008 Reg Whitton * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.java.dev.eval; import java.math.BigDecimal; import java.util.Map; final class Operation { final Type type; final Operator operator; final Object operand1; final Object operand2; final Object operand3; private Operation(Type type, Operator operator, Object operand1, Object operand2, Object operand3) { this.type = type; this.operator = operator; this.operand1 = operand1; this.operand2 = operand2; this.operand3 = operand3; } static Operation nopOperationfactory(Object operand) { return new Operation(Operator.NOP.resultType, Operator.NOP, operand, null, null); } static Object unaryOperationfactory(Operator operator, Object operand) { validateOperandType(operand, operator.operandType); /* * If values can already be resolved then return result instead of operation */ if (operand instanceof BigDecimal) { return operator.perform((BigDecimal) operand, null, null); } return new Operation(operator.resultType, operator, operand, null, null); } static Object binaryOperationfactory(Operator operator, Object operand1, Object operand2) { validateOperandType(operand1, operator.operandType); validateOperandType(operand2, operator.operandType); /* * If values can already be resolved then return result instead of operation */ if (operand1 instanceof BigDecimal && operand2 instanceof BigDecimal) { return operator.perform((BigDecimal) operand1, (BigDecimal) operand2, null); } return new Operation(operator.resultType, operator, operand1, operand2, null); } static Object tenaryOperationFactory(Operator operator, Object operand1, Object operand2, Object operand3) { validateOperandType(operand1, Type.BOOLEAN); validateOperandType(operand2, Type.ARITHMETIC); validateOperandType(operand3, Type.ARITHMETIC); /* * If values can already be resolved then return result instead of operation */ if (operand1 instanceof BigDecimal) { return ((BigDecimal) operand1).signum() != 0 ? operand2 : operand3; } return new Operation(Type.ARITHMETIC, operator, operand1, operand2, operand3); } BigDecimal eval(Map<?,?> variables) { switch (this.operator.numberOfOperands) { case 3: return this.operator.perform(evaluateOperand(this.operand1, variables), evaluateOperand(this.operand2, variables), evaluateOperand(this.operand3, variables)); case 2: return this.operator.perform(evaluateOperand(this.operand1, variables), evaluateOperand(this.operand2, variables), null); default: return this.operator.perform(evaluateOperand(this.operand1, variables), null, null); } } private BigDecimal evaluateOperand(Object operand, Map<?,?> variables) { if (operand instanceof Operation) { return ((Operation) operand).eval(variables); } else if (operand instanceof String) { BigDecimal value; Object v = variables.get(operand); if (!(v instanceof BigDecimal)) { throw new RuntimeException("value '"+operand+"' is '"+v+"' which is not a BigDecimal"); } if (variables == null || (value = (BigDecimal) variables.get(operand)) == null) { throw new RuntimeException("no value for variable \"" + operand + "\""); } return value; } else { return (BigDecimal) operand; } } /** * Validate that where operations are combined together that the types are as expected. */ private static void validateOperandType(Object operand, Type type) { Type operandType; if (operand instanceof Operation && (operandType = ((Operation) operand).type) != type) { throw new RuntimeException("cannot use " + operandType.name + " operands with " + type.name + " operators"); } } @Override public String toString() { switch (this.operator.numberOfOperands) { case 3: return "(" + this.operand1 + this.operator.string + this.operand2 + ":" + this.operand3 + ")"; case 2: return "(" + this.operand1 + this.operator.string + this.operand2 + ")"; default: return "(" + this.operator.string + this.operand1 + ")"; } } }