/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.parser;
import gw.config.CommonServices;
import gw.lang.parser.ISymbol;
import gw.lang.parser.ISymbolTable;
import gw.lang.parser.expressions.IVarStatement;
import gw.lang.reflect.*;
import gw.lang.reflect.gs.IGosuClassTypeInfo;
import gw.lang.reflect.gs.IGosuEnhancement;
import gw.lang.reflect.gs.IGosuMethodInfo;
import gw.lang.reflect.java.JavaTypes;
import gw.util.concurrent.LockingLazyVar;
import java.util.*;
/**
*/
@SuppressWarnings({"unchecked"})
public class GosuClassTypeInfo extends BaseTypeInfo implements IGosuClassTypeInfo
{
private static final ThreadLocal<List<Boolean>> INCLUDE_ALL = new ThreadLocal<List<Boolean>>();
private IGosuClassInternal _gsClass;
private ArrayList<IGosuMethodInfo> _declaredMethods;
private List<IPropertyInfo> _declaredProperties;
private ArrayList _declaredConstructors;
private LockingLazyVar<List<IAnnotationInfo>> _declaredAnnotations;
private Map<GosuBaseAttributedFeatureInfo, IModifierInfo> _modifierInfoByFeature;
private MyFeatureManager _fm;
public static void pushIncludeAll()
{
List<Boolean> stack = INCLUDE_ALL.get();
if( stack == null )
{
stack = new ArrayList<Boolean>();
INCLUDE_ALL.set( stack );
}
stack.add( 0, Boolean.TRUE );
}
public static void popIncludeAll()
{
List stack = INCLUDE_ALL.get();
if( stack == null || stack.isEmpty() )
{
throw new IllegalStateException( "Attempted to pop an empty IncludeAll stack" );
}
stack.remove( 0 );
}
public static boolean isIncludeAll()
{
List<Boolean> stack = INCLUDE_ALL.get();
return stack != null && !stack.isEmpty() && stack.get( 0 );
}
public GosuClassTypeInfo( IGosuClassInternal gsClass )
{
super( gsClass );
_gsClass = gsClass;
_fm = new MyFeatureManager();
_modifierInfoByFeature = new HashMap<GosuBaseAttributedFeatureInfo, IModifierInfo>();
_declaredAnnotations =
new LockingLazyVar<List<IAnnotationInfo>>()
{
@Override
protected List<IAnnotationInfo> init()
{
List<? extends IGosuAnnotation> annotations = getGosuClass().getGosuAnnotations();
List<IAnnotationInfo> result = Collections.emptyList();
if( annotations != null )
{
result = new ArrayList<IAnnotationInfo>();
for( int i = 0; i < annotations.size(); i++ )
{
IGosuAnnotation annotation = annotations.get( i );
result.add( new GosuAnnotationInfo( annotation, GosuClassTypeInfo.this, getGosuClass(), i ) );
}
}
return result;
}
};
}
public void forceInit() {
_fm.forceInit();
}
public boolean isStatic()
{
return _gsClass.isStatic();
}
public boolean isPrivate()
{
return Modifier.isPrivate( _gsClass.getModifiers() );
}
public boolean isInternal()
{
return Modifier.isInternal( _gsClass.getModifiers() );
}
public boolean isProtected()
{
return Modifier.isProtected( _gsClass.getModifiers() );
}
public boolean isPublic()
{
return Modifier.isPublic( _gsClass.getModifiers() );
}
public boolean isDeprecated() {
List<IAnnotationInfo> annotations = getAnnotationsOfType(TypeSystem.get(gw.lang.Deprecated.class, TypeSystem.getGlobalModule()));
return (annotations != null) &&
(annotations.size() > 0);
}
public List<IAnnotationInfo> getDeclaredAnnotations()
{
return _declaredAnnotations.get();
}
public List<? extends IMethodInfo> getDeclaredMethods()
{
extractMethods();
return _declaredMethods;
}
public List<? extends IPropertyInfo> getDeclaredProperties()
{
extractProperties();
return _declaredProperties;
}
public List<? extends IConstructorInfo> getDeclaredConstructors() {
extractConstructors();
return _declaredConstructors;
}
public String getName()
{
return getGosuClass().getRelativeName();
}
public Accessibility getAccessibilityForType( IType whosaskin )
{
return FeatureManager.getAccessibilityForClass(_gsClass, whosaskin == null ? getCompilingClass() : whosaskin);
}
public List<? extends IPropertyInfo> getProperties()
{
return getProperties( null );
}
public List<IPropertyInfo> getProperties( IType whosAskin )
{
return _fm.getProperties(getAccessibilityForType(whosAskin));
}
public IPropertyInfo getProperty( CharSequence propName )
{
return getProperty( null, propName );
}
public IPropertyInfo getProperty( IType whosAskin, CharSequence propName )
{
return _fm.getProperty(getAccessibilityForType(whosAskin), propName);
}
public MethodList getMethods()
{
return getMethods( null );
}
public MethodList getMethods(IType whosAskin)
{
return _fm.getMethods(getAccessibilityForType(whosAskin));
}
public List<GosuConstructorInfo> getConstructors()
{
return getConstructors( null );
}
public List<GosuConstructorInfo> getConstructors( IType whosAskin )
{
return (List<GosuConstructorInfo>) _fm.getConstructors(getAccessibilityForType(whosAskin));
}
public IMethodInfo getMethod( CharSequence methodName, IType... params )
{
return FIND.method( getMethods(), methodName, params );
}
public IMethodInfo getMethod( IType whosaskin, CharSequence methodName, IType... params )
{
MethodList methods = getMethods(whosaskin);
return FIND.method( methods, methodName, params );
}
public IConstructorInfo getConstructor( IType... params )
{
return FIND.constructor( getConstructors(), params );
}
public IConstructorInfo getConstructor( IType whosAskin, IType[] params )
{
List<GosuConstructorInfo> ctors = getConstructors( whosAskin );
return FIND.constructor( ctors, params );
}
public IMethodInfo getCallableMethod( CharSequence strMethod, IType... params )
{
return FIND.callableMethod( getMethods(), strMethod, params );
}
public IConstructorInfo getCallableConstructor( IType... params )
{
return FIND.callableConstructor( getConstructors(), params );
}
public List<IEventInfo> getEvents()
{
return Collections.emptyList();
}
public IEventInfo getEvent( CharSequence strEvent )
{
return null;
}
public IGosuClassInternal getGosuClass()
{
return _gsClass;
}
/**
* We expose type info in a context sensitive manner. For instance, we only
* expose private features from within the containing class. We can determine
* the context class from the current compiling class. The compiling class is
* obtained from either 1) the actual CompiledGosuClass stack the Gosu
* Class TypeLoader manages or 2) from the class itself if it's not compiled
* yet. Note the latter case happens only on the UI side when a class is being
* edited.
*/
private IType getCompilingClass()
{
if( isIncludeAll() )
{
return _gsClass;
}
IType type = GosuClassCompilingStack.getCurrentCompilingType();
if( type != null )
{
return type;
}
ISymbolTable symTableCtx = CompiledGosuClassSymbolTable.getSymTableCtx();
ISymbol thisSymbol = symTableCtx.getThisSymbolFromStackOrMap();
if( thisSymbol != null )
{
IType thisSymbolType = thisSymbol.getType();
if( thisSymbolType instanceof IGosuClassInternal )
{
return thisSymbolType;
}
}
return !_gsClass.isDeclarationsCompiled() ? _gsClass : null;
}
private void extractProperties()
{
if( _declaredProperties != null )
{
return;
}
TypeSystem.lock();
try
{
if( _declaredProperties != null )
{
return;
}
List<IPropertyInfo> declaredProperties = new ArrayList<IPropertyInfo>();
if( _gsClass.isParameterizedType() )
{
makePropertiesFromGenericTypesFields( declaredProperties );
}
else
{
makePropertiesFromFields( declaredProperties, _gsClass.getStaticFields() );
makePropertiesFromFields( declaredProperties, (Collection<IVarStatement>) _gsClass.getMemberFields());
}
addDefinedProperties( declaredProperties, _gsClass.getStaticProperties() );
addDefinedProperties( declaredProperties, _gsClass.getMemberProperties() );
_declaredProperties = declaredProperties;
}
finally
{
TypeSystem.unlock();
}
}
private void makePropertiesFromGenericTypesFields( List<IPropertyInfo> declaredPropertiesMap )
{
if( !_gsClass.isParameterizedType() )
{
throw new IllegalStateException( "Not a parameterized type" );
}
TypeSystem.lock();
try
{
IGosuClassInternal cl = TypeLord.getPureGenericType( _gsClass );
for( IPropertyInfo pi : cl.getTypeInfo().getDeclaredProperties() )
{
if( pi instanceof GosuVarPropertyInfo )
{
GosuVarPropertyInfo fieldProp = new GosuVarPropertyInfo( this, (GosuVarPropertyInfo)pi );
declaredPropertiesMap.add( fieldProp );
}
}
}
finally
{
TypeSystem.unlock();
}
}
private void makePropertiesFromFields(List<IPropertyInfo> declaredPropertiesMap, Collection<IVarStatement> fields)
{
TypeSystem.lock();
try
{
for( IVarStatement varStmt : fields )
{
GosuVarPropertyInfo fieldProp = new GosuVarPropertyInfo( this, varStmt );
declaredPropertiesMap.add( fieldProp );
}
}
finally
{
TypeSystem.unlock();
}
}
private void addDefinedProperties( List<IPropertyInfo> declaredPropertiesMap, List<DynamicPropertySymbol> propertySymbols )
{
TypeSystem.lock();
try
{
for( DynamicPropertySymbol dps : propertySymbols )
{
if( getOwnersType().isParameterizedType() )
{
dps = dps.getParameterizedVersion( (IGosuClassInternal)getOwnersType() );
}
GosuPropertyInfo property;
if( dps.getDisplayName().equals( "IntrinsicType" ) )
{
property = new GosuPropertyInfo( this, new IntrinsicTypePropertySymbol( _gsClass, TypeSystem.getCompiledGosuClassSymbolTable() ) );
}
else
{
if (dps instanceof ParameterizedDynamicPropertySymbol) {
property = new GosuPropertyInfo( this, dps );
} else {
property = new ParameterizedGosuPropertyInfo( this, dps, new GosuPropertyInfo( this, dps) );
}
}
declaredPropertiesMap.add( property );
}
}
finally
{
TypeSystem.unlock();
}
}
private void extractMethods()
{
if( _declaredMethods != null )
{
return;
}
TypeSystem.lock();
try
{
if( _declaredMethods != null )
{
return;
}
ArrayList<IGosuMethodInfo> declaredMethods = new ArrayList<IGosuMethodInfo>();
List mapStaticFunctions = _gsClass.getStaticFunctions();
if( mapStaticFunctions != null )
{
List<IGosuMethodInfo> staticMethodInfos = createMethodInfos( mapStaticFunctions );
declaredMethods.addAll( staticMethodInfos );
}
List mapMemberFunctions = _gsClass.getMemberFunctions();
if( mapMemberFunctions != null )
{
List methodInfos = createMethodInfos( mapMemberFunctions );
declaredMethods.addAll( methodInfos );
}
declaredMethods.trimToSize();
_declaredMethods = declaredMethods;
}
finally
{
TypeSystem.unlock();
}
}
private List<IGosuMethodInfo> createMethodInfos( List mapFunctions )
{
List functionInfos = new ArrayList();
for (Object mapFunction : mapFunctions) {
DynamicFunctionSymbol dfs = (DynamicFunctionSymbol) mapFunction;
if ( getOwnersType().isParameterizedType()) {
dfs = dfs.getParameterizedVersion((IGosuClassInternal) getOwnersType());
}
GosuMethodInfo methodInfo = new GosuMethodInfo(this, dfs);
functionInfos.add(methodInfo);
}
return functionInfos;
}
private void extractConstructors()
{
if( _declaredConstructors != null )
{
return;
}
TypeSystem.lock();
try
{
if( _declaredConstructors != null )
{
return;
}
ArrayList constructors = new ArrayList();
if( _gsClass instanceof IGosuEnhancementInternal )
{
//no constructors allowed on enhancements
}
else
{
List<DynamicFunctionSymbol> mapConstructors = _gsClass.getConstructorFunctions();
if( mapConstructors != null )
{
constructors.addAll( createConstructorInfos( mapConstructors ) );
}
constructors = new ArrayList( TypeInfoUtil.makeSortedUnmodifiableRandomAccessList( constructors ));
}
constructors.trimToSize();
_declaredConstructors = constructors;
}
finally
{
TypeSystem.unlock();
}
}
private List createConstructorInfos( List<DynamicFunctionSymbol> functions )
{
List functionInfos = new ArrayList();
for( DynamicFunctionSymbol function : functions )
{
GosuConstructorInfo methodInfo = new GosuConstructorInfo( this, function );
if( getOwnersType().isParameterizedType() )
{
function = function.getParameterizedVersion( (IGosuClassInternal)getOwnersType() );
methodInfo = new ParameterizedGosuConstructorInfo( this, function, methodInfo );
}
functionInfos.add( methodInfo );
}
return functionInfos;
}
public void unload()
{
_declaredConstructors = null;
_declaredMethods = null;
_declaredProperties = null;
_fm.clear();
}
public String getDescription()
{
return getGosuClass().getFullDescription();
}
public IModifierInfo getModifierInfo(GosuBaseAttributedFeatureInfo featureInfo)
{
if( getOwnersType().isParameterizedType() )
{
return ((GosuClassTypeInfo)getOwnersType().getGenericType().getTypeInfo()).getModifierInfo( featureInfo );
}
return _modifierInfoByFeature.get(featureInfo);
}
public void setModifierInfo(GosuBaseAttributedFeatureInfo featureInfo, IModifierInfo modifierInfo)
{
if( getOwnersType().isParameterizedType() )
{
((GosuClassTypeInfo)getOwnersType().getGenericType().getTypeInfo()).setModifierInfo( featureInfo, modifierInfo );
return;
}
if( modifierInfo == null )
{
System.out.println("ERROR: Null modifier info for: " + featureInfo);
//throw new NullPointerException( "modifierInfo is null" );
}
_modifierInfoByFeature.put(featureInfo, modifierInfo);
}
private class MyFeatureManager extends FeatureManager<String>
{
public MyFeatureManager() {
super(GosuClassTypeInfo.this, true);
}
public void forceInit() {
super.maybeInitConstructors();
super.maybeInitMethods();
super.maybeInitProperties();
getAnnotations();
}
@Override
protected IType convertType(IType type) {
if( type instanceof IErrorType) {
return type;
}
type = IGosuClassInternal.Util.getGosuClassFrom(type);
return type == null ? ErrorType.getInstance() : type;
}
protected void addEnhancementMethods(List<IMethodInfo> privateMethods) {
IType type = _gsClass;
if (_gsClass.isProxy()) {
type = _gsClass.getJavaType();
}
List<IType> parentTypes = new ArrayList<IType>();
IType atype = type;
while( atype != null )
{
parentTypes.add( atype );
atype = atype.getSupertype();
}
for( IType parentType : parentTypes )
{
CommonServices.getEntityAccess().addEnhancementMethods(parentType, privateMethods );
IType[] list = parentType.getInterfaces();
for( IType ifaceType : list )
{
if (!TypeSystem.isDeleted(ifaceType)) {
CommonServices.getEntityAccess().addEnhancementMethods(ifaceType, privateMethods );
}
}
}
if (!(_gsClass instanceof IGosuEnhancement)) {
CommonServices.getEntityAccess().addEnhancementMethods(JavaTypes.OBJECT(), privateMethods);
}
}
protected void addEnhancementProperties( PropertyNameMap<String> privateProps, boolean caseSensitive )
{
IType type = _gsClass;
if( _gsClass.isProxy() )
{
type = _gsClass.getJavaType();
}
List<IType> parentTypes = new ArrayList<IType>();
IType atype = type;
while( atype != null )
{
parentTypes.add( atype );
atype = atype.getSupertype();
}
for( IType parentType : parentTypes )
{
CommonServices.getEntityAccess().addEnhancementProperties( parentType, privateProps, true );
IType[] list = parentType.getInterfaces();
for( IType ifaceType : list )
{
CommonServices.getEntityAccess().addEnhancementProperties( ifaceType, privateProps, true );
}
}
if( !(_gsClass instanceof IGosuEnhancement) )
{
CommonServices.getEntityAccess().addEnhancementProperties( JavaTypes.OBJECT(), privateProps, true );
}
}
}
public String toString() {
return getOwnersType().getName();
}
}