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   * Implements the modulus operator.
32   *
33   * @author Andy Clement
34   * @author Juergen Hoeller
35   * @author Giovanni Dall'Oglio Risso
36   * @since 3.0
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  				// Unknown Number subtypes -> best guess is double division
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 }