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 * Implements the {@code multiply} operator. 032 * 033 * <p>Conversions and promotions are handled as defined in 034 * <a href="https://java.sun.com/docs/books/jls/third_edition/html/conversions.html">Section 5.6.2 of the 035 * Java Language Specification</a>, with the addiction of {@code BigDecimal}/{@code BigInteger} management: 036 * 037 * <p>If any of the operands is of a reference type, unboxing conversion (Section 5.1.8) 038 * is performed. Then:<br> 039 * If either operand is of type {@code BigDecimal}, the other is converted to {@code BigDecimal}.<br> 040 * If either operand is of type double, the other is converted to double.<br> 041 * Otherwise, if either operand is of type float, the other is converted to float.<br> 042 * If either operand is of type {@code BigInteger}, the other is converted to {@code BigInteger}.<br> 043 * Otherwise, if either operand is of type long, the other is converted to long.<br> 044 * Otherwise, both operands are converted to type int. 045 * 046 * @author Andy Clement 047 * @author Juergen Hoeller 048 * @author Sam Brannen 049 * @author Giovanni Dall'Oglio Risso 050 * @since 3.0 051 */ 052public class OpMultiply extends Operator { 053 054 public OpMultiply(int pos, SpelNodeImpl... operands) { 055 super("*", pos, operands); 056 } 057 058 059 /** 060 * Implements the {@code multiply} operator directly here for certain types 061 * of supported operands and otherwise delegates to any registered overloader 062 * for types not supported here. 063 * <p>Supported operand types: 064 * <ul> 065 * <li>numbers 066 * <li>String and int ('abc' * 2 == 'abcabc') 067 * </ul> 068 */ 069 @Override 070 public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { 071 Object leftOperand = getLeftOperand().getValueInternal(state).getValue(); 072 Object rightOperand = getRightOperand().getValueInternal(state).getValue(); 073 074 if (leftOperand instanceof Number && rightOperand instanceof Number) { 075 Number leftNumber = (Number) leftOperand; 076 Number rightNumber = (Number) rightOperand; 077 078 if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) { 079 BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class); 080 BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class); 081 return new TypedValue(leftBigDecimal.multiply(rightBigDecimal)); 082 } 083 else if (leftNumber instanceof Double || rightNumber instanceof Double) { 084 this.exitTypeDescriptor = "D"; 085 return new TypedValue(leftNumber.doubleValue() * rightNumber.doubleValue()); 086 } 087 else if (leftNumber instanceof Float || rightNumber instanceof Float) { 088 this.exitTypeDescriptor = "F"; 089 return new TypedValue(leftNumber.floatValue() * rightNumber.floatValue()); 090 } 091 else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) { 092 BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class); 093 BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class); 094 return new TypedValue(leftBigInteger.multiply(rightBigInteger)); 095 } 096 else if (leftNumber instanceof Long || rightNumber instanceof Long) { 097 this.exitTypeDescriptor = "J"; 098 return new TypedValue(leftNumber.longValue() * rightNumber.longValue()); 099 } 100 else if (CodeFlow.isIntegerForNumericOp(leftNumber) || CodeFlow.isIntegerForNumericOp(rightNumber)) { 101 this.exitTypeDescriptor = "I"; 102 return new TypedValue(leftNumber.intValue() * rightNumber.intValue()); 103 } 104 else { 105 // Unknown Number subtypes -> best guess is double multiplication 106 return new TypedValue(leftNumber.doubleValue() * rightNumber.doubleValue()); 107 } 108 } 109 110 if (leftOperand instanceof String && rightOperand instanceof Integer) { 111 int repeats = (Integer) rightOperand; 112 StringBuilder result = new StringBuilder(); 113 for (int i = 0; i < repeats; i++) { 114 result.append(leftOperand); 115 } 116 return new TypedValue(result.toString()); 117 } 118 119 return state.operate(Operation.MULTIPLY, leftOperand, rightOperand); 120 } 121 122 @Override 123 public boolean isCompilable() { 124 if (!getLeftOperand().isCompilable()) { 125 return false; 126 } 127 if (this.children.length > 1) { 128 if (!getRightOperand().isCompilable()) { 129 return false; 130 } 131 } 132 return (this.exitTypeDescriptor != null); 133 } 134 135 @Override 136 public void generateCode(MethodVisitor mv, CodeFlow cf) { 137 getLeftOperand().generateCode(mv, cf); 138 String leftDesc = getLeftOperand().exitTypeDescriptor; 139 CodeFlow.insertNumericUnboxOrPrimitiveTypeCoercion(mv, leftDesc, this.exitTypeDescriptor.charAt(0)); 140 if (this.children.length > 1) { 141 cf.enterCompilationScope(); 142 getRightOperand().generateCode(mv, cf); 143 String rightDesc = getRightOperand().exitTypeDescriptor; 144 cf.exitCompilationScope(); 145 CodeFlow.insertNumericUnboxOrPrimitiveTypeCoercion(mv, rightDesc, this.exitTypeDescriptor.charAt(0)); 146 switch (this.exitTypeDescriptor.charAt(0)) { 147 case 'I': 148 mv.visitInsn(IMUL); 149 break; 150 case 'J': 151 mv.visitInsn(LMUL); 152 break; 153 case 'F': 154 mv.visitInsn(FMUL); 155 break; 156 case 'D': 157 mv.visitInsn(DMUL); 158 break; 159 default: 160 throw new IllegalStateException( 161 "Unrecognized exit type descriptor: '" + this.exitTypeDescriptor + "'"); 162 } 163 } 164 cf.pushDescriptor(this.exitTypeDescriptor); 165 } 166 167}