/******************************************************************************* * 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 java.math.BigInteger; 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 the EL modulo operator: % or mod * Based JSP.2.3.5.3 * * @author cbateman * */ /*package*/ class ModArithmeticBinaryOperator extends ArithmeticBinaryOperator { private static final String MODULO = "modulo"; //$NON-NLS-1$ ModArithmeticBinaryOperator(DiagnosticFactory diagnosticFactory) { super(diagnosticFactory); } public ValueType performOperation(ValueType firstArg, ValueType secondArg) { // JSP.2.3.5.3, step 1 if both null, then return zero if (TypeCoercer.typeIsNull(firstArg.getSignature()) && TypeCoercer.typeIsNull(secondArg.getSignature())) { return IntegerLiteralType.ZERO; } final String boxedFirstArg = TypeTransformer.transformBoxPrimitives(firstArg.getSignature()); final String boxedSecondArg = TypeTransformer.transformBoxPrimitives(secondArg.getSignature()); // JSP.2.3.5.3, step 2, if either arg is BigDecimal, Float, Double // or String (ignoring whether it is value coercable), then coerce // to Double and do op if (TypeConstants.TYPE_BIG_DOUBLE.equals(boxedFirstArg) || TypeConstants.TYPE_BIG_DOUBLE.equals(boxedSecondArg) || TypeConstants.TYPE_BOXED_DOUBLE.equals(boxedFirstArg) || TypeConstants.TYPE_BOXED_DOUBLE.equals(boxedSecondArg) || TypeConstants.TYPE_BOXED_FLOAT.equals(boxedFirstArg) || TypeConstants.TYPE_BOXED_FLOAT.equals(boxedSecondArg)) { // TODO: handle case where one is a literal or resolvable string value // that containss ".", "e" or "E" return performDouble(firstArg, secondArg); } // JSP.2.3.5.3, step 3, if either arg is a BigInteger, coerce // both to BigInteger if (TypeConstants.TYPE_BIG_INTEGER.equals(boxedFirstArg) || TypeConstants.TYPE_BIG_INTEGER.equals(boxedSecondArg)) { return performBigInteger(firstArg, secondArg); } // JSP.2.3.5.3, step 4, otherwise try to perform as a Long op return performLong(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.3, step 1 if both null, then return zero if (TypeCoercer.typeIsNull(firstArg.getSignature()) && TypeCoercer.typeIsNull(secondArg.getSignature())) { return _diagnosticFactory.create_BINARY_OP_BOTH_OPERANDS_NULL(MODULO); } final String boxedFirstArg = TypeTransformer.transformBoxPrimitives(firstArg.getSignature()); final String boxedSecondArg = TypeTransformer.transformBoxPrimitives(secondArg.getSignature()); // JSP.2.3.5.3, step 2, if either arg is BigDecimal, Float, Double // or String (ignoring whether it is value coercable), then coerce // to Double and do op if (TypeConstants.TYPE_BIG_DOUBLE.equals(boxedFirstArg) || TypeConstants.TYPE_BIG_DOUBLE.equals(boxedSecondArg) || TypeConstants.TYPE_BOXED_DOUBLE.equals(boxedFirstArg) || TypeConstants.TYPE_BOXED_DOUBLE.equals(boxedSecondArg) || TypeConstants.TYPE_BOXED_FLOAT.equals(boxedFirstArg) || TypeConstants.TYPE_BOXED_FLOAT.equals(boxedSecondArg)) { // TODO: handle case where one is a literal or resolvable string value // that containss ".", "e" or "E" return validateDouble(firstArg, secondArg); } // JSP.2.3.5.3, step 3, if either arg is a BigInteger, coerce // both to BigInteger if (TypeConstants.TYPE_BIG_INTEGER.equals(boxedFirstArg) || TypeConstants.TYPE_BIG_INTEGER.equals(boxedSecondArg)) { return validateBigInteger(firstArg, secondArg); } // JSP.2.3.5.3, step 4, otherwise try to perform as a Long op return validateLong(firstArg, secondArg); } 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 (secondValue.doubleValue() == 0.0) { // division by zero 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 we get to here, the coercion is valid, so a Double will be // returned return new ValueType(Signature.SIG_DOUBLE, IAssignable.ASSIGNMENT_TYPE_RHS); } catch (TypeCoercionException tce) { // could not coerce, so null return null; } } private ValueType performBigInteger(ValueType firstArg, ValueType secondArg) { try { TypeCoercer.coerceToNumber(TypeTransformer.transformBoxPrimitives(firstArg.getSignature())); TypeCoercer.coerceToNumber(TypeTransformer.transformBoxPrimitives(secondArg.getSignature())); // check second arg for zero if (secondArg instanceof LiteralType) { if (((LiteralType)secondArg).coerceToNumber(BigInteger.class).equals(BigInteger.ZERO)) { // division by zero return null; } } // since one of the args is BigInteger, they are not both literals, // so if we get to here, we have a successful mod of two // big integers to one big integer return new ValueType(TypeConstants.TYPE_BIG_INTEGER, IAssignable.ASSIGNMENT_TYPE_RHS); } catch (TypeCoercionException tce) { // no coercion return null; } } private ValueType performLong(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(Long.class); if (secondValue.longValue() == 0) { // division by zero return null; } } Number firstValue = null; if (firstArg instanceof LiteralType) { firstValue = ((LiteralType)firstArg).coerceToNumber(Long.class); } if (firstValue != null && secondValue != null) { return new IntegerLiteralType( doRealOperation(Long.valueOf(firstValue.longValue()), Long.valueOf(secondValue.longValue())).longValue()); } // if we get to here, the coercion is valid, so a Long will be // returned return new ValueType(Signature.SIG_LONG, IAssignable.ASSIGNMENT_TYPE_RHS); } catch (TypeCoercionException tce) { // could not coerce, so null return null; } } 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 (secondValue.doubleValue() == 0.0) { // division by zero 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) { return _diagnosticFactory. create_BINARY_OP_CONSTANT_EXPRESSION_ALWAYS_EVAL_SAME (MODULO, Double.toString( firstValue.doubleValue()%secondValue.doubleValue())); } // if we get to here, the coercion is valid, so a Double will be // returned and everything is good return Diagnostic.OK_INSTANCE; } catch (TypeCoercionException tce) { // could not coerce, so error return _diagnosticFactory. create_BINARY_OP_COULD_NOT_MAKE_NUMERIC_COERCION(MODULO); } } private Diagnostic validateBigInteger(ValueType firstArg, ValueType secondArg) { try { TypeCoercer.coerceToNumber(TypeTransformer.transformBoxPrimitives(firstArg.getSignature())); TypeCoercer.coerceToNumber(TypeTransformer.transformBoxPrimitives(secondArg.getSignature())); // check second arg for zero if (secondArg instanceof LiteralType) { if (((LiteralType)secondArg).coerceToNumber(BigInteger.class).equals(BigInteger.ZERO)) { // division by zero return _diagnosticFactory.create_BINARY_OP_POSSIBLE_DIVISION_BY_ZERO(); } } // since one of the args is BigInteger, they are not both literals, // so if we get to here, we have a successful mod of two // big integers to one big integer return Diagnostic.OK_INSTANCE; } catch (TypeCoercionException tce) { // no coercion return _diagnosticFactory. create_BINARY_OP_COULD_NOT_MAKE_NUMERIC_COERCION(MODULO); } } private Diagnostic validateLong(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(Long.class); if (secondValue.longValue() == 0) { // division by zero return _diagnosticFactory. create_BINARY_OP_POSSIBLE_DIVISION_BY_ZERO(); } } Number firstValue = null; if (firstArg instanceof LiteralType) { firstValue = ((LiteralType)firstArg).coerceToNumber(Long.class); } if (firstValue != null && secondValue != null) { return _diagnosticFactory. create_BINARY_OP_CONSTANT_EXPRESSION_ALWAYS_EVAL_SAME (MODULO, Long.toString(firstValue.longValue()%secondValue.longValue())); } // if we get to here, the coercion is valid, so a Long will be // returned return Diagnostic.OK_INSTANCE; } catch (TypeCoercionException tce) { // could not coerce, so error return _diagnosticFactory. create_BINARY_OP_COULD_NOT_MAKE_NUMERIC_COERCION(MODULO); } } protected Long doRealOperation(Long firstArg, Long secondArg) { return Long.valueOf(firstArg.longValue() % secondArg.longValue()); } protected Double doRealOperation(Double firstArg, Double secondArg) { return new Double(firstArg.doubleValue() % secondArg.doubleValue()); } protected BigDecimal doRealOperation(BigDecimal firstArg, BigDecimal secondArg) { return new BigDecimal(firstArg.doubleValue() % secondArg.doubleValue()); } protected String getOperatorName() { return MODULO; } }