001/* 002 * Copyright 2002-2019 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.Assert; 030import org.springframework.util.NumberUtils; 031 032/** 033 * Implements division operator. 034 * 035 * @author Andy Clement 036 * @author Juergen Hoeller 037 * @author Giovanni Dall'Oglio Risso 038 * @since 3.0 039 */ 040public class OpDivide extends Operator { 041 042 public OpDivide(int startPos, int endPos, SpelNodeImpl... operands) { 043 super("/", startPos, endPos, operands); 044 } 045 046 047 @Override 048 public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { 049 Object leftOperand = getLeftOperand().getValueInternal(state).getValue(); 050 Object rightOperand = getRightOperand().getValueInternal(state).getValue(); 051 052 if (leftOperand instanceof Number && rightOperand instanceof Number) { 053 Number leftNumber = (Number) leftOperand; 054 Number rightNumber = (Number) rightOperand; 055 056 if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) { 057 BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class); 058 BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class); 059 int scale = Math.max(leftBigDecimal.scale(), rightBigDecimal.scale()); 060 return new TypedValue(leftBigDecimal.divide(rightBigDecimal, scale, RoundingMode.HALF_EVEN)); 061 } 062 else if (leftNumber instanceof Double || rightNumber instanceof Double) { 063 this.exitTypeDescriptor = "D"; 064 return new TypedValue(leftNumber.doubleValue() / rightNumber.doubleValue()); 065 } 066 else if (leftNumber instanceof Float || rightNumber instanceof Float) { 067 this.exitTypeDescriptor = "F"; 068 return new TypedValue(leftNumber.floatValue() / rightNumber.floatValue()); 069 } 070 else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) { 071 BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class); 072 BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class); 073 return new TypedValue(leftBigInteger.divide(rightBigInteger)); 074 } 075 else if (leftNumber instanceof Long || rightNumber instanceof Long) { 076 this.exitTypeDescriptor = "J"; 077 return new TypedValue(leftNumber.longValue() / rightNumber.longValue()); 078 } 079 else if (CodeFlow.isIntegerForNumericOp(leftNumber) || CodeFlow.isIntegerForNumericOp(rightNumber)) { 080 this.exitTypeDescriptor = "I"; 081 return new TypedValue(leftNumber.intValue() / rightNumber.intValue()); 082 } 083 else { 084 // Unknown Number subtypes -> best guess is double division 085 return new TypedValue(leftNumber.doubleValue() / rightNumber.doubleValue()); 086 } 087 } 088 089 return state.operate(Operation.DIVIDE, leftOperand, rightOperand); 090 } 091 092 @Override 093 public boolean isCompilable() { 094 if (!getLeftOperand().isCompilable()) { 095 return false; 096 } 097 if (this.children.length > 1) { 098 if (!getRightOperand().isCompilable()) { 099 return false; 100 } 101 } 102 return (this.exitTypeDescriptor != null); 103 } 104 105 @Override 106 public void generateCode(MethodVisitor mv, CodeFlow cf) { 107 getLeftOperand().generateCode(mv, cf); 108 String leftDesc = getLeftOperand().exitTypeDescriptor; 109 String exitDesc = this.exitTypeDescriptor; 110 Assert.state(exitDesc != null, "No exit type descriptor"); 111 char targetDesc = exitDesc.charAt(0); 112 CodeFlow.insertNumericUnboxOrPrimitiveTypeCoercion(mv, leftDesc, targetDesc); 113 if (this.children.length > 1) { 114 cf.enterCompilationScope(); 115 getRightOperand().generateCode(mv, cf); 116 String rightDesc = getRightOperand().exitTypeDescriptor; 117 cf.exitCompilationScope(); 118 CodeFlow.insertNumericUnboxOrPrimitiveTypeCoercion(mv, rightDesc, targetDesc); 119 switch (targetDesc) { 120 case 'I': 121 mv.visitInsn(IDIV); 122 break; 123 case 'J': 124 mv.visitInsn(LDIV); 125 break; 126 case 'F': 127 mv.visitInsn(FDIV); 128 break; 129 case 'D': 130 mv.visitInsn(DDIV); 131 break; 132 default: 133 throw new IllegalStateException( 134 "Unrecognized exit type descriptor: '" + this.exitTypeDescriptor + "'"); 135 } 136 } 137 cf.pushDescriptor(this.exitTypeDescriptor); 138 } 139 140}