/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.gosu.ir.transform.expression; import gw.internal.gosu.parser.BeanAccess; import gw.internal.gosu.parser.ParserBase; import gw.internal.gosu.parser.expressions.RelationalExpression; import gw.internal.gosu.parser.expressions.ConditionalExpression; import gw.internal.gosu.ir.transform.ExpressionTransformer; import gw.internal.gosu.ir.transform.TopLevelTransformationContext; import gw.lang.ir.IRExpression; import gw.lang.ir.IRSymbol; import gw.lang.ir.expression.IRConditionalAndExpression; import gw.lang.ir.expression.IRRelationalExpression; import gw.lang.ir.statement.IRAssignmentStatement; import gw.lang.parser.GosuParserTypes; import gw.lang.parser.ICoercionManager; import gw.lang.reflect.IType; import gw.lang.reflect.java.JavaTypes; import gw.config.CommonServices; import java.util.Collections; import java.util.Date; import java.util.ArrayList; import java.util.List; /** */ public class RelationalExpressionTransformer extends AbstractExpressionTransformer<RelationalExpression> { public static IRExpression compile( TopLevelTransformationContext cc, RelationalExpression expr ) { RelationalExpressionTransformer gen = new RelationalExpressionTransformer( cc, expr ); return gen.compile(); } private RelationalExpressionTransformer( TopLevelTransformationContext cc, RelationalExpression expr ) { super( cc, expr ); } protected IRExpression compile_impl() { IType lhsType = _expr().getLHS().getType(); IType rhsType = _expr().getRHS().getType(); if( lhsType.isPrimitive() && rhsType.isPrimitive() ) { return comparePrimitives(); } else if( lhsType == rhsType && JavaTypes.COMPARABLE().isAssignableFrom( lhsType ) ) { return compareWithCompareTo(); } else { return compareDynamically(); } } private IRExpression compareWithCompareTo() { // Generates following code: // Comparable lhs = <lhs-expr> // Comparable rhs = <rhs-expr> // (lhs != null && rhs != null && lhs.compareTo( rhs ) [>, <, >=, <=] 0) IRSymbol lhsTemp = _cc().makeAndIndexTempSymbol( getDescriptor( JavaTypes.COMPARABLE() ) ); IRAssignmentStatement tempLhsAssignment = buildAssignment( lhsTemp, ExpressionTransformer.compile( _expr().getLHS(), _cc() ) ); IRSymbol rhsTemp = _cc().makeAndIndexTempSymbol( getDescriptor( JavaTypes.COMPARABLE() ) ); IRAssignmentStatement tempRhsAssignment = buildAssignment( rhsTemp, ExpressionTransformer.compile( _expr().getRHS(), _cc() ) ); IRExpression callCompareTo = callMethod( Comparable.class, "compareTo", new Class[]{Object.class}, identifier( lhsTemp ), Collections.singletonList( (IRExpression)identifier( rhsTemp ) ) ); IRExpression theExpr = new IRConditionalAndExpression( buildNotEquals( identifier( lhsTemp ), nullLiteral() ), new IRConditionalAndExpression( buildNotEquals( identifier( rhsTemp ), nullLiteral() ), new IRRelationalExpression( callCompareTo, pushConstant( 0 ), IRRelationalExpression.Operation.get( _expr().getOperator() ) ) ) ); return buildComposite( tempLhsAssignment, tempRhsAssignment, theExpr ); } private IRExpression comparePrimitives() { // Get the upper bound type IType lhsType = _expr().getLHS().getType(); IType rhsType = _expr().getRHS().getType(); IType type = ParserBase.resolveType( lhsType, '>', rhsType ); IRExpression lhs = numberConvert( _expr().getLHS().getType(), type, ExpressionTransformer.compile( _expr().getLHS(), _cc() ) ); IRExpression rhs = numberConvert( _expr().getRHS().getType(), type, ExpressionTransformer.compile( _expr().getRHS(), _cc() ) ); IRRelationalExpression.Operation op; if( _expr().getOperator().equals( ">" ) ) { op = IRRelationalExpression.Operation.GT; } else if (_expr().getOperator().equals( ">=" )) { op = IRRelationalExpression.Operation.GTE; } else if (_expr().getOperator().equals( "<" )) { op = IRRelationalExpression.Operation.LT; } else if (_expr().getOperator().equals( "<=" )) { op = IRRelationalExpression.Operation.LTE; } else { throw new IllegalArgumentException( "Unrecognized relational operation " + _expr().getOperator() ); } return new IRRelationalExpression(lhs, rhs, op); } private IRExpression compareDynamically() { List<IRExpression> args = new ArrayList<IRExpression>(); // Push the LHS expression value and make sure it's boxed for the method call args.add( boxValue( _expr().getLHS().getType(), ExpressionTransformer.compile( _expr().getLHS(), _cc() ) ) ); // Push the LHS Type args.add( pushType( _expr().getLHS().getType() ) ); // Push operator args.add( pushConstant( _expr().getOperator() ) ); // Push the RHS expression value and make sure it's boxed for the method call args.add( boxValue( _expr().getRHS().getType(), ExpressionTransformer.compile( _expr().getRHS(), _cc() ) ) ); // Push the RHS Type args.add( pushType( _expr().getRHS().getType() ) ); // Call into Gosu's runtime for the Boolean answer return callStaticMethod( RelationalExpressionTransformer.class, "evaluate", new Class[]{Object.class, IType.class, String.class, Object.class, IType.class}, args); } public static boolean evaluate( Object lhsValue, IType lhsType, String strOperator, Object rhsValue, IType rhsType ) { if( lhsValue == null ) { return false; } if( rhsValue == null ) { return false; } final ICoercionManager coercionMgr = CommonServices.getCoercionManager(); if( strOperator.equals( ">" ) ) { if( BeanAccess.isNumericType( lhsType ) ) { return ConditionalExpression.compareNumbers( lhsValue, rhsValue, lhsType, rhsType ) > 0 ? Boolean.TRUE : Boolean.FALSE; } else if( lhsType == GosuParserTypes.DATETIME_TYPE() ) { return coercionMgr.makeDateFrom( lhsValue ).after( coercionMgr.makeDateFrom( rhsValue ) ) ? Boolean.TRUE : Boolean.FALSE; } else { if( BeanAccess.isBeanType( lhsType ) ) { if( BeanAccess.isBeanType( rhsType ) ) { if( lhsType.isAssignableFrom( rhsType ) ) { if( JavaTypes.COMPARABLE().isAssignableFrom( lhsType ) ) { //noinspection unchecked return ((Comparable)lhsValue).compareTo( rhsValue ) > 0 ? Boolean.TRUE : Boolean.FALSE; } } } } } return coercionMgr.makeStringFrom( lhsValue ).compareTo( coercionMgr.makeStringFrom( rhsValue ) ) > 0 ? Boolean.TRUE : Boolean.FALSE; } else if( strOperator.equals( "<" ) ) { if( BeanAccess.isNumericType( lhsType ) ) { return ConditionalExpression.compareNumbers( lhsValue, rhsValue, lhsType, rhsType ) < 0 ? Boolean.TRUE : Boolean.FALSE; } else if( lhsType == GosuParserTypes.DATETIME_TYPE() ) { return coercionMgr.makeDateFrom( lhsValue ).before( coercionMgr.makeDateFrom( rhsValue ) ) ? Boolean.TRUE : Boolean.FALSE; } else { if( BeanAccess.isBeanType( lhsType ) ) { if( BeanAccess.isBeanType( rhsType ) ) { if( lhsType.isAssignableFrom( rhsType ) ) { if( JavaTypes.COMPARABLE().isAssignableFrom( lhsType ) ) { //noinspection unchecked return ((Comparable)lhsValue).compareTo( rhsValue ) < 0 ? Boolean.TRUE : Boolean.FALSE; } } } } } return coercionMgr.makeStringFrom( lhsValue ).compareTo( coercionMgr.makeStringFrom( rhsValue ) ) < 0 ? Boolean.TRUE : Boolean.FALSE; } else if( strOperator.equals( ">=" ) ) { if( BeanAccess.isNumericType( lhsType ) ) { return ConditionalExpression.compareNumbers( lhsValue, rhsValue, lhsType, rhsType ) >= 0 ? Boolean.TRUE : Boolean.FALSE; } else if( lhsType == GosuParserTypes.DATETIME_TYPE() ) { Date l = coercionMgr.makeDateFrom( lhsValue ); Date r = coercionMgr.makeDateFrom( rhsValue ); return (l.compareTo( r ) >= 0) ? Boolean.TRUE : Boolean.FALSE; } else { if( BeanAccess.isBeanType( lhsType ) ) { if( BeanAccess.isBeanType( rhsType ) ) { if( lhsType.isAssignableFrom( rhsType ) ) { if( JavaTypes.COMPARABLE().isAssignableFrom( lhsType ) ) { //noinspection unchecked return ((Comparable)lhsValue).compareTo( rhsValue ) >= 0 ? Boolean.TRUE : Boolean.FALSE; } } } } } return coercionMgr.makeStringFrom( lhsValue ).compareTo( coercionMgr.makeStringFrom( rhsValue ) ) >= 0 ? Boolean.TRUE : Boolean.FALSE; } else // if( _strOperator.equals( "<=" ) ) { if( BeanAccess.isNumericType( lhsType ) ) { return ConditionalExpression.compareNumbers( lhsValue, rhsValue, lhsType, rhsType ) <= 0 ? Boolean.TRUE : Boolean.FALSE; } else if( lhsType == GosuParserTypes.DATETIME_TYPE() ) { Date l = coercionMgr.makeDateFrom( lhsValue ); Date r = coercionMgr.makeDateFrom( rhsValue ); return (l.before( r ) || l.equals( r )) ? Boolean.TRUE : Boolean.FALSE; } else { if( BeanAccess.isBeanType( lhsType ) ) { if( BeanAccess.isBeanType( rhsType ) ) { if( lhsType.isAssignableFrom( rhsType ) ) { if( JavaTypes.COMPARABLE().isAssignableFrom( lhsType ) ) { //noinspection unchecked return ((Comparable)lhsValue).compareTo( rhsValue ) <= 0 ? Boolean.TRUE : Boolean.FALSE; } } } } } return coercionMgr.makeStringFrom( lhsValue ).compareTo( coercionMgr.makeStringFrom( rhsValue ) ) <= 0 ? Boolean.TRUE : Boolean.FALSE; } } }