| /******************************************************************************* |
| * Copyright (c) 2006 Oracle Corporation. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Cameron Bateman/Oracle - initial API and implementation |
| * |
| ********************************************************************************/ |
| |
| package org.eclipse.jst.jsf.validation.internal.el.operators; |
| |
| import java.math.BigDecimal; |
| |
| import org.eclipse.emf.common.util.Diagnostic; |
| import org.eclipse.jdt.core.Signature; |
| import org.eclipse.jst.jsf.common.internal.types.FloatLiteralType; |
| import org.eclipse.jst.jsf.common.internal.types.IAssignable; |
| import org.eclipse.jst.jsf.common.internal.types.IntegerLiteralType; |
| import org.eclipse.jst.jsf.common.internal.types.LiteralType; |
| import org.eclipse.jst.jsf.common.internal.types.TypeCoercer; |
| import org.eclipse.jst.jsf.common.internal.types.TypeCoercionException; |
| import org.eclipse.jst.jsf.common.internal.types.TypeConstants; |
| import org.eclipse.jst.jsf.common.internal.types.TypeTransformer; |
| import org.eclipse.jst.jsf.common.internal.types.ValueType; |
| import org.eclipse.jst.jsf.validation.internal.el.diagnostics.DiagnosticFactory; |
| |
| /** |
| * Represents dividing EL binary operators: div and / (same operator) |
| * Based on JSP.2.3.5.2 |
| * |
| * @author cbateman |
| * |
| */ |
| /*package*/ class DivArithmeticBinaryOperator extends ArithmeticBinaryOperator |
| { |
| |
| private static final String DIVISION = "division"; //$NON-NLS-1$ |
| |
| DivArithmeticBinaryOperator(DiagnosticFactory diagnosticFactory) { |
| super(diagnosticFactory); |
| // TODO Auto-generated constructor stub |
| } |
| |
| public ValueType performOperation(ValueType firstArg, ValueType secondArg) |
| { |
| // JSP.2.3.5.2, step one: if both null then always 0 |
| if (TypeCoercer.typeIsNull(firstArg.getSignature()) |
| && TypeCoercer.typeIsNull(secondArg.getSignature())) |
| { |
| return new IntegerLiteralType(0); |
| } |
| |
| final String boxedFirstArg = TypeTransformer.transformBoxPrimitives(firstArg.getSignature()); |
| final String boxedSecondArg = TypeTransformer.transformBoxPrimitives(secondArg.getSignature()); |
| |
| // JSP.2.3.5.2, step 2: if one arg is BigInteger or BigDecimal |
| // then coerce to BigDecimal and do div |
| if (TypeConstants.TYPE_BIG_DOUBLE.equals(boxedFirstArg) |
| || TypeConstants.TYPE_BIG_DOUBLE.equals(boxedSecondArg) |
| || TypeConstants.TYPE_BIG_INTEGER.equals(boxedFirstArg) |
| || TypeConstants.TYPE_BIG_INTEGER.equals(boxedSecondArg)) |
| { |
| return performBigDecimal(firstArg, secondArg); |
| } |
| |
| return performDouble(firstArg, secondArg); |
| } |
| |
| public Diagnostic validate(ValueType firstArg, ValueType secondArg) { |
| if (TypeConstants.TYPE_JAVAOBJECT.equals(firstArg.getSignature()) || |
| TypeConstants.TYPE_JAVAOBJECT.equals(secondArg.getSignature())) { |
| return Diagnostic.OK_INSTANCE; |
| } |
| |
| // JSP.2.3.5.2, step one: if both null then always 0 |
| if (TypeCoercer.typeIsNull(firstArg.getSignature()) |
| && TypeCoercer.typeIsNull(secondArg.getSignature())) |
| { |
| return _diagnosticFactory.create_BINARY_OP_BOTH_OPERANDS_NULL(DIVISION); |
| } |
| |
| final String boxedFirstArg = TypeTransformer.transformBoxPrimitives(firstArg.getSignature()); |
| final String boxedSecondArg = TypeTransformer.transformBoxPrimitives(secondArg.getSignature()); |
| |
| // JSP.2.3.5.2, step 2: if one arg is BigInteger or BigDecimal |
| // then coerce to BigDecimal and do div |
| if (TypeConstants.TYPE_BIG_DOUBLE.equals(boxedFirstArg) |
| || TypeConstants.TYPE_BIG_DOUBLE.equals(boxedSecondArg) |
| || TypeConstants.TYPE_BIG_INTEGER.equals(boxedFirstArg) |
| || TypeConstants.TYPE_BIG_INTEGER.equals(boxedSecondArg)) |
| { |
| return validateBigDecimal(firstArg, secondArg); |
| } |
| |
| return validateDouble(firstArg, secondArg); |
| } |
| |
| private ValueType performBigDecimal(ValueType firstArg, ValueType secondArg) |
| { |
| // since one or the other args must be either big decimal or big int, |
| // we don't have two literals, so it is sufficient to ensure that we can |
| // coerce both to numbers and check for div by zero and div of zero |
| try |
| { |
| TypeCoercer.coerceToNumber(TypeTransformer.transformBoxPrimitives(firstArg.getSignature())); |
| TypeCoercer.coerceToNumber(TypeTransformer.transformBoxPrimitives(secondArg.getSignature())); |
| |
| // if we get to here, the result is always BigDecimal unless we have |
| // a div by zero |
| if (secondArg instanceof LiteralType) |
| { |
| final Number coercedValue = ((LiteralType)secondArg).coerceToNumber(BigDecimal.class); |
| if (((BigDecimal)coercedValue).equals(new BigDecimal(0))) |
| { |
| return null; |
| } |
| } |
| |
| return new ValueType(TypeConstants.TYPE_BIG_DOUBLE, IAssignable.ASSIGNMENT_TYPE_RHS); |
| } |
| catch (TypeCoercionException ce) |
| { |
| return null; |
| } |
| } |
| |
| private ValueType performDouble(ValueType firstArg, ValueType secondArg) |
| { |
| try |
| { |
| TypeCoercer.coerceToNumber(TypeTransformer.transformBoxPrimitives(firstArg.getSignature())); |
| TypeCoercer.coerceToNumber(TypeTransformer.transformBoxPrimitives(secondArg.getSignature())); |
| |
| Number secondValue = null; |
| if (secondArg instanceof LiteralType) |
| { |
| secondValue = ((LiteralType)secondArg).coerceToNumber(Double.class); |
| // if the second value is definitely 0, then return null since |
| // we have a div by zero |
| if (secondValue.doubleValue() == 0.0) |
| { |
| return null; |
| } |
| } |
| |
| Number firstValue = null; |
| |
| if (firstArg instanceof LiteralType) |
| { |
| firstValue = ((LiteralType)firstArg).coerceToNumber(Double.class); |
| } |
| |
| if (firstValue != null && secondValue != null) |
| { |
| return new FloatLiteralType( |
| doRealOperation(new Double(firstValue.doubleValue()) |
| , new Double(secondValue.doubleValue())).doubleValue()); |
| } |
| |
| // if not both literals and could coerce, then the type is double |
| return new ValueType(Signature.SIG_DOUBLE, IAssignable.ASSIGNMENT_TYPE_RHS); |
| } |
| catch (TypeCoercionException ce) |
| { |
| // could not coerce for the operation |
| return null; |
| } |
| } |
| |
| private Diagnostic validateBigDecimal(ValueType firstArg, ValueType secondArg) |
| { |
| // since one or the other args must be either big decimal or big int, |
| // we don't have two literals, so it is sufficient to ensure that we can |
| // coerce both to numbers and check for div by zero and div of zero |
| try |
| { |
| TypeCoercer.coerceToNumber(TypeTransformer.transformBoxPrimitives(firstArg.getSignature())); |
| TypeCoercer.coerceToNumber(TypeTransformer.transformBoxPrimitives(secondArg.getSignature())); |
| |
| // if we get to here, the result is always BigDecimal unless we have |
| // a div by zero |
| if (secondArg instanceof LiteralType) |
| { |
| final Number coercedValue = ((LiteralType)secondArg).coerceToNumber(BigDecimal.class); |
| if (((BigDecimal)coercedValue).equals(new BigDecimal(0))) |
| { |
| return _diagnosticFactory.create_BINARY_OP_POSSIBLE_DIVISION_BY_ZERO(); |
| } |
| } |
| |
| // everything's okay if we get here |
| return Diagnostic.OK_INSTANCE; |
| } |
| catch (TypeCoercionException ce) |
| { |
| return _diagnosticFactory.create_BINARY_OP_COULD_NOT_MAKE_NUMERIC_COERCION(DIVISION); |
| } |
| } |
| |
| private Diagnostic validateDouble(ValueType firstArg, ValueType secondArg) |
| { |
| try |
| { |
| TypeCoercer.coerceToNumber(TypeTransformer.transformBoxPrimitives(firstArg.getSignature())); |
| TypeCoercer.coerceToNumber(TypeTransformer.transformBoxPrimitives(secondArg.getSignature())); |
| |
| Number secondValue = null; |
| if (secondArg instanceof LiteralType) |
| { |
| secondValue = ((LiteralType)secondArg).coerceToNumber(Double.class); |
| // if the second value is definitely 0, then return null since |
| // we have a div by zero |
| if (secondValue.doubleValue() == 0.0) |
| { |
| return _diagnosticFactory.create_BINARY_OP_POSSIBLE_DIVISION_BY_ZERO(); |
| } |
| } |
| |
| Number firstValue = null; |
| |
| if (firstArg instanceof LiteralType) |
| { |
| firstValue = ((LiteralType)firstArg).coerceToNumber(Double.class); |
| } |
| |
| if (firstValue != null && secondValue != null) |
| { |
| String result |
| = Double.toString( |
| doRealOperation(new Double(firstValue.doubleValue()), |
| new Double(secondValue.doubleValue())).doubleValue()); |
| |
| return _diagnosticFactory.create_BINARY_OP_CONSTANT_EXPRESSION_ALWAYS_EVAL_SAME(DIVISION, result); |
| } |
| |
| // if not both literals and could coerce, then the type is double |
| return Diagnostic.OK_INSTANCE; |
| } |
| catch (TypeCoercionException ce) |
| { |
| // could not coerce for the operation |
| return _diagnosticFactory.create_BINARY_OP_COULD_NOT_MAKE_NUMERIC_COERCION(DIVISION); |
| } |
| } |
| |
| protected BigDecimal doRealOperation(BigDecimal firstArg, BigDecimal secondArg) { |
| // per JSP.2.3.5.2, step 2 |
| return firstArg.divide(secondArg, BigDecimal.ROUND_HALF_UP); |
| } |
| |
| protected Double doRealOperation(Double firstArg, Double secondArg) |
| { |
| return new Double(firstArg.doubleValue() / secondArg.doubleValue()); |
| } |
| |
| protected Long doRealOperation(Long firstArg, Long secondArg) { |
| return Long.valueOf(firstArg.longValue() / secondArg.longValue()); |
| } |
| |
| protected String getOperatorName() { |
| return DIVISION; |
| } |
| } |