/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.parser.expressions;
import gw.config.CommonServices;
import gw.internal.gosu.parser.ParserBase;
import gw.lang.IDimension;
import gw.lang.parser.expressions.IMultiplicativeExpression;
import gw.lang.reflect.IPlaceholder;
import gw.lang.reflect.IType;
import gw.lang.reflect.TypeSystem;
import gw.lang.reflect.java.JavaTypes;
import java.math.BigDecimal;
import java.math.MathContext;
/**
* Represents a multiplicative expression in the Gosu grammar:
* <pre>
* <i>multiplicative-expression</i>
* <unary-expression>
* <multiplicative-expression> <b>*</b> <unary-expression>
* <multiplicative-expression> <b>/</b> <unary-expression>
* <multiplicative-expression> <b>%</b> <unary-expression>
* </pre>
* <p/>
*
* @see gw.lang.parser.IGosuParser
*/
public final class MultiplicativeExpression extends ArithmeticExpression implements IMultiplicativeExpression
{
/**
* Evaluate the expression.
*/
public Object evaluate()
{
if( !isCompileTimeConstant() )
{
return super.evaluate();
}
return evaluate( getType(), getLHS().evaluate(), getRHS().evaluate(), getLHS().getType(), getRHS().getType(), getOperator().charAt( 0 ), false );
}
public static Object evaluate( IType type, Object lhsValue, Object rhsValue, IType lhsType, IType rhsType, int iOperator, boolean bNullSafe, Object ctx, int startLhs, int endLhs, int startRhs, int endRhs )
{
AdditiveExpression.maybeThrowRichNPE( lhsValue, rhsValue, bNullSafe, ctx, startLhs, endLhs, startRhs, endRhs );
return evaluate( type, lhsValue, rhsValue, lhsType, rhsType, iOperator, bNullSafe );
}
// Potentially called from generated bytecode
@SuppressWarnings({"ConstantConditions"})
public static Object evaluate( IType type, Object lhsValue, Object rhsValue, IType lhsType, IType rhsType, int iOperator, boolean bNullSafe )
{
if( lhsType instanceof IPlaceholder && ((IPlaceholder)lhsType).isPlaceholder() )
{
lhsType = TypeSystem.getFromObject( lhsValue );
type = ParserBase.resolveRuntimeType( lhsType, iOperator, rhsType );
}
if( rhsType instanceof IPlaceholder && ((IPlaceholder)rhsType).isPlaceholder() )
{
rhsType = TypeSystem.getFromObject( rhsValue );
type = ParserBase.resolveRuntimeType( lhsType, iOperator, rhsType );
}
if( lhsValue == null )
{
if( bNullSafe )
{
return null;
}
throw new NullPointerException("left-hand operand was null");
}
if( rhsValue == null )
{
if( bNullSafe )
{
return null;
}
throw new NullPointerException("right-hand operand was null");
}
DimensionOperandResolver customNumberResolver =
DimensionOperandResolver.resolve( type, (char)iOperator, lhsType, lhsValue, rhsType, rhsValue );
type = customNumberResolver.getRawNumberType();
lhsValue = customNumberResolver.getLhsValue();
rhsValue = customNumberResolver.getRhsValue();
IDimension customNumberBase = customNumberResolver.getBase();
Object retValue;
switch( iOperator )
{
case '*':
{
if( type == JavaTypes.BIG_DECIMAL() )
{
BigDecimal lhsBD = CommonServices.getCoercionManager().makeBigDecimalFrom( lhsValue );
BigDecimal rhsBD = CommonServices.getCoercionManager().makeBigDecimalFrom( rhsValue );
retValue = lhsBD.multiply( rhsBD );
}
else if( type == JavaTypes.BIG_INTEGER() )
{
retValue = CommonServices.getCoercionManager().makeBigIntegerFrom( lhsValue ).multiply( CommonServices.getCoercionManager().makeBigIntegerFrom( rhsValue ) );
}
else if( type == JavaTypes.INTEGER() || type == JavaTypes.pINT() )
{
retValue = CommonServices.getCoercionManager().makeIntegerFrom( lhsValue ) * CommonServices.getCoercionManager().makeIntegerFrom( rhsValue );
}
else if( type == JavaTypes.LONG() || type == JavaTypes.pLONG() )
{
retValue = makeLong( CommonServices.getCoercionManager().makeLongFrom( lhsValue ) * CommonServices.getCoercionManager().makeLongFrom( rhsValue ) );
}
else if( type == JavaTypes.DOUBLE() || type == JavaTypes.pDOUBLE() )
{
retValue = makeDoubleValue( makeDoubleValue( lhsValue ) * makeDoubleValue( rhsValue ) );
}
else if( type == JavaTypes.FLOAT() || type == JavaTypes.pFLOAT() )
{
retValue = makeFloatValue( makeFloatValue( lhsValue ) * makeFloatValue( rhsValue ) );
}
else if( type == JavaTypes.SHORT() || type == JavaTypes.pSHORT() )
{
retValue = Integer.valueOf( CommonServices.getCoercionManager().makeIntegerFrom( lhsValue ) * CommonServices.getCoercionManager().makeIntegerFrom( rhsValue ) ).shortValue();
}
else if( type == JavaTypes.BYTE() || type == JavaTypes.pBYTE() )
{
retValue = (byte)(CommonServices.getCoercionManager().makeIntegerFrom( lhsValue ) * CommonServices.getCoercionManager().makeIntegerFrom( rhsValue ));
}
else
{
throw new UnsupportedNumberTypeException(type);
}
break;
}
case '/':
{
if( type == JavaTypes.BIG_DECIMAL() )
{
BigDecimal lhsBD = CommonServices.getCoercionManager().makeBigDecimalFrom( lhsValue );
BigDecimal rhsBD = CommonServices.getCoercionManager().makeBigDecimalFrom( rhsValue );
retValue = lhsBD.divide( rhsBD, MathContext.DECIMAL128 );
}
else if( type == JavaTypes.BIG_INTEGER() )
{
retValue = CommonServices.getCoercionManager().makeBigIntegerFrom( lhsValue ).divide( CommonServices.getCoercionManager().makeBigIntegerFrom( rhsValue ) );
}
else if( type == JavaTypes.INTEGER() || type == JavaTypes.pINT() )
{
retValue = CommonServices.getCoercionManager().makeIntegerFrom( lhsValue ) / CommonServices.getCoercionManager().makeIntegerFrom( rhsValue );
}
else if( type == JavaTypes.LONG() || type == JavaTypes.pLONG() )
{
retValue = makeLong( CommonServices.getCoercionManager().makeLongFrom( lhsValue ) / CommonServices.getCoercionManager().makeLongFrom( rhsValue ) );
}
else if( type == JavaTypes.DOUBLE() || type == JavaTypes.pDOUBLE() )
{
retValue = makeDoubleValue( makeDoubleValue( lhsValue ) / makeDoubleValue( rhsValue ) );
}
else if( type == JavaTypes.FLOAT() || type == JavaTypes.pFLOAT() )
{
retValue = makeFloatValue( makeFloatValue( lhsValue ) / makeFloatValue( rhsValue ) );
}
else if( type == JavaTypes.SHORT() || type == JavaTypes.pSHORT() )
{
retValue = Integer.valueOf( CommonServices.getCoercionManager().makeIntegerFrom( lhsValue ) / CommonServices.getCoercionManager().makeIntegerFrom( rhsValue ) ).shortValue();
}
else if( type == JavaTypes.BYTE() || type == JavaTypes.pBYTE() )
{
retValue = (byte)(CommonServices.getCoercionManager().makeIntegerFrom( lhsValue ) / CommonServices.getCoercionManager().makeIntegerFrom( rhsValue ));
}
else
{
throw new UnsupportedNumberTypeException(type);
}
break;
}
case '%':
{
if( type == JavaTypes.BIG_DECIMAL() )
{
BigDecimal lhsBD = CommonServices.getCoercionManager().makeBigDecimalFrom( lhsValue );
BigDecimal rhsBD = CommonServices.getCoercionManager().makeBigDecimalFrom( rhsValue );
BigDecimal result = lhsBD.remainder( rhsBD, MathContext.DECIMAL128 );
retValue = result.abs();
}
else if( type == JavaTypes.BIG_INTEGER() )
{
retValue = CommonServices.getCoercionManager().makeBigIntegerFrom( lhsValue ).mod( CommonServices.getCoercionManager().makeBigIntegerFrom( rhsValue ) );
}
else if( type == JavaTypes.INTEGER() || type == JavaTypes.pINT() )
{
retValue = CommonServices.getCoercionManager().makeIntegerFrom( lhsValue ) % CommonServices.getCoercionManager().makeIntegerFrom( rhsValue );
}
else if( type == JavaTypes.LONG() || type == JavaTypes.pLONG() )
{
retValue = makeLong( CommonServices.getCoercionManager().makeLongFrom( lhsValue ) % CommonServices.getCoercionManager().makeLongFrom( rhsValue ) );
}
else if( type == JavaTypes.DOUBLE() || type == JavaTypes.pDOUBLE() )
{
retValue = makeDoubleValue( makeDoubleValue( lhsValue ) % makeDoubleValue( rhsValue ) );
}
else if( type == JavaTypes.FLOAT() || type == JavaTypes.pFLOAT() )
{
retValue = makeFloatValue( makeFloatValue( lhsValue ) % makeFloatValue( rhsValue ) );
}
else if( type == JavaTypes.SHORT() || type == JavaTypes.pSHORT() )
{
retValue = Integer.valueOf( CommonServices.getCoercionManager().makeIntegerFrom( lhsValue ) % CommonServices.getCoercionManager().makeIntegerFrom( rhsValue ) ).shortValue();
}
else if( type == JavaTypes.BYTE() || type == JavaTypes.pBYTE() )
{
retValue = (byte)(CommonServices.getCoercionManager().makeIntegerFrom( lhsValue ) % CommonServices.getCoercionManager().makeIntegerFrom( rhsValue ));
}
else
{
throw new UnsupportedNumberTypeException(type);
}
break;
}
default:
retValue = null;
}
if( retValue != null )
{
if( customNumberBase != null )
{
//noinspection unchecked
retValue = customNumberBase.fromNumber( (Number)retValue );
}
}
return retValue;
}
}