001/* 002 * Copyright 2002-2015 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.expression.spel.ast; 018 019import java.math.BigDecimal; 020import java.math.BigInteger; 021import java.math.RoundingMode; 022 023import org.springframework.asm.MethodVisitor; 024import org.springframework.expression.EvaluationException; 025import org.springframework.expression.Operation; 026import org.springframework.expression.TypedValue; 027import org.springframework.expression.spel.CodeFlow; 028import org.springframework.expression.spel.ExpressionState; 029import org.springframework.util.NumberUtils; 030 031/** 032 * Implements division operator. 033 * 034 * @author Andy Clement 035 * @author Juergen Hoeller 036 * @author Giovanni Dall'Oglio Risso 037 * @since 3.0 038 */ 039public class OpDivide extends Operator { 040 041 public OpDivide(int pos, SpelNodeImpl... operands) { 042 super("/", pos, operands); 043 } 044 045 046 @Override 047 public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { 048 Object leftOperand = getLeftOperand().getValueInternal(state).getValue(); 049 Object rightOperand = getRightOperand().getValueInternal(state).getValue(); 050 051 if (leftOperand instanceof Number && rightOperand instanceof Number) { 052 Number leftNumber = (Number) leftOperand; 053 Number rightNumber = (Number) rightOperand; 054 055 if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) { 056 BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class); 057 BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class); 058 int scale = Math.max(leftBigDecimal.scale(), rightBigDecimal.scale()); 059 return new TypedValue(leftBigDecimal.divide(rightBigDecimal, scale, RoundingMode.HALF_EVEN)); 060 } 061 else if (leftNumber instanceof Double || rightNumber instanceof Double) { 062 this.exitTypeDescriptor = "D"; 063 return new TypedValue(leftNumber.doubleValue() / rightNumber.doubleValue()); 064 } 065 else if (leftNumber instanceof Float || rightNumber instanceof Float) { 066 this.exitTypeDescriptor = "F"; 067 return new TypedValue(leftNumber.floatValue() / rightNumber.floatValue()); 068 } 069 else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) { 070 BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class); 071 BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class); 072 return new TypedValue(leftBigInteger.divide(rightBigInteger)); 073 } 074 else if (leftNumber instanceof Long || rightNumber instanceof Long) { 075 this.exitTypeDescriptor = "J"; 076 return new TypedValue(leftNumber.longValue() / rightNumber.longValue()); 077 } 078 else if (CodeFlow.isIntegerForNumericOp(leftNumber) || CodeFlow.isIntegerForNumericOp(rightNumber)) { 079 this.exitTypeDescriptor = "I"; 080 return new TypedValue(leftNumber.intValue() / rightNumber.intValue()); 081 } 082 else { 083 // Unknown Number subtypes -> best guess is double division 084 return new TypedValue(leftNumber.doubleValue() / rightNumber.doubleValue()); 085 } 086 } 087 088 return state.operate(Operation.DIVIDE, leftOperand, rightOperand); 089 } 090 091 @Override 092 public boolean isCompilable() { 093 if (!getLeftOperand().isCompilable()) { 094 return false; 095 } 096 if (this.children.length > 1) { 097 if (!getRightOperand().isCompilable()) { 098 return false; 099 } 100 } 101 return (this.exitTypeDescriptor != null); 102 } 103 104 @Override 105 public void generateCode(MethodVisitor mv, CodeFlow cf) { 106 getLeftOperand().generateCode(mv, cf); 107 String leftDesc = getLeftOperand().exitTypeDescriptor; 108 CodeFlow.insertNumericUnboxOrPrimitiveTypeCoercion(mv, leftDesc, this.exitTypeDescriptor.charAt(0)); 109 if (this.children.length > 1) { 110 cf.enterCompilationScope(); 111 getRightOperand().generateCode(mv, cf); 112 String rightDesc = getRightOperand().exitTypeDescriptor; 113 cf.exitCompilationScope(); 114 CodeFlow.insertNumericUnboxOrPrimitiveTypeCoercion(mv, rightDesc, this.exitTypeDescriptor.charAt(0)); 115 switch (this.exitTypeDescriptor.charAt(0)) { 116 case 'I': 117 mv.visitInsn(IDIV); 118 break; 119 case 'J': 120 mv.visitInsn(LDIV); 121 break; 122 case 'F': 123 mv.visitInsn(FDIV); 124 break; 125 case 'D': 126 mv.visitInsn(DDIV); 127 break; 128 default: 129 throw new IllegalStateException( 130 "Unrecognized exit type descriptor: '" + this.exitTypeDescriptor + "'"); 131 } 132 } 133 cf.pushDescriptor(this.exitTypeDescriptor); 134 } 135 136}