001/* 002 * Copyright 2002-2015 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.expression.spel.ast; 018 019import java.math.BigDecimal; 020import java.math.BigInteger; 021 022import org.springframework.asm.MethodVisitor; 023import org.springframework.expression.EvaluationException; 024import org.springframework.expression.Operation; 025import org.springframework.expression.TypedValue; 026import org.springframework.expression.spel.CodeFlow; 027import org.springframework.expression.spel.ExpressionState; 028import org.springframework.util.NumberUtils; 029 030/** 031 * The minus operator supports: 032 * <ul> 033 * <li>subtraction of numbers 034 * <li>subtraction of an int from a string of one character 035 * (effectively decreasing that character), so 'd'-3='a' 036 * </ul> 037 * 038 * <p>It can be used as a unary operator for numbers. 039 * The standard promotions are performed when the operand types vary (double-int=double). 040 * For other options it defers to the registered overloader. 041 * 042 * @author Andy Clement 043 * @author Juergen Hoeller 044 * @author Giovanni Dall'Oglio Risso 045 * @since 3.0 046 */ 047public class OpMinus extends Operator { 048 049 public OpMinus(int pos, SpelNodeImpl... operands) { 050 super("-", pos, operands); 051 } 052 053 054 @Override 055 public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { 056 SpelNodeImpl leftOp = getLeftOperand(); 057 SpelNodeImpl rightOp = getRightOperand(); 058 059 if (rightOp == null) { // if only one operand, then this is unary minus 060 Object operand = leftOp.getValueInternal(state).getValue(); 061 if (operand instanceof Number) { 062 if (operand instanceof BigDecimal) { 063 return new TypedValue(((BigDecimal) operand).negate()); 064 } 065 else if (operand instanceof Double) { 066 this.exitTypeDescriptor = "D"; 067 return new TypedValue(0 - ((Number) operand).doubleValue()); 068 } 069 else if (operand instanceof Float) { 070 this.exitTypeDescriptor = "F"; 071 return new TypedValue(0 - ((Number) operand).floatValue()); 072 } 073 else if (operand instanceof BigInteger) { 074 return new TypedValue(((BigInteger) operand).negate()); 075 } 076 else if (operand instanceof Long) { 077 this.exitTypeDescriptor = "J"; 078 return new TypedValue(0 - ((Number) operand).longValue()); 079 } 080 else if (operand instanceof Integer) { 081 this.exitTypeDescriptor = "I"; 082 return new TypedValue(0 - ((Number) operand).intValue()); 083 } 084 else if (operand instanceof Short) { 085 return new TypedValue(0 - ((Number) operand).shortValue()); 086 } 087 else if (operand instanceof Byte) { 088 return new TypedValue(0 - ((Number) operand).byteValue()); 089 } 090 else { 091 // Unknown Number subtypes -> best guess is double subtraction 092 return new TypedValue(0 - ((Number) operand).doubleValue()); 093 } 094 } 095 return state.operate(Operation.SUBTRACT, operand, null); 096 } 097 098 Object left = leftOp.getValueInternal(state).getValue(); 099 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 this.exitTypeDescriptor = "D"; 112 return new TypedValue(leftNumber.doubleValue() - rightNumber.doubleValue()); 113 } 114 else if (leftNumber instanceof Float || rightNumber instanceof Float) { 115 this.exitTypeDescriptor = "F"; 116 return new TypedValue(leftNumber.floatValue() - rightNumber.floatValue()); 117 } 118 else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) { 119 BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class); 120 BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class); 121 return new TypedValue(leftBigInteger.subtract(rightBigInteger)); 122 } 123 else if (leftNumber instanceof Long || rightNumber instanceof Long) { 124 this.exitTypeDescriptor = "J"; 125 return new TypedValue(leftNumber.longValue() - rightNumber.longValue()); 126 } 127 else if (CodeFlow.isIntegerForNumericOp(leftNumber) || CodeFlow.isIntegerForNumericOp(rightNumber)) { 128 this.exitTypeDescriptor = "I"; 129 return new TypedValue(leftNumber.intValue() - rightNumber.intValue()); 130 } 131 else { 132 // Unknown Number subtypes -> best guess is double subtraction 133 return new TypedValue(leftNumber.doubleValue() - rightNumber.doubleValue()); 134 } 135 } 136 137 if (left instanceof String && right instanceof Integer && ((String) left).length() == 1) { 138 String theString = (String) left; 139 Integer theInteger = (Integer) right; 140 // Implements character - int (ie. b - 1 = a) 141 return new TypedValue(Character.toString((char) (theString.charAt(0) - theInteger))); 142 } 143 144 return state.operate(Operation.SUBTRACT, left, right); 145 } 146 147 @Override 148 public String toStringAST() { 149 if (getRightOperand() == null) { // unary minus 150 return "-" + getLeftOperand().toStringAST(); 151 } 152 return super.toStringAST(); 153 } 154 155 @Override 156 public SpelNodeImpl getRightOperand() { 157 if (this.children.length < 2) { 158 return null; 159 } 160 return this.children[1]; 161 } 162 163 @Override 164 public boolean isCompilable() { 165 if (!getLeftOperand().isCompilable()) { 166 return false; 167 } 168 if (this.children.length > 1) { 169 if (!getRightOperand().isCompilable()) { 170 return false; 171 } 172 } 173 return (this.exitTypeDescriptor != null); 174 } 175 176 @Override 177 public void generateCode(MethodVisitor mv, CodeFlow cf) { 178 getLeftOperand().generateCode(mv, cf); 179 String leftDesc = getLeftOperand().exitTypeDescriptor; 180 CodeFlow.insertNumericUnboxOrPrimitiveTypeCoercion(mv, leftDesc, this.exitTypeDescriptor.charAt(0)); 181 if (this.children.length > 1) { 182 cf.enterCompilationScope(); 183 getRightOperand().generateCode(mv, cf); 184 String rightDesc = getRightOperand().exitTypeDescriptor; 185 cf.exitCompilationScope(); 186 CodeFlow.insertNumericUnboxOrPrimitiveTypeCoercion(mv, rightDesc, this.exitTypeDescriptor.charAt(0)); 187 switch (this.exitTypeDescriptor.charAt(0)) { 188 case 'I': 189 mv.visitInsn(ISUB); 190 break; 191 case 'J': 192 mv.visitInsn(LSUB); 193 break; 194 case 'F': 195 mv.visitInsn(FSUB); 196 break; 197 case 'D': 198 mv.visitInsn(DSUB); 199 break; 200 default: 201 throw new IllegalStateException( 202 "Unrecognized exit type descriptor: '" + this.exitTypeDescriptor + "'"); 203 } 204 } 205 else { 206 switch (this.exitTypeDescriptor.charAt(0)) { 207 case 'I': 208 mv.visitInsn(INEG); 209 break; 210 case 'J': 211 mv.visitInsn(LNEG); 212 break; 213 case 'F': 214 mv.visitInsn(FNEG); 215 break; 216 case 'D': 217 mv.visitInsn(DNEG); 218 break; 219 default: 220 throw new IllegalStateException( 221 "Unrecognized exit type descriptor: '" + this.exitTypeDescriptor + "'"); 222 } 223 } 224 cf.pushDescriptor(this.exitTypeDescriptor); 225 } 226 227}