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.core.convert.TypeDescriptor;
24 import org.springframework.expression.EvaluationException;
25 import org.springframework.expression.Operation;
26 import org.springframework.expression.TypeConverter;
27 import org.springframework.expression.TypedValue;
28 import org.springframework.expression.spel.CodeFlow;
29 import org.springframework.expression.spel.ExpressionState;
30 import org.springframework.util.Assert;
31 import org.springframework.util.NumberUtils;
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 public class OpPlus extends Operator {
51
52 public OpPlus(int pos, SpelNodeImpl... operands) {
53 super("+", pos, operands);
54 Assert.notEmpty(operands);
55 }
56
57
58 @Override
59 public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
60 SpelNodeImpl leftOp = getLeftOperand();
61 SpelNodeImpl rightOp = getRightOperand();
62
63 if (rightOp == null) {
64 Object operandOne = leftOp.getValueInternal(state).getValue();
65 if (operandOne instanceof Number) {
66 if (operandOne instanceof Double) {
67 this.exitTypeDescriptor = "D";
68 }
69 else if (operandOne instanceof Float) {
70 this.exitTypeDescriptor = "F";
71 }
72 else if (operandOne instanceof Long) {
73 this.exitTypeDescriptor = "J";
74 }
75 else if (operandOne instanceof Integer) {
76 this.exitTypeDescriptor = "I";
77 }
78 return new TypedValue(operandOne);
79 }
80 return state.operate(Operation.ADD, operandOne, null);
81 }
82
83 TypedValue operandOneValue = leftOp.getValueInternal(state);
84 Object leftOperand = operandOneValue.getValue();
85 TypedValue operandTwoValue = rightOp.getValueInternal(state);
86 Object rightOperand = operandTwoValue.getValue();
87
88 if (leftOperand instanceof Number && rightOperand instanceof Number) {
89 Number leftNumber = (Number) leftOperand;
90 Number rightNumber = (Number) rightOperand;
91
92 if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) {
93 BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class);
94 BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class);
95 return new TypedValue(leftBigDecimal.add(rightBigDecimal));
96 }
97 else if (leftNumber instanceof Double || rightNumber instanceof Double) {
98 if (leftNumber.getClass() == rightNumber.getClass()) {
99 this.exitTypeDescriptor = "D";
100 }
101 return new TypedValue(leftNumber.doubleValue() + rightNumber.doubleValue());
102 }
103 else if (leftNumber instanceof Float || rightNumber instanceof Float) {
104 if (leftNumber.getClass() == rightNumber.getClass()) {
105 this.exitTypeDescriptor = "F";
106 }
107 return new TypedValue(leftNumber.floatValue() + rightNumber.floatValue());
108 }
109 else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) {
110 BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class);
111 BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class);
112 return new TypedValue(leftBigInteger.add(rightBigInteger));
113 }
114 else if (leftNumber instanceof Long || rightNumber instanceof Long) {
115 if (leftNumber.getClass() == rightNumber.getClass()) {
116 this.exitTypeDescriptor = "J";
117 }
118 return new TypedValue(leftNumber.longValue() + rightNumber.longValue());
119 }
120 else if (CodeFlow.isIntegerForNumericOp(leftNumber) || CodeFlow.isIntegerForNumericOp(rightNumber)) {
121 if (leftNumber instanceof Integer && rightNumber instanceof Integer) {
122 this.exitTypeDescriptor = "I";
123 }
124 return new TypedValue(leftNumber.intValue() + rightNumber.intValue());
125 }
126 else {
127
128 return new TypedValue(leftNumber.doubleValue() + rightNumber.doubleValue());
129 }
130 }
131
132 if (leftOperand instanceof String && rightOperand instanceof String) {
133 this.exitTypeDescriptor = "Ljava/lang/String";
134 return new TypedValue((String) leftOperand + rightOperand);
135 }
136
137 if (leftOperand instanceof String) {
138 return new TypedValue(
139 leftOperand + (rightOperand == null ? "null" : convertTypedValueToString(operandTwoValue, state)));
140 }
141
142 if (rightOperand instanceof String) {
143 return new TypedValue(
144 (leftOperand == null ? "null" : convertTypedValueToString(operandOneValue, state)) + rightOperand);
145 }
146
147 return state.operate(Operation.ADD, leftOperand, rightOperand);
148 }
149
150 @Override
151 public String toStringAST() {
152 if (this.children.length < 2) {
153 return "+" + getLeftOperand().toStringAST();
154 }
155 return super.toStringAST();
156 }
157
158 @Override
159 public SpelNodeImpl getRightOperand() {
160 if (this.children.length < 2) {
161 return null;
162 }
163 return this.children[1];
164 }
165
166
167
168
169
170
171
172
173 private static String convertTypedValueToString(TypedValue value, ExpressionState state) {
174 TypeConverter typeConverter = state.getEvaluationContext().getTypeConverter();
175 TypeDescriptor typeDescriptor = TypeDescriptor.valueOf(String.class);
176 if (typeConverter.canConvert(value.getTypeDescriptor(), typeDescriptor)) {
177 return String.valueOf(typeConverter.convertValue(value.getValue(),
178 value.getTypeDescriptor(), typeDescriptor));
179 }
180 return String.valueOf(value.getValue());
181 }
182
183 @Override
184 public boolean isCompilable() {
185 if (!getLeftOperand().isCompilable()) {
186 return false;
187 }
188 if (this.children.length > 1) {
189 if (!getRightOperand().isCompilable()) {
190 return false;
191 }
192 }
193 return (this.exitTypeDescriptor != null);
194 }
195
196
197
198
199
200 private void walk(MethodVisitor mv, CodeFlow cf, SpelNodeImpl operand) {
201 if (operand instanceof OpPlus) {
202 OpPlus plus = (OpPlus)operand;
203 walk(mv,cf,plus.getLeftOperand());
204 walk(mv,cf,plus.getRightOperand());
205 }
206 else {
207 cf.enterCompilationScope();
208 operand.generateCode(mv,cf);
209 if (cf.lastDescriptor() != "Ljava/lang/String") {
210 mv.visitTypeInsn(CHECKCAST, "java/lang/String");
211 }
212 cf.exitCompilationScope();
213 mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
214 }
215 }
216
217 @Override
218 public void generateCode(MethodVisitor mv, CodeFlow cf) {
219 if (this.exitTypeDescriptor == "Ljava/lang/String") {
220 mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
221 mv.visitInsn(DUP);
222 mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
223 walk(mv,cf,getLeftOperand());
224 walk(mv,cf,getRightOperand());
225 mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
226 }
227 else {
228 getLeftOperand().generateCode(mv, cf);
229 String leftDesc = getLeftOperand().exitTypeDescriptor;
230 if (!CodeFlow.isPrimitive(leftDesc)) {
231 CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), leftDesc);
232 }
233 if (this.children.length > 1) {
234 cf.enterCompilationScope();
235 getRightOperand().generateCode(mv, cf);
236 String rightDesc = getRightOperand().exitTypeDescriptor;
237 cf.exitCompilationScope();
238 if (!CodeFlow.isPrimitive(rightDesc)) {
239 CodeFlow.insertUnboxInsns(mv, this.exitTypeDescriptor.charAt(0), rightDesc);
240 }
241 switch (this.exitTypeDescriptor.charAt(0)) {
242 case 'I':
243 mv.visitInsn(IADD);
244 break;
245 case 'J':
246 mv.visitInsn(LADD);
247 break;
248 case 'F':
249 mv.visitInsn(FADD);
250 break;
251 case 'D':
252 mv.visitInsn(DADD);
253 break;
254 default:
255 throw new IllegalStateException(
256 "Unrecognized exit type descriptor: '" + this.exitTypeDescriptor + "'");
257 }
258 }
259 }
260 cf.pushDescriptor(this.exitTypeDescriptor);
261 }
262
263 }