/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.gosu.parser.expressions; import gw.config.CommonServices; import gw.internal.gosu.parser.BeanAccess; import gw.internal.gosu.parser.ParseTree; import gw.internal.gosu.parser.ParserBase; import gw.lang.IDimension; import gw.lang.parser.expressions.IAdditiveExpression; import gw.lang.reflect.IPlaceholder; import gw.lang.reflect.IType; import gw.lang.reflect.TypeSystem; import gw.lang.reflect.gs.IGosuClass; import gw.lang.reflect.java.JavaTypes; import java.math.BigDecimal; import java.util.Arrays; /** * Represents an additive expression in the Gosu grammar: * <pre> * <i>additive-expression</i> * <multiplicative-expression> * <additive-expression> <b>+</b> <multiplicative-expression> * <additive-expression> <b>-</b> <multiplicative-expression> * </pre> * <p/> * * @see gw.lang.parser.IGosuParser */ public final class AdditiveExpression extends ArithmeticExpression implements IAdditiveExpression { /** * Returns whether or not the operation is addition or substraction. * * @return True if operation is addition. */ public boolean isAdditive() { return getOperator() != null && getOperator().endsWith( "+" ); } // Tests if this expr is part of an assignment such as: size += 5, where this expr only contains the 5 as a child public boolean isAssignment() { ParseTree loc = getLocation(); return loc != null && loc.getChildCount() < 2; } /** * Evaluates this additive expression. Either performs numeric addition/subtraction or * String concatination. */ public Object evaluate() { if( !isCompileTimeConstant() ) { return super.evaluate(); } return evaluate( getType(), getLHS().evaluate(), getRHS().evaluate(), getLHS().getType(), getRHS().getType(), isAdditive(), false, !getType().equals( JavaTypes.STRING() ) ); } // Potentially called from generated bytecode public static Object evaluate( IType type, Object lhsValue, Object rhsValue, IType lhsType, IType rhsType, boolean bAdditive, boolean bNullSafe, boolean bNumericType, Object ctx, int startLhs, int endLhs, int startRhs, int endRhs ) { if( bNumericType ) { AdditiveExpression.maybeThrowRichNPE( lhsValue, rhsValue, bNullSafe, ctx, startLhs, endLhs, startRhs, endRhs ); } return evaluate( type, lhsValue, rhsValue, lhsType, rhsType, bAdditive, bNullSafe, bNumericType ); } public static void maybeThrowRichNPE( Object lhsValue, Object rhsValue, boolean bNullSafe, Object ctx, int startLhs, int endLhs, int startRhs, int endRhs ) { if( !bNullSafe && ctx instanceof IGosuClass ) { NullPointerException npe = null; try { IGosuClass gsClass = (IGosuClass)ctx; if( lhsValue == null && rhsValue == null ) { CharSequence entireExpr = gsClass.getSource().subSequence( startLhs, endRhs + 1 ); npe = new NullPointerException( "Both sides of the expression \"" + entireExpr + "\" evaluated to null." ); } else if( lhsValue == null ) { CharSequence entireExpr = gsClass.getSource().subSequence( startLhs, endRhs + 1 ); CharSequence lhsCode = gsClass.getSource().subSequence( startLhs, endLhs + 1 ); npe = new NullPointerException( "The expression \"" + lhsCode + "\" evaluated to null within the expression \"" + entireExpr + "\"." ); } else if( rhsValue == null ) { CharSequence entireExpr = gsClass.getSource().subSequence( startLhs, endRhs + 1 ); CharSequence rhsCode = gsClass.getSource().subSequence( startRhs, endRhs + 1 ); npe = new NullPointerException( "The expression \"" + rhsCode + "\" evaluated to null within the expression \"" + entireExpr + "\"." ); } } catch( Exception e ) { //ignore } if( npe != null ) { StackTraceElement[] elts = npe.getStackTrace(); npe.setStackTrace( Arrays.copyOfRange( elts, 2, elts.length ) ); throw npe; } } } public static Object evaluate( IType type, Object lhsValue, Object rhsValue, IType lhsType, IType rhsType, boolean bAdditive, boolean bNullSafe, boolean bNumericType ) { boolean bDynamic = false; if( lhsType instanceof IPlaceholder && ((IPlaceholder)lhsType).isPlaceholder() ) { bDynamic = true; lhsType = TypeSystem.getFromObject( lhsValue ); } if( rhsType instanceof IPlaceholder && ((IPlaceholder)rhsType).isPlaceholder() ) { bDynamic = true; rhsType = TypeSystem.getFromObject( rhsValue ); } if( bDynamic ) { type = ParserBase.resolveRuntimeType( lhsType, bAdditive ? '+' : '-', rhsType ); bNumericType = BeanAccess.isNumericType( type ); } if( bNumericType ) { // Only evaluate as null if this is a numeric expression. // String concatenation should behave like Java regarding null values. 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 dimOperandResolver = DimensionOperandResolver.resolve( type, bAdditive ? '+' : '-', lhsType, lhsValue, rhsType, rhsValue ); type = dimOperandResolver.getRawNumberType(); lhsValue = dimOperandResolver.getLhsValue(); rhsValue = dimOperandResolver.getRhsValue(); IDimension customNumberBase = dimOperandResolver.getBase(); // Add/Subtract values as numbers Object retValue; if( bAdditive ) { if( type == JavaTypes.BIG_DECIMAL() ) { BigDecimal lhsBD = CommonServices.getCoercionManager().makeBigDecimalFrom( lhsValue ); BigDecimal rhsBD = CommonServices.getCoercionManager().makeBigDecimalFrom( rhsValue ); retValue = lhsBD.add( rhsBD ); } else if( type == JavaTypes.BIG_INTEGER() ) { retValue = CommonServices.getCoercionManager().makeBigIntegerFrom( lhsValue ).add( 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); } } else { if( type == JavaTypes.BIG_DECIMAL() ) { BigDecimal lhsBD = CommonServices.getCoercionManager().makeBigDecimalFrom( lhsValue ); BigDecimal rhsBD = CommonServices.getCoercionManager().makeBigDecimalFrom( rhsValue ); retValue = lhsBD.subtract( rhsBD ); } else if( type == JavaTypes.BIG_INTEGER() ) { retValue = CommonServices.getCoercionManager().makeBigIntegerFrom( lhsValue ).subtract( 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 ); } } if( retValue != null ) { if( customNumberBase != null ) { //noinspection unchecked retValue = customNumberBase.fromNumber( (Number)retValue ); } } return retValue; } else { // Concatenate values as strings return CommonServices.getCoercionManager().makeStringFrom( lhsValue ) + CommonServices.getCoercionManager().makeStringFrom( rhsValue ); } } }