blob: 67242ec601a373c2100c4b902a646535d49140b9 [file] [log] [blame]
/*******************************************************************************
* 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;
}
}