/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.gosu.parser.expressions; import gw.internal.gosu.parser.ErrorType; import gw.internal.gosu.parser.ErrorTypeInfo; import gw.internal.gosu.parser.Expression; import gw.internal.gosu.parser.types.ConstructorType; import gw.internal.gosu.parser.types.FunctionLiteralType; import gw.lang.parser.IExpression; import gw.lang.parser.Keyword; import gw.lang.parser.expressions.IFeatureLiteralExpression; import gw.lang.reflect.FunctionType; import gw.lang.reflect.IConstructorInfo; import gw.lang.reflect.IFeatureInfo; import gw.lang.reflect.IHasParameterInfos; import gw.lang.reflect.IInvocableType; import gw.lang.reflect.IMethodInfo; import gw.lang.reflect.IParameterInfo; import gw.lang.reflect.IPropertyInfo; import gw.lang.reflect.IRelativeTypeInfo; import gw.lang.reflect.IType; import gw.lang.reflect.ITypeInfo; import gw.lang.reflect.TypeSystem; import gw.lang.reflect.features.BoundComplexPropertyChainReference; import gw.lang.reflect.features.BoundMethodReference; import gw.lang.reflect.features.BoundPropertyReference; import gw.lang.reflect.features.BoundSimplePropertyChainReference; import gw.lang.reflect.features.ComplexPropertyChainReference; import gw.lang.reflect.features.ConstructorReference; import gw.lang.reflect.features.MethodChainReference; import gw.lang.reflect.features.MethodReference; import gw.lang.reflect.features.PropertyReference; import gw.lang.reflect.features.SimplePropertyChainReference; import gw.lang.reflect.java.JavaTypes; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * Represents a feature literal expression as defined in the Gosu grammar. * * @see gw.lang.parser.IGosuParser */ public class FeatureLiteral extends Expression implements IFeatureLiteralExpression { private IExpression _root; IFeatureInfo _feature; private List<IExpression> _boundArgs; public FeatureLiteral( Expression rootExpr ) { _root = rootExpr; } public boolean resolveProperty( String propName ) { IType typeToResolveAgainst = getRootTypeToResolveAgainst(); IPropertyInfo property; if( typeToResolveAgainst.getTypeInfo() instanceof IRelativeTypeInfo ) { property = ((IRelativeTypeInfo)typeToResolveAgainst.getTypeInfo()).getProperty( typeToResolveAgainst, propName ); } else { property = typeToResolveAgainst.getTypeInfo().getProperty( propName ); } if( property == null ) { _feature = ErrorType.getInstance().getTypeInfo().getProperty( propName ); return false; } else { _feature = property; return true; } } public boolean resolveMethod( String methodName, IType... argTypes ) { IType typeToResolveAgainst = getRootTypeToResolveAgainst(); IMethodInfo methodInfo; ITypeInfo typeInfo = typeToResolveAgainst.getTypeInfo(); if( typeInfo instanceof IRelativeTypeInfo ) { methodInfo = ((IRelativeTypeInfo)typeInfo).getMethod( typeToResolveAgainst, methodName, argTypes ); } else { methodInfo = typeInfo.getMethod( methodName, argTypes ); } if( methodInfo == null ) { if( typeInfo instanceof IRelativeTypeInfo ) { methodInfo = findSingleMethodMatchingName( methodName, ((IRelativeTypeInfo)typeInfo).getMethods( typeToResolveAgainst ) ); } else { methodInfo = findSingleMethodMatchingName( methodName, typeInfo.getMethods() ); } } if( methodInfo == null ) { _feature = ErrorType.getInstance().getTypeInfo().getMethod( methodName, argTypes ); return false; } else { _feature = methodInfo; return true; } } private IMethodInfo findSingleMethodMatchingName( String methodName, List<? extends IMethodInfo> methods ) { IMethodInfo match = null; for( IMethodInfo possibleMatch : methods ) { if( possibleMatch.getDisplayName().equals( methodName ) ) { if( match != null ) { return null; } else { match = possibleMatch; } } } return match; } public boolean resolveConstructor( IType... argTypes ) { IType typeToResolveAgainst = getRootTypeToResolveAgainst(); ITypeInfo typeInfo = typeToResolveAgainst.getTypeInfo(); IConstructorInfo constructorInfo = null; if( !(typeInfo instanceof ErrorTypeInfo) ) { if( typeInfo instanceof IRelativeTypeInfo ) { constructorInfo = ((IRelativeTypeInfo) typeInfo).getConstructor( typeToResolveAgainst, argTypes ); } else { constructorInfo = typeInfo.getConstructor( argTypes ); } } if( constructorInfo == null ) { if( typeInfo instanceof IRelativeTypeInfo ) { List<? extends IConstructorInfo> constructors = ((IRelativeTypeInfo)typeInfo).getConstructors( typeToResolveAgainst ); if( constructors != null && constructors.size() == 1 ) { constructorInfo = constructors.get( 0 ); } } else { List<? extends IConstructorInfo> constructors = typeInfo.getConstructors(); if( constructors != null && constructors.size() == 1 ) { constructorInfo = constructors.get( 0 ); } } } if( constructorInfo == null ) { _feature = JavaTypes.OBJECT().getTypeInfo().getConstructor(); return false; } else { _feature = constructorInfo; return true; } } @Override public String toString() { return _root.toString() + featureRepresentation(); } private String featureRepresentation() { return _feature == null ? "<ERROR>" : _feature.getName(); } private IType getRootTypeToResolveAgainst() { if( _root instanceof FeatureLiteral ) { return ((FeatureLiteral)_root).getFeatureReturnType(); } else if( _root instanceof TypeLiteral ) { return ((TypeLiteral)_root).getType().getType(); } else { return _root.getType(); } } public IType getFeatureReturnType() { if( isPropertyLiteral() ) { return ((IPropertyInfo)_feature).getFeatureType(); } else if( isMethodLiteral() ) { return ((IMethodInfo)_feature).getReturnType(); } else if( isConstructorLiteral() ) { return ((IConstructorInfo)_feature).getType(); } else { return ErrorType.getInstance(); } } public boolean isConstructorLiteral() { return _feature instanceof IConstructorInfo; } public boolean isMethodLiteral() { return _feature instanceof IMethodInfo; } public boolean isPropertyLiteral() { return _feature instanceof IPropertyInfo; } public void resolveType() { setType( getExpressionType() ); } public IExpression getRoot() { return _root; } public IExpression getFinalRoot() { IExpression root = getRoot(); if( root instanceof IFeatureLiteralExpression ) { return ((IFeatureLiteralExpression)root).getFinalRoot(); } return root; } public IType getFinalRootType() { if( _root instanceof TypeLiteral ) { return ((TypeLiteral)_root).getType().getType(); } else if( _root instanceof FeatureLiteral ) { return ((FeatureLiteral)_root).getFinalRootType(); } else { return _root.getType(); } } public IType getRootType() { if( _root instanceof TypeLiteral ) { return ((TypeLiteral)_root).getType().getType(); } else if( _root instanceof FeatureLiteral ) { return ((FeatureLiteral)_root).getFeatureReturnType(); } else { return _root.getType(); } } public String getPropertyName() { return _feature.getName(); } public String getMethodName() { return _feature.getDisplayName(); } public IType[] getParameterTypes() { ArrayList<IType> types = new ArrayList<IType>(); IParameterInfo[] parameterInfos; if( _feature instanceof IMethodInfo ) { parameterInfos = ((IMethodInfo)_feature).getParameters(); } else if( _feature instanceof IConstructorInfo ) { parameterInfos = ((IConstructorInfo)_feature).getParameters(); } else { parameterInfos = new IParameterInfo[0]; } for( IParameterInfo pi : parameterInfos ) { types.add( pi.getFeatureType() ); } return types.toArray( new IType[types.size()] ); } public IType getExpressionType() { if( isPropertyLiteral() ) { IExpression root = getRoot(); Class clazz; if( root instanceof TypeLiteral ) { clazz = PropertyReference.class; } else if( root instanceof FeatureLiteral ) { if( JavaTypes.getGosuType( PropertyReference.class ).isAssignableFrom( root.getType() ) || JavaTypes.getGosuType( SimplePropertyChainReference.class ).isAssignableFrom( root.getType() )) { clazz = SimplePropertyChainReference.class; } else if( JavaTypes.getGosuType( BoundPropertyReference.class ).isAssignableFrom( root.getType() ) || JavaTypes.getGosuType( BoundSimplePropertyChainReference.class ).isAssignableFrom( root.getType() ) ) { clazz = BoundSimplePropertyChainReference.class; } else { if( ((FeatureLiteral)root).isBound() ) { clazz = BoundComplexPropertyChainReference.class; } else { clazz = ComplexPropertyChainReference.class; } } } else { clazz = BoundPropertyReference.class; } IType rawType = TypeSystem.get( clazz ); IType parameterizedType = rawType.getParameterizedType( getFinalRootType(), getFeatureReturnType() ); return parameterizedType; } else if( isMethodLiteral() ) { IExpression root = getRoot(); Class clazz; if( root instanceof TypeLiteral ) { clazz = MethodReference.class; } else if( root instanceof FeatureLiteral ) { clazz = MethodChainReference.class; } else { clazz = BoundMethodReference.class; } IType rawType = TypeSystem.get( clazz ); IMethodInfo mi = (IMethodInfo)_feature; IType bt = makeBlockType( mi.getReturnType(), getFeatureReferenceParameters() ); IType parameterizedType = rawType.getParameterizedType( getFinalRootType(), bt ); IType specializedType = specializeWithParams( clazz, parameterizedType, mi ); return specializedType; } else if( isConstructorLiteral() ) { if( getRoot() instanceof TypeLiteral ) { Class<ConstructorReference> clazz = ConstructorReference.class; IType parameterizedType = TypeSystem.get( clazz ).getParameterizedType( getFinalRootType(), makeBlockType( getFinalRootType(), getParameterTypes() ) ); IType specializedType = specializeWithParams( clazz, parameterizedType, (IHasParameterInfos) getFeature() ); return specializedType; } else { return ErrorType.getInstance(); } } else { return ErrorType.getInstance(); } } private IType specializeWithParams( Class clazz, IType rawType, IHasParameterInfos feature ) { return new FunctionLiteralType( clazz, rawType, feature ); } private IType makeBlockType( IType returnType, IType[] params ) { if( params.length > 16 ) { return JavaTypes.OBJECT(); } else { return new BlockType( returnType, params, Collections.<String>emptyList(), Collections.<IExpression>emptyList() ); } } public boolean isStaticish() { if( _feature instanceof IMethodInfo ) { return ((IMethodInfo)_feature).isStatic(); } else if( _feature instanceof IPropertyInfo ) { return ((IPropertyInfo)_feature).isStatic(); } else if( _feature instanceof IConstructorInfo ) { return true; } else { return false; } } public IType[] getFeatureReferenceParameters() { ArrayList<IType> actualParams = new ArrayList<IType>(); IExpression root = getRoot(); if( root instanceof TypeLiteral ) { if( _feature instanceof IPropertyInfo && !((IPropertyInfo)_feature).isStatic() ) { actualParams.add( ((TypeLiteral)root).evaluate() ); } if( _feature instanceof IMethodInfo && !((IMethodInfo)_feature).isStatic() ) { actualParams.add( ((TypeLiteral)root).evaluate() ); } } else if( root instanceof FeatureLiteral ) { actualParams.addAll( Arrays.asList( ((FeatureLiteral)root).getFeatureReferenceParameters() ) ); } if( _boundArgs == null ) { if( _feature instanceof IConstructorInfo ) { for( IParameterInfo pi : ((IConstructorInfo)_feature).getParameters() ) { actualParams.add( pi.getFeatureType() ); } } else if( _feature instanceof IMethodInfo ) { for( IParameterInfo mi : ((IMethodInfo)_feature).getParameters() ) { actualParams.add( mi.getFeatureType() ); } } } return actualParams.toArray( new IType[actualParams.size()] ); } public IFeatureInfo getFeature() { return _feature; } public List<? extends IInvocableType> getFunctionTypes( String name ) { ArrayList<IInvocableType> lst = new ArrayList<IInvocableType>(); IType rootType = getRootTypeToResolveAgainst(); if( name.equals( Keyword.KW_construct.toString() ) ) { ITypeInfo typeInfo = rootType.getTypeInfo(); List<? extends IConstructorInfo> constructors; if (typeInfo instanceof IRelativeTypeInfo) { constructors = ((IRelativeTypeInfo) typeInfo).getConstructors(rootType); } else { constructors = typeInfo.getConstructors(); } for( IConstructorInfo ci : constructors) { lst.add( new ConstructorType( ci ) ); } } else { ITypeInfo typeInfo = rootType.getTypeInfo(); List<? extends IMethodInfo> methods; if (typeInfo instanceof IRelativeTypeInfo) { methods = ((IRelativeTypeInfo) typeInfo).getMethods(rootType); } else { methods = typeInfo.getMethods(); } for( IMethodInfo mi : methods ) { if( mi.getDisplayName().equals( name ) ) { lst.add( new FunctionType( mi ) ); } } } return lst; } public void setBoundFeature( IFeatureInfo fi, List<IExpression> arguments ) { _feature = fi; _boundArgs = arguments; } public List<IExpression> getBoundArgs() { return _boundArgs; } public boolean isBound() { if( _root instanceof FeatureLiteral ) { return ((FeatureLiteral) _root).isBound(); } else { return !(_root instanceof TypeLiteral); } } }