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
39
40
41
42
43
44
45
46
47 public class OpMinus extends Operator {
48
49 public OpMinus(int pos, SpelNodeImpl... operands) {
50 super("-", pos, operands);
51 }
52
53
54 @Override
55 public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
56 SpelNodeImpl leftOp = getLeftOperand();
57 SpelNodeImpl rightOp = getRightOperand();
58
59 if (rightOp == null) {
60 Object operand = leftOp.getValueInternal(state).getValue();
61 if (operand instanceof Number) {
62 if (operand instanceof BigDecimal) {
63 return new TypedValue(((BigDecimal) operand).negate());
64 }
65 else if (operand instanceof Double) {
66 this.exitTypeDescriptor = "D";
67 return new TypedValue(0 - ((Number) operand).doubleValue());
68 }
69 else if (operand instanceof Float) {
70 this.exitTypeDescriptor = "F";
71 return new TypedValue(0 - ((Number) operand).floatValue());
72 }
73 else if (operand instanceof BigInteger) {
74 return new TypedValue(((BigInteger) operand).negate());
75 }
76 else if (operand instanceof Long) {
77 this.exitTypeDescriptor = "J";
78 return new TypedValue(0 - ((Number) operand).longValue());
79 }
80 else if (operand instanceof Integer) {
81 this.exitTypeDescriptor = "I";
82 return new TypedValue(0 - ((Number) operand).intValue());
83 }
84 else if (operand instanceof Short) {
85 return new TypedValue(0 - ((Number) operand).shortValue());
86 }
87 else if (operand instanceof Byte) {
88 return new TypedValue(0 - ((Number) operand).byteValue());
89 }
90 else {
91
92 return new TypedValue(0 - ((Number) operand).doubleValue());
93 }
94 }
95 return state.operate(Operation.SUBTRACT, operand, null);
96 }
97
98 Object left = leftOp.getValueInternal(state).getValue();
99 Object right = rightOp.getValueInternal(state).getValue();
100
101 if (left instanceof Number && right instanceof Number) {
102 Number leftNumber = (Number) left;
103 Number rightNumber = (Number) right;
104
105 if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) {
106 BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class);
107 BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class);
108 return new TypedValue(leftBigDecimal.subtract(rightBigDecimal));
109 }
110 else if (leftNumber instanceof Double || rightNumber instanceof Double) {
111 if (leftNumber.getClass() == rightNumber.getClass()) {
112 this.exitTypeDescriptor = "D";
113 }
114 return new TypedValue(leftNumber.doubleValue() - rightNumber.doubleValue());
115 }
116 else if (leftNumber instanceof Float || rightNumber instanceof Float) {
117 if (leftNumber.getClass() == rightNumber.getClass()) {
118 this.exitTypeDescriptor = "F";
119 }
120 return new TypedValue(leftNumber.floatValue() - rightNumber.floatValue());
121 }
122 else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) {
123 BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class);
124 BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class);
125 return new TypedValue(leftBigInteger.subtract(rightBigInteger));
126 }
127 else if (leftNumber instanceof Long || rightNumber instanceof Long) {
128 if (leftNumber.getClass() == rightNumber.getClass()) {
129 this.exitTypeDescriptor = "J";
130 }
131 return new TypedValue(leftNumber.longValue() - rightNumber.longValue());
132 }
133 else if (CodeFlow.isIntegerForNumericOp(leftNumber) || CodeFlow.isIntegerForNumericOp(rightNumber)) {
134 if (leftNumber instanceof Integer && rightNumber instanceof Integer) {
135 this.exitTypeDescriptor = "I";
136 }
137 return new TypedValue(leftNumber.intValue() - rightNumber.intValue());
138 }
139 else {
140
141 return new TypedValue(leftNumber.doubleValue() - rightNumber.doubleValue());
142 }
143 }
144
145 if (left instanceof String && right instanceof Integer && ((String) left).length() == 1) {
146 String theString = (String) left;
147 Integer theInteger = (Integer) right;
148
149 return new TypedValue(Character.toString((char) (theString.charAt(0) - theInteger)));
150 }
151
152 return state.operate(Operation.SUBTRACT, left, right);
153 }
154
155 @Override
156 public String toStringAST() {
157 if (getRightOperand() == null) {
158 return "-" + getLeftOperand().toStringAST();
159 }
160 return super.toStringAST();
161 }
162
163 @Override
164 public SpelNodeImpl getRightOperand() {
165 if (this.children.length < 2) {
166 return null;
167 }
168 return this.children[1];
169 }
170
171 @Override
172 public boolean isCompilable() {
173 if (!getLeftOperand().isCompilable()) {
174 return false;
175 }
176 if (this.children.length > 1) {
177 if (!getRightOperand().isCompilable()) {
178 return false;
179 }
180 }
181 return (this.exitTypeDescriptor != null);
182 }
183
184 @Override
185 public void generateCode(MethodVisitor mv, CodeFlow cf) {
186 getLeftOperand().generateCode(mv, cf);
187 String leftDesc = getLeftOperand().exitTypeDescriptor;
188 if (!CodeFlow.isPrimitive(leftDesc)) {
189 CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftDesc);
190 }
191 if (this.children.length > 1) {
192 cf.enterCompilationScope();
193 getRightOperand().generateCode(mv, cf);
194 String rightDesc = getRightOperand().exitTypeDescriptor;
195 cf.exitCompilationScope();
196 if (!CodeFlow.isPrimitive(rightDesc)) {
197 CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightDesc);
198 }
199 switch (this.exitTypeDescriptor.charAt(0)) {
200 case 'I':
201 mv.visitInsn(ISUB);
202 break;
203 case 'J':
204 mv.visitInsn(LSUB);
205 break;
206 case 'F':
207 mv.visitInsn(FSUB);
208 break;
209 case 'D':
210 mv.visitInsn(DSUB);
211 break;
212 default:
213 throw new IllegalStateException(
214 "Unrecognized exit type descriptor: '" + this.exitTypeDescriptor + "'");
215 }
216 }
217 else {
218 switch (this.exitTypeDescriptor.charAt(0)) {
219 case 'I':
220 mv.visitInsn(INEG);
221 break;
222 case 'J':
223 mv.visitInsn(LNEG);
224 break;
225 case 'F':
226 mv.visitInsn(FNEG);
227 break;
228 case 'D':
229 mv.visitInsn(DNEG);
230 break;
231 default:
232 throw new IllegalStateException(
233 "Unrecognized exit type descriptor: '" + this.exitTypeDescriptor + "'");
234 }
235 }
236 cf.pushDescriptor(this.exitTypeDescriptor);
237 }
238
239 }