/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.parser;
import gw.lang.reflect.*;
import gw.lang.reflect.java.IJavaType;
import gw.lang.reflect.java.JavaTypes;
import gw.util.concurrent.LockingLazyVar;
import gw.util.concurrent.LocklessLazyVar;
import java.io.ObjectStreamException;
import java.lang.reflect.Array;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
*/
public class MetaType extends AbstractType implements IMetaType
{
static class RootType{}
static class DefaultType{}
/**
* These fields need to be lazu vars to avoid bombarding the typesystem with calls
*/
private static final LocklessLazyVar<IJavaType> ROOT_TYPE =
new LocklessLazyVar<IJavaType>()
{
public IJavaType init()
{
IJavaType type = (IJavaType) TypeSystem.getByFullNameIfValid( RootType.class.getName().replace('$', '.'), TypeSystem.getGlobalModule() );
if( type == null )
{
throw new RuntimeException( "Cannot load gw.internal.gosu.parser.MetaType.RootType. The TypeSystem is not setup properly. It's highly likely Gosu is not in your classpath (perhaps via the project SDK)." );
}
return type;
}
};
static final LocklessLazyVar<IJavaType> DEFAULT_TYPE =
new LocklessLazyVar<IJavaType>()
{
public IJavaType init()
{
return (IJavaType) TypeSystem.getByFullNameIfValid( DefaultType.class.getName().replace('$', '.'), TypeSystem.getGlobalModule() );
}
};
public static LocklessLazyVar<MetaType> ROOT_TYPE_TYPE =
new LocklessLazyVar<MetaType>()
{
public MetaType init()
{
return MetaType.get( ROOT_TYPE.get() );
}
};
public static LocklessLazyVar<IType> DEFAULT_TYPE_TYPE =
new LocklessLazyVar<IType>()
{
public IType init()
{
return MetaType.get( DEFAULT_TYPE.get() );
}
};
static {
TypeSystem.addShutdownListener(new TypeSystemShutdownListener() {
public void shutdown() {
clearCaches();
}
});
}
public static void clearCaches()
{
ROOT_TYPE.clear();
ROOT_TYPE_TYPE.clear();
DEFAULT_TYPE.clear();
DEFAULT_TYPE_TYPE.clear();
}
private IType _type;
transient private Map<IRelativeTypeInfo.Accessibility, ITypeInfo> _typeInfoByAccessibility;
transient private Set<IJavaType> _allTypesInHierarchy;
transient private LockingLazyVar<IType> _arrayType;
transient private GenericTypeVariable[] _typeVars;
transient private boolean _bLiteral;
transient private IType[] _typeParams;
transient private String _strName;
transient private String _strRelativeName;
public static MetaType get( IType type )
{
return (MetaType) type.getMetaType();
}
public static MetaType getLiteral( IType type )
{
return (MetaType) type.getLiteralMetaType();
}
MetaType( IType type, boolean bLiteral )
{
_type = type;
_bLiteral = bLiteral;
_typeInfoByAccessibility = new HashMap<IRelativeTypeInfo.Accessibility, ITypeInfo>( 2 );
if( type == ROOT_TYPE.get() )
{
_typeVars = new GenericTypeVariable[]{new GenericTypeVariable("T", JavaTypes.OBJECT())};
_typeParams = IType.EMPTY_ARRAY;
}
else
{
_typeVars = GenericTypeVariable.EMPTY_TYPEVARS;
_typeParams = new IType[]{type};
}
_arrayType =
new LockingLazyVar<IType>()
{
@Override
protected IType init()
{
return new DefaultNonLoadableArrayType( MetaType.this, null, getTypeLoader() );
}
};
}
@Override
public IType getType()
{
return _type;
}
@Override
public boolean isLiteral()
{
return _bLiteral;
}
@Override
public ITypeLoader getTypeLoader()
{
return _type.getTypeLoader();
}
@Override
public String getName()
{
if( _strName == null )
{
_strName = "Type" + (isGenericType() ? "" : "<" + _type.getName() + ">");
}
return _strName;
}
@Override
public String getDisplayName()
{
return getName();
}
@Override
public String getRelativeName()
{
if( _strRelativeName == null )
{
_strRelativeName =
isGenericType() || isDefault()
? "Type"
: "Type<" + _type.getRelativeName() + ">";
}
return _strRelativeName;
}
private boolean isDefault()
{
return
_type instanceof MetaType
? ((MetaType)_type).isDefault()
: _type == DEFAULT_TYPE.get();
}
@Override
public String getNamespace()
{
return isGenericType() ? "" : _type.getNamespace();
}
@Override
public boolean isArray()
{
return false;
}
@Override
public boolean isPrimitive()
{
return false;
}
@Override
public Object makeArrayInstance( int iLength )
{
return JavaTypes.ITYPE().makeArrayInstance( iLength );
}
/**
* MetaTypes are assignable if:
* <ul>
* <li> they are both arrays and their component types are assignable
* <li> the are not arrays and:
* <li> - one of them is not a MetaType and instead implements IType
* <li> - or - one of them is the DEFAULT_TYPE
* <li> - or - one of them is the ROOT_TYPE
* <li> - or - their core types are assignable
* </ul>
*/
@Override
public boolean isAssignableFrom( IType type )
{
if( isArray() && type.isArray() )
{
return getComponentType().isAssignableFrom( type.getComponentType() );
}
return !(isArray() || type.isArray()) && type.getAllTypesInHierarchy().contains( JavaTypes.ITYPE() ) &&
(!(type instanceof IMetaType) ||
getType().equals( DEFAULT_TYPE.get() ) ||
((IMetaType)type).getType().equals( DEFAULT_TYPE.get() ) ||
getType().equals( ROOT_TYPE.get() ) ||
((IMetaType)type).getType().equals( ROOT_TYPE.get() ) ||
getType().isAssignableFrom( ((IMetaType)type).getType() ));
}
/**
* Instance of this type (e.g., other types) are immutable.
*/
@Override
public boolean isMutable()
{
return false;
}
@Override
public ITypeInfo getTypeInfo()
{
return loadTypeInfo();
}
/**
* @return One of four possible typeinfos (public, protected, package, private),
* depending on the accessibility of the enclosing type of the call site.
*/
private ITypeInfo loadTypeInfo()
{
IRelativeTypeInfo.Accessibility context =
_type.getTypeInfo() instanceof IRelativeTypeInfo
? ((IRelativeTypeInfo)_type.getTypeInfo()).getAccessibilityForType( null )
: null;
ITypeInfo typeInfo = _typeInfoByAccessibility.get( context );
if( typeInfo == null )
{
typeInfo = new MetaTypeTypeInfo( this );
_typeInfoByAccessibility.put( context, typeInfo );
}
return typeInfo;
}
@Override
public void unloadTypeInfo()
{
_typeInfoByAccessibility.clear();
}
@Override
public boolean isInterface()
{
return false;
}
@Override
public boolean isEnum()
{
return false;
}
@Override
public IType[] getInterfaces()
{
return EMPTY_TYPE_ARRAY;
}
@Override
public IType getSupertype()
{
return null;
}
@Override
public IType getEnclosingType()
{
return null;
}
@Override
public IType getGenericType()
{
return ROOT_TYPE_TYPE.get();
}
@Override
public boolean isFinal()
{
return true;
}
@Override
public boolean isParameterizedType()
{
return _type != ROOT_TYPE.get();
}
@Override
public boolean isGenericType()
{
return _type == ROOT_TYPE.get();
}
@Override
public GenericTypeVariable[] getGenericTypeVariables()
{
return _typeVars;
}
@Override
public IType[] getTypeParameters()
{
return _typeParams;
}
@Override
public IType getParameterizedType( IType... ofType )
{
return get( ofType[0] );
}
@Override
public Set<? extends IType> getAllTypesInHierarchy()
{
if( _allTypesInHierarchy == null )
{
//noinspection unchecked,RedundantCast
IType type = TypeSystem.get(getType().getClass(), TypeSystem.getGlobalModule());
_allTypesInHierarchy = (Set)getTypeInterfaces(type, new HashSet<IType>() );
}
return _allTypesInHierarchy;
}
private Set<IType> getTypeInterfaces( IType type, Set<IType> set )
{
if( getType() == DEFAULT_TYPE.get() )
{
return Collections.singleton( (IType)JavaTypes.ITYPE() );
}
for( IType iface : type.getInterfaces() )
{
iface = TypeLord.getDefaultParameterizedType( iface );
if( !iface.getNamespace().startsWith( "gw.internal" ) )
{
set.add( iface );
}
getTypeInterfaces( iface, set );
}
return set;
}
@Override
public IType getArrayType()
{
return _arrayType.get();
}
@Override
public Object getArrayComponent( Object array, int iIndex ) throws IllegalArgumentException, ArrayIndexOutOfBoundsException
{
return Array.get( array, iIndex );
}
@Override
public void setArrayComponent( Object array, int iIndex, Object value ) throws IllegalArgumentException, ArrayIndexOutOfBoundsException
{
Array.set( array, iIndex, value );
}
@Override
public int getArrayLength( Object array ) throws IllegalArgumentException
{
return Array.getLength( array );
}
@Override
public IType getComponentType()
{
return null;
}
@Override
public Object readResolve() throws ObjectStreamException
{
return get( _type );
}
@Override
public boolean isValid()
{
return true;
}
@Override
public int getModifiers()
{
return Modifier.PUBLIC;
}
@Override
public boolean isAbstract()
{
return false;
}
@Override
public boolean equals( Object o )
{
return o instanceof MetaType && this._type.equals( ((MetaType)o)._type );
}
@Override
public int hashCode()
{
return _type.hashCode();
}
@Override
public String toString()
{
return getName();
}
@Override
public boolean isDiscarded()
{
return false;
}
@Override
public void setDiscarded( boolean bDiscarded )
{
}
@Override
public boolean isCompoundType()
{
return false;
}
@Override
public Set<IType> getCompoundTypeComponents()
{
return null;
}
}