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