/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.parser;
import gw.config.CommonServices;
import gw.internal.gosu.parser.expressions.BeanMethodCallExpression;
import gw.internal.gosu.parser.expressions.Identifier;
import gw.internal.gosu.parser.expressions.MemberAccess;
import gw.internal.gosu.parser.expressions.MethodCallExpression;
import gw.internal.gosu.parser.expressions.NewExpression;
import gw.internal.gosu.parser.expressions.TypeLiteral;
import gw.internal.gosu.parser.statements.ClassStatement;
import gw.internal.gosu.parser.statements.FunctionStatement;
import gw.internal.gosu.parser.statements.PropertyStatement;
import gw.lang.parser.IDeclarationSiteValidator;
import gw.lang.parser.IParseTree;
import gw.lang.parser.IParsedElement;
import gw.lang.parser.ISymbol;
import gw.lang.parser.ITypeUsesMap;
import gw.lang.parser.IUsageSiteValidator;
import gw.lang.parser.IUsageSiteValidatorReference;
import gw.lang.parser.ParserOptions;
import gw.lang.parser.StandardSymbolTable;
import gw.lang.parser.exceptions.ParseResultsException;
import gw.lang.parser.resources.Res;
import gw.lang.reflect.IAnnotationInfo;
import gw.lang.reflect.IAttributedFeatureInfo;
import gw.lang.reflect.IConstructorInfo;
import gw.lang.reflect.IMethodInfo;
import gw.lang.reflect.IMethodInfoDelegate;
import gw.lang.reflect.IPropertyInfo;
import gw.lang.reflect.IType;
import gw.lang.reflect.gs.IGosuClass;
import gw.lang.reflect.gs.IGosuFragment;
import gw.lang.reflect.java.JavaTypes;
import gw.util.GosuClassUtil;
import gw.util.GosuExceptionUtil;
import java.lang.reflect.Constructor;
import java.util.List;
public class CompileTimeAnnotationHandler
{
public static void postDefinitionVerification( IParsedElement elt )
{
postDefinitionVerification( elt, true );
}
public static Object eval( IAnnotationInfo ai )
{
if( ai instanceof GosuAnnotationInfo )
{
GosuAnnotationInfo info = (GosuAnnotationInfo) ai;
return evalGosuAnnotation(info.getNewExpressionAsString(), info.getOwnersType());
}
else
{
return ai.getInstance();
}
}
private static Object evalGosuAnnotation( String newExpressionString, IGosuClass ownersType )
{
if( newExpressionString != null )
{
ITypeUsesMap usesMap;
IType outerMostEnclosingType = TypeLord.getOuterMostEnclosingClass( ownersType );
if( outerMostEnclosingType instanceof IGosuClass )
{
usesMap = ((IGosuClass)outerMostEnclosingType).getTypeUsesMap();
}
else
{
usesMap = ownersType.getTypeUsesMap();
}
if( usesMap != null )
{
usesMap = usesMap.copy();
usesMap.addToDefaultTypeUses( "gw.lang." );
}
else
{
usesMap = new TypeUsesMap();
}
addEnclosingPackages( usesMap, ownersType );
ParserOptions parserOptions = new ParserOptions().withTypeUsesMap( usesMap );
IGosuFragment fragment;
try
{
fragment = GosuFragmentParser.getInstance().parseExpressionOnly( newExpressionString, new StandardSymbolTable( true ), parserOptions );
}
catch( ParseResultsException e )
{
throw GosuExceptionUtil.forceThrow( e );
}
return fragment.evaluate( null );
}
else
{
return null;
}
}
private static void postDefinitionVerification( IParsedElement elt, boolean recurseOnClassStmt )
{
// Usage Sites
if( elt instanceof BeanMethodCallExpression )
{
verifyBeanMethodCallExpression( (BeanMethodCallExpression) elt );
}
else if( elt instanceof MethodCallExpression )
{
verifyMethodCallExpression( (MethodCallExpression) elt );
}
else if( elt instanceof MemberAccess )
{
IType rootType = ((MemberAccess)elt).getRootType();
if( rootType != null && rootType.getTypeInfo() != null && ((MemberAccess)elt).getMemberName() != null )
{
IPropertyInfo pi = BeanAccess.getProperty( rootType.getTypeInfo(), rootType, ((MemberAccess)elt).getMemberName() );
if( pi != null )
{
verifyUsage( elt, pi.getAnnotations() );
}
}
}
else if( elt instanceof Identifier )
{
ISymbol sym = ((Identifier)elt).getSymbol();
if( sym instanceof DynamicPropertySymbol )
{
DynamicFunctionSymbol getterDfs = ((DynamicPropertySymbol)sym).getGetterDfs();
if( getterDfs != null && getterDfs.getMethodOrConstructorInfo() != null)
{
verifyUsage( elt, getterDfs.getMethodOrConstructorInfo().getAnnotations() );
}
DynamicFunctionSymbol setterDfs = ((DynamicPropertySymbol)sym).getSetterDfs();
if( setterDfs != null && setterDfs.getMethodOrConstructorInfo() != null )
{
verifyUsage( elt, setterDfs.getMethodOrConstructorInfo().getAnnotations() );
}
}
}
else if( elt instanceof NewExpression )
{
IConstructorInfo ctor = ((NewExpression)elt).getConstructor();
if( ctor != null )
{
verifyUsage( elt, ctor.getAnnotations() );
}
}
else if( elt instanceof TypeLiteral )
{
IType type = ((TypeLiteral)elt).evaluate();
if(type != null && type.getTypeInfo() != null )
{
verifyUsage( elt, ((TypeLiteral)elt).evaluate().getTypeInfo().getAnnotations() );
}
}
// Declaration Sites
if( elt instanceof ClassStatement )
{
IGosuClassInternal clazz = ((ClassStatement)elt).getGosuClass();
if( clazz != null )
{
verifyDeclarationSite( elt, clazz.getTypeInfo().getAnnotations() );
}
if( !recurseOnClassStmt )
{
return;
}
}
else if( elt instanceof FunctionStatement )
{
DynamicFunctionSymbol dfs = ((FunctionStatement)elt).getDynamicFunctionSymbol();
if( dfs != null )
{
IAttributedFeatureInfo fi = dfs.getMethodOrConstructorInfo();
if( fi != null )
{
verifyDeclarationSite( elt, fi.getAnnotations() );
}
}
}
else if( elt instanceof PropertyStatement )
{
FunctionStatement fs = ((PropertyStatement)elt).getPropertyGetterOrSetter();
if( fs != null )
{
DynamicFunctionSymbol dfs = fs.getDynamicFunctionSymbol();
if( dfs != null )
{
IAttributedFeatureInfo fi = dfs.getMethodOrConstructorInfo();
if( fi != null )
{
verifyDeclarationSite( elt, fi.getAnnotations() );
}
}
}
}
IParseTree location = elt == null ? null : elt.getLocation();
if( location != null )
{
for( IParseTree parseTree : location.getChildren() )
{
postDefinitionVerification( parseTree.getParsedElement(), false );
}
}
}
private static void verifyBeanMethodCallExpression( BeanMethodCallExpression expr )
{
verifyMethodInvocation( expr, expr.getMethodDescriptor() );
}
private static void verifyMethodCallExpression( MethodCallExpression expr )
{
if( expr.getFunctionType() != null && expr.getFunctionType().getMethodInfo() != null )
{
verifyMethodInvocation( expr, expr.getFunctionType().getMethodInfo() );
}
}
private static void verifyMethodInvocation( IParsedElement expr, IMethodInfo md )
{
if( md != null )
{
while( md instanceof IMethodInfoDelegate )
{
md = ((IMethodInfoDelegate)md).getSource();
}
verifyUsage( expr, md.getAnnotations() );
}
}
private static void verifyUsage( IParsedElement expr, List<IAnnotationInfo> featureAnnotations )
{
for( IAnnotationInfo ai : featureAnnotations )
{
IUsageSiteValidator mcv = null;
if(JavaTypes.getGosuType( IUsageSiteValidator.class ).isAssignableFrom( ai.getType() ) )
{
mcv = (IUsageSiteValidator)evalAndHandleError( ai, expr );
}
else if( JavaTypes.getGosuType( IUsageSiteValidatorReference.class ).isAssignableFrom( ai.getType() ) )
{
IUsageSiteValidatorReference ref = (IUsageSiteValidatorReference)evalAndHandleError( ai, expr );
if(ref != null) {
Class<? extends IUsageSiteValidator> validator = ref.value();
try {
Constructor<? extends IUsageSiteValidator> ctor = validator.getDeclaredConstructor();
ctor.setAccessible( true );
mcv = ctor.newInstance();
}
catch( Exception e ) {
CommonServices.getEntityAccess().getLogger().warn( "Unable to instantiate IUsageSiteValidator of type " + validator.getName() );
}
}
}
if( mcv != null )
{
mcv.validate( expr );
}
}
}
private static void verifyDeclarationSite( IParsedElement feature, List<IAnnotationInfo> annotations )
{
if( annotations != null )
{
for( IAnnotationInfo annotationInfo : annotations )
{
if( JavaTypes.getGosuType( IDeclarationSiteValidator.class ).isAssignableFrom( annotationInfo.getType() ) )
{
IDeclarationSiteValidator o = (IDeclarationSiteValidator)evalAndHandleError( annotationInfo, feature );
if( o != null )
{
o.validate( feature );
}
}
}
}
}
private static Object evalAndHandleError( IAnnotationInfo ai, IParsedElement elt )
{
try
{
IType gsClass = ai.getType();
if( gsClass != null && gsClass instanceof IGosuClass )
{
// Force bytecode compilation ahead of unholy annotation evaluation
((IGosuClass)gsClass).getBackingClass();
}
return eval( ai );
}
catch( Exception e )
{
elt.addParseException( Res.MSG_COMPILE_TIME_ANNOTATION_FAILED_TO_EXECUTE, e.getMessage() );
return null;
}
}
private static void addEnclosingPackages( ITypeUsesMap map, IType type )
{
type = TypeLord.getPureGenericType( type );
type = TypeLord.getOuterMostEnclosingClass( type );
map.addToDefaultTypeUses( GosuClassUtil.getPackage( type.getName() ) + "." );
}
}