1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.expression.spel.ast;
18
19 import java.math.BigDecimal;
20 import java.math.BigInteger;
21 import java.math.RoundingMode;
22
23 import org.springframework.asm.MethodVisitor;
24 import org.springframework.expression.EvaluationException;
25 import org.springframework.expression.Operation;
26 import org.springframework.expression.TypedValue;
27 import org.springframework.expression.spel.CodeFlow;
28 import org.springframework.expression.spel.ExpressionState;
29 import org.springframework.util.NumberUtils;
30
31
32
33
34
35
36
37
38
39 public class OpDivide extends Operator {
40
41 public OpDivide(int pos, SpelNodeImpl... operands) {
42 super("/", pos, operands);
43 }
44
45
46 @Override
47 public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
48 Object leftOperand = getLeftOperand().getValueInternal(state).getValue();
49 Object rightOperand = getRightOperand().getValueInternal(state).getValue();
50
51 if (leftOperand instanceof Number && rightOperand instanceof Number) {
52 Number leftNumber = (Number) leftOperand;
53 Number rightNumber = (Number) rightOperand;
54
55 if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) {
56 BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class);
57 BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class);
58 int scale = Math.max(leftBigDecimal.scale(), rightBigDecimal.scale());
59 return new TypedValue(leftBigDecimal.divide(rightBigDecimal, scale, RoundingMode.HALF_EVEN));
60 }
61 else if (leftNumber instanceof Double || rightNumber instanceof Double) {
62 if (leftNumber.getClass() == rightNumber.getClass()) {
63 this.exitTypeDescriptor = "D";
64 }
65 return new TypedValue(leftNumber.doubleValue() / rightNumber.doubleValue());
66 }
67 else if (leftNumber instanceof Float || rightNumber instanceof Float) {
68 if (leftNumber.getClass() == rightNumber.getClass()) {
69 this.exitTypeDescriptor = "F";
70 }
71 return new TypedValue(leftNumber.floatValue() / rightNumber.floatValue());
72 }
73 else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) {
74 BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class);
75 BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class);
76 return new TypedValue(leftBigInteger.divide(rightBigInteger));
77 }
78 else if (leftNumber instanceof Long || rightNumber instanceof Long) {
79 if (leftNumber.getClass() == rightNumber.getClass()) {
80 this.exitTypeDescriptor = "J";
81 }
82 return new TypedValue(leftNumber.longValue() / rightNumber.longValue());
83 }
84 else if (CodeFlow.isIntegerForNumericOp(leftNumber) || CodeFlow.isIntegerForNumericOp(rightNumber)) {
85 if (leftNumber instanceof Integer && rightNumber instanceof Integer) {
86 this.exitTypeDescriptor = "I";
87 }
88 return new TypedValue(leftNumber.intValue() / rightNumber.intValue());
89 }
90 else {
91
92 return new TypedValue(leftNumber.doubleValue() / rightNumber.doubleValue());
93 }
94 }
95
96 return state.operate(Operation.DIVIDE, leftOperand, rightOperand);
97 }
98
99 @Override
100 public boolean isCompilable() {
101 if (!getLeftOperand().isCompilable()) {
102 return false;
103 }
104 if (this.children.length > 1) {
105 if (!getRightOperand().isCompilable()) {
106 return false;
107 }
108 }
109 return (this.exitTypeDescriptor != null);
110 }
111
112 @Override
113 public void generateCode(MethodVisitor mv, CodeFlow cf) {
114 getLeftOperand().generateCode(mv, cf);
115 String leftDesc = getLeftOperand().exitTypeDescriptor;
116 if (!CodeFlow.isPrimitive(leftDesc)) {
117 CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftDesc);
118 }
119 if (this.children.length > 1) {
120 cf.enterCompilationScope();
121 getRightOperand().generateCode(mv, cf);
122 String rightDesc = getRightOperand().exitTypeDescriptor;
123 cf.exitCompilationScope();
124 if (!CodeFlow.isPrimitive(rightDesc)) {
125 CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightDesc);
126 }
127 switch (this.exitTypeDescriptor.charAt(0)) {
128 case 'I':
129 mv.visitInsn(IDIV);
130 break;
131 case 'J':
132 mv.visitInsn(LDIV);
133 break;
134 case 'F':
135 mv.visitInsn(FDIV);
136 break;
137 case 'D':
138 mv.visitInsn(DDIV);
139 break;
140 default:
141 throw new IllegalStateException(
142 "Unrecognized exit type descriptor: '" + this.exitTypeDescriptor + "'");
143 }
144 }
145 cf.pushDescriptor(this.exitTypeDescriptor);
146 }
147
148 }