/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.parser;
import gw.internal.gosu.parser.java.classinfo.CompileTimeExpressionParser;
import gw.internal.gosu.parser.java.classinfo.JavaSourceDefaultValue;
import gw.lang.parser.GosuParserFactory;
import gw.lang.parser.ICompilationState;
import gw.lang.parser.IExpression;
import gw.lang.parser.IGosuParser;
import gw.lang.parser.IParseTree;
import gw.lang.parser.IReducedDynamicFunctionSymbol;
import gw.lang.parser.ISymbol;
import gw.lang.parser.ITypeUsesMap;
import gw.lang.parser.ParserOptions;
import gw.lang.parser.StandardSymbolTable;
import gw.lang.parser.SymbolType;
import gw.lang.parser.TypelessScriptPartId;
import gw.lang.parser.exceptions.ErrantGosuClassException;
import gw.lang.parser.exceptions.ParseResultsException;
import gw.lang.parser.expressions.IIdentifierExpression;
import gw.lang.parser.expressions.INewExpression;
import gw.lang.reflect.BaseFeatureInfo;
import gw.lang.reflect.IAnnotationInfo;
import gw.lang.reflect.IAttributedFeatureInfo;
import gw.lang.reflect.IConstructorInfo;
import gw.lang.reflect.IFeatureInfo;
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.TypeSystem;
import gw.lang.reflect.gs.IGosuClass;
import gw.lang.reflect.gs.IGosuClassTypeInfo;
import gw.lang.reflect.gs.IGosuConstructorInfo;
import gw.lang.reflect.java.IJavaClassInfo;
import gw.lang.reflect.java.IJavaClassMethod;
import gw.lang.reflect.java.IJavaType;
import gw.util.GosuClassUtil;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class GosuAnnotationInfo implements IAnnotationInfo
{
private static final Object NOT_FOUND = new Object() { public String toString() {return "NOT FOUND";} };
private int _index;
private volatile Object _value;
private IFeatureInfo _container;
private IGosuClassInternal _owner;
private String _newExpressionAsString;
private INewExpression _expr;
private IGosuAnnotation _rawAnnotation;
private IType _type;
public GosuAnnotationInfo( IGosuAnnotation rawAnnotation, IFeatureInfo container, IGosuClassInternal owner, int index )
{
_rawAnnotation = rawAnnotation;
_container = container;
_index = index;
_value = null;
_owner = owner;
_newExpressionAsString = rawAnnotation.getNewExpressionAsString();
_type = rawAnnotation.getType();
IExpression e = rawAnnotation.getExpression();
_expr = e instanceof INewExpression ? (INewExpression)e : null;
}
public String getName()
{
return _type.getName();
}
public IFeatureInfo getContainer()
{
return _container;
}
public IGosuClassInternal getOwnersType()
{
return _owner;
}
public String getDisplayName()
{
return getName();
}
public String getDescription()
{
return getName();
}
public Object getInstance()
{
if( _value == null )
{
TypeSystem.lock();
ICompilationState state = _owner.getCompilationState();
if( state.isCompilingHeader() || state.isCompilingDeclarations() || state.isCompilingDefinitions() )
{
throw new IllegalStateException( "You cannot request Annotation values during compilation phase." );
}
try
{
if( !_owner.isValid() )
{
throw new ErrantGosuClassException( _owner );
}
if( _value == null )
{
Map<String, List> featureMap = _owner.getRuntimeFeatureAnnotationMap();
List annotations = featureMap.get( getFeatureName() );
if( annotations == null )
{
if( _owner.isProxy() )
{
return getFromJavaType();
}
throw new IllegalStateException( "Could not resolve feature map for " + getFeatureName() );
}
if( _index >= annotations.size() )
{
throw new IllegalStateException( "Index " + _index + " is not a valid index into " + annotations );
}
Object val = annotations.get( _index );
_value = val;
}
}
finally
{
TypeSystem.unlock();
}
}
return _value;
}
private Object getFromJavaType()
{
IJavaType javaType = _owner.getJavaType();
IRelativeTypeInfo typeInfo = (IRelativeTypeInfo)javaType.getTypeInfo();
if( _container instanceof IPropertyInfo )
{
return typeInfo.getProperty( javaType, _container.getDisplayName() ).getAnnotation( getType() ).getInstance();
}
return typeInfo.getMethod( javaType, _container.getDisplayName(),
BaseFeatureInfo.getParamTypes( ((IMethodInfo)_container).getParameters() ) ).getAnnotation( getType() ).getInstance();
}
// @Override
// public Object _getFieldValue(String field) {
// Object instance = getInstance();
// try {
// Method method = instance.getClass().getMethod(field);
// Object value = method.invoke(instance);
// value = CompileTimeExpressionParser.convertValueToInfoFriendlyValue( value, getOwnersType().getTypeInfo() );
// return value;
// } catch (Exception e) {
// throw new RuntimeException(e);
// }
// }
@Override
public Object getFieldValue( String field )
{
Object value;
if( _type instanceof IJavaType )
{
value = getJavaFieldValue( (IJavaType)_type, field );
}
else
{
value = getGosuFieldValue( (IGosuClassInternal)_type, field );
}
return CompileTimeExpressionParser.convertValueToInfoFriendlyValue( value, _container );
}
private Object getGosuFieldValue( IGosuClassInternal type, String field )
{
Object value = getValueFromCallSite( type, field );
if( value == NOT_FOUND )
{
value = getValueFromDeclaredDefaultValueAtDeclSite( type, field );
if( value == NOT_FOUND )
{
throw new RuntimeException( "Annotation field, " + field + ", not found in " + type.getName() );
}
}
return value;
}
private Object getValueFromDeclaredDefaultValueAtDeclSite( IGosuClassInternal type, String field )
{
List<? extends IConstructorInfo> ctors = type.getTypeInfo().getConstructors( type );
for( IConstructorInfo ctor: ctors )
{
if( ctor instanceof IGosuConstructorInfo )
{
String[] paramNames = ((IGosuConstructorInfo)ctor).getParameterNames();
for( int i = 0; i < paramNames.length; i++ )
{
String paramName = paramNames[i];
if( paramName != null && paramName.equals( field ) )
{
return ((IGosuConstructorInfo)ctor).getDefaultValueExpressions()[i].evaluate();
}
}
}
}
return NOT_FOUND;
}
private Object getJavaFieldValue( IJavaType type, String field )
{
Object value = getValueFromCallSite( type, field );
if( value == NOT_FOUND )
{
value = getValueFromDeclaredDefaultValueAtDeclSite( type, field );
if( value == NOT_FOUND )
{
throw new RuntimeException( "Annotation field, " + field + ", not found in " + type.getName() );
}
}
return value;
}
private Object getValueFromDeclaredDefaultValueAtDeclSite( IJavaType type, String field )
{
IJavaClassInfo classInfo = type.getBackingClassInfo();
try
{
IJavaClassMethod m = classInfo.getDeclaredMethod( field );
Object value = m.getDefaultValue();
if( value instanceof JavaSourceDefaultValue )
{
value = ((JavaSourceDefaultValue) value).evaluate();
}
return value;
}
catch( NoSuchMethodException e )
{
return NOT_FOUND;
}
}
private Object getValueFromCallSite( IType type, String field )
{
List<IIdentifierExpression> ids = new ArrayList<IIdentifierExpression>();
getExpr().getContainedParsedElementsByType( IIdentifierExpression.class, ids );
boolean bTypedSymFound = false;
if( !ids.isEmpty() )
{
for( IIdentifierExpression id: ids )
{
ISymbol sym = id.getSymbol();
bTypedSymFound = bTypedSymFound || sym instanceof TypedSymbol;
if( sym instanceof TypedSymbol &&
((TypedSymbol)sym).getSymbolType() == SymbolType.NAMED_PARAMETER &&
sym.getName().equals( field ) )
{
IParseTree nextSibling = id.getLocation().getNextSibling();
if( nextSibling != null )
{
Expression expr = (Expression)nextSibling.getParsedElement();
return expr.evaluate();
}
return NOT_FOUND;
}
}
}
if( !bTypedSymFound )
{
// Assume old-style ctor invocation where params are not named, so
// we must also assume the ctor-defined param ordering and get the
// field names and ordering from the params.
IExpression[] args = getExpr().getArgs();
if( args != null && args.length > 0 )
{
IParameterInfo[] parameters = getExpr().getConstructor().getParameters();
for( int i = 0; i < parameters.length; i++ )
{
IParameterInfo param = parameters[i];
if( field.equalsIgnoreCase( param.getDisplayName() ) )
{
return args[i].evaluate();
}
}
if( getType() instanceof IJavaType ) {
IJavaClassInfo classInfo = ((IJavaType)getType()).getBackingClassInfo();
if( classInfo instanceof ClassJavaClassInfo ) {
Field[] fields = classInfo.getBackingClass().getDeclaredFields();
for( int i = 0; i < fields.length; i++ )
{
Field f = fields[i];
if( field.equalsIgnoreCase( f.getName() ) )
{
return args[i].evaluate();
}
}
}
}
}
}
if( "value".equalsIgnoreCase( field ) )
{
IExpression[] args = getExpr().getArgs();
if( args != null && args.length == 1 )
{
return args[0].evaluate();
}
}
return NOT_FOUND;
}
private INewExpression getExpr()
{
if( _expr == null )
{
_expr = parseNewExpression();
}
return _expr;
}
private INewExpression parseNewExpression()
{
IGosuClassInternal ownersType = (IGosuClassInternal)_container.getOwnersType();
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 options = new ParserOptions().withTypeUsesMap( usesMap );
IGosuParser parser = GosuParserFactory.createParser( _newExpressionAsString );
options.setParserOptions( parser );
StandardSymbolTable symTable = new StandardSymbolTable( true );
TypeSystem.pushSymTableCtx( symTable );
try
{
parser.setSymbolTable( TypeSystem.getCompiledGosuClassSymbolTable() ); // Set up the symbol table
return (INewExpression)parser.parseExp( new TypelessScriptPartId( toString() ), options.getExpectedType(), options.getFileContext(), false );
}
catch( ParseResultsException e )
{
if( e.hasOnlyParseWarnings() )
{
return (INewExpression)e.getParsedElement();
}
else
{
throw new RuntimeException( e );
}
}
finally
{
TypeSystem.popSymTableCtx();
}
}
private static void addEnclosingPackages( ITypeUsesMap map, IType type )
{
type = TypeLord.getPureGenericType( type );
type = TypeLord.getOuterMostEnclosingClass( type );
map.addToDefaultTypeUses( GosuClassUtil.getPackage( type.getName() ) + "." );
}
private String getFeatureName()
{
return _container instanceof IGosuClassTypeInfo ? GosuClass.CLASS_ANNOTATION_SLOT : resolveFeatureName();
}
private String resolveFeatureName()
{
if( _container instanceof AbstractGenericMethodInfo )
{
AbstractGenericMethodInfo gmi = (AbstractGenericMethodInfo)_container;
IReducedDynamicFunctionSymbol fs = gmi.getDfs();
IAttributedFeatureInfo actualFeatureInfo = gmi;
while( fs instanceof ReducedParameterizedDynamicFunctionSymbol )
{
fs = ((ReducedParameterizedDynamicFunctionSymbol) fs).getBackingDfs();
}
//IAttributedFeatureInfo actualFeatureInfo = fs.getMethodOrConstructorInfo();
return actualFeatureInfo.getName();
}
else
{
return _container.getName();
}
}
public IType getType()
{
if (TypeSystem.isDeleted(_type)) {
return TypeSystem.getErrorType(_type.getName());
} else {
return TypeLord.getPureGenericType( _type );
}
}
public String toString()
{
return getName();
}
public String getNewExpressionAsString() {
return _newExpressionAsString;
}
public IGosuAnnotation getRawAnnotation()
{
return _rawAnnotation;
}
}