View Javadoc
1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
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   * The minus operator supports:
32   * <ul>
33   * <li>subtraction of numbers
34   * <li>subtraction of an int from a string of one character
35   * (effectively decreasing that character), so 'd'-3='a'
36   * </ul>
37   *
38   * <p>It can be used as a unary operator for numbers.
39   * The standard promotions are performed when the operand types vary (double-int=double).
40   * For other options it defers to the registered overloader.
41   *
42   * @author Andy Clement
43   * @author Juergen Hoeller
44   * @author Giovanni Dall'Oglio Risso
45   * @since 3.0
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) {  // if only one operand, then this is unary minus
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  					// Unknown Number subtypes -> best guess is double subtraction
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 				// Unknown Number subtypes -> best guess is double subtraction
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 			// Implements character - int (ie. b - 1 = a)
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) {  // unary minus
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 }