/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.parser;
import gw.lang.parser.TypeVarToTypeMap;
import gw.lang.reflect.*;
import gw.lang.reflect.module.IModule;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
/**
* This is what the proxy methods look like.
*
*<pre><b>
*public ITypeLoader getTypeLoader() {
* _reload();
* IType itype;
* try {
* itype = (IType) _getType();
* } catch (ClassCastException classcastexception) {
* throw new RuntimeException((new StringBuilder("Type interface changed. Expected gw.internal.gosu.parser.IGosuClassInternal for ")).append(_getTypeNameInternal()).toString(), classcastexception);
* }
* return (ITypeLoader) itype.getTypeLoader();
*}
*</b></pre>
*/
public abstract class AbstractTypeRef implements Serializable, ITypeRef
{
transient private String _typeName;
transient volatile private IType _type;
transient private IModule _module;
transient private ITypeLoader _loader;
transient private String _pureGenericTypeName;
transient private IType _componentType;
transient private IType[] _typeParameters;
transient private boolean _bParameterized;
transient private int _mdChecksum;
transient private volatile int _hashCode = Integer.MIN_VALUE;
transient volatile private boolean _bStale;
transient volatile private boolean _bReloading;
transient private boolean _deleted;
transient private boolean _bReloadable = true;
private void writeObject(ObjectOutputStream os) throws IOException {
os.writeObject(_getTypeName());
os.writeBoolean(_bParameterized);
os.writeObject(_componentType == null ? null : _componentType.getName());
os.writeObject(_pureGenericTypeName);
os.writeInt(_typeParameters == null ? 0 : _typeParameters.length);
if (_typeParameters != null) {
for (int i = 0; i < _typeParameters.length; i++) {
os.writeObject(_typeParameters[i].getName());
}
}
}
private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException {
_module = TypeSystem.getGlobalModule();
_typeName = (String) in.readObject();
_bParameterized = in.readBoolean();
String componentName = (String) in.readObject();
if (componentName != null) {
_componentType = TypeLord.parseType(componentName, TypeVarToTypeMap.EMPTY_MAP);
}
_pureGenericTypeName = (String) in.readObject();
int paramsLength = in.readInt();
_typeParameters = new IType[paramsLength];
for (int i = 0; i < paramsLength; i++) {
final String fullyQualifiedName = (String) in.readObject();
_typeParameters[i] = TypeLord.parseType(fullyQualifiedName, TypeVarToTypeMap.EMPTY_MAP);
}
_mdChecksum = TypeSystem.getRefreshChecksum();
}
public Object readResolve() throws ObjectStreamException
{
if( _type instanceof AbstractTypeRef )
{
return _type;
}
return this;
}
void _setType( IType type )
{
if( type == null )
{
throw new TypeMayHaveBeenDeletedException( "Failed to re-resolve type " + _typeName, this );
}
if( type instanceof AbstractTypeRef )
{
throw new TypeResolveException( "Proxying a proxy is not permitted: " + _type.getClass().getName() );
}
if( type.isDiscarded() )
{
throw new TypeResolveException( type.getName() + " has been discarded and cannot be referenced" );
}
if( _type != null && _type != type )
{
_type.setDiscarded( true );
}
if( _type != type ||
_bStale ||
_mdChecksum != TypeSystem.getRefreshChecksum() )
{
TypeSystem.lock();
try
{
_type = type;
_module = type.getTypeLoader().getModule();
_loader = type.getTypeLoader();
_typeName = _type.getName();
_bParameterized = _type.isParameterizedType();
if( _bParameterized )
{
_pureGenericTypeName = TypeLord.getPureGenericType(_type).getName();
_typeParameters = _type.getTypeParameters();
}
if( _type.isArray() )
{
_componentType = _type.getComponentType();
}
_bStale = false;
_deleted = false;
_mdChecksum = TypeSystem.getRefreshChecksum();
}
finally
{
TypeSystem.unlock();
}
}
}
final public boolean isStale()
{
// Pcf fragments can't be re-resolved. If this flag is set, the type should never be reported as stale
if (!isReloadable()) {
return false;
}
if( _componentType != null && !(_componentType instanceof INonLoadableType) )
{
return false;
}
boolean bStale = false;
if( _type == null || (_mdChecksum != TypeSystem.getRefreshChecksum() /*&& !isDefaultType()*/))
{
bStale = true;
}
if( bStale || _bStale )
{
// Do NOT reload any types if we are clearing cache
bStale = !getModule().getModuleTypeLoader().getTypeRefFactory().isClearing();
}
return bStale;
}
public boolean isReloadable() {
return _bReloadable;
}
public void setReloadable(boolean bReloadable) {
_bReloadable = bReloadable;
}
public IModule getModule() {
IModule envModule = _module.getExecutionEnvironment().getModule( _module.getName() );
if (_module != null && envModule != _module) {
throw new TypeResolveException("This is rather tragic. The module once existed, now it cannot be found.\nOld module: " + _module + "\nNew module: " + envModule + "\nType: " + _typeName);
}
return envModule;
}
public ITypeLoader getTypeLoaderDirectly() {
return _loader;
}
public void _setStale(RefreshKind refreshKind)
{
// Fragments and any blocks or inner classes thereof, can't be re-resolved.
// If this flag is set, _setStale() is therefor a no-op
if (!isReloadable()) {
return;
}
if( _componentType != null &&
!(_componentType instanceof INonLoadableType) &&
refreshKind != RefreshKind.DELETION)
{
return;
}
_type = null;
_bStale = true;
if (refreshKind == RefreshKind.DELETION) {
_deleted = true;
}
}
public void unloadTypeInfo()
{
if( _type == null )
{
return;
}
_getType().unloadTypeInfo();
}
public boolean equals( Object obj )
{
if (isDeleted()) {
return this == obj;
}
_reload();
if( obj instanceof ITypeRef )
{
AbstractTypeRef ref2 = (AbstractTypeRef) obj;
return this == obj ||
// handle odd case where two separate type refs have the same type
(!ref2.isDeleted() && _getType() == ref2._getType());
}
IType type = _getType();
return _type != null && type.equals( obj );
}
public int hashCode()
{
if( _hashCode == Integer.MIN_VALUE )
{
synchronized( this )
{
if( _hashCode == Integer.MIN_VALUE )
{
int sysHash = System.identityHashCode( this );
if( sysHash == Integer.MIN_VALUE )
{
sysHash = Integer.MAX_VALUE;
}
_hashCode = sysHash;
}
}
}
return _hashCode;
}
public boolean _shouldReload()
{
return isStale();
}
final protected IType _getType()
{
checkNotDeleted();
IType type = _type;
if( type == null && !_bReloading )
{
TypeSystem.lock();
try
{
if( _type == null && !_bReloading )
{
_bReloading = true;
try
{
_resolveType();
}
finally
{
_bReloading = false;
}
}
type = _type;
}
finally
{
TypeSystem.unlock();
}
}
return type;
}
public boolean isDeleted() {
return _deleted;
}
private void checkNotDeleted() {
if (_deleted) {
throw new TypeResolveException("This type has been deleted.");
}
}
public Class<? extends IType> _getClassOfRef()
{
return _getType().getClass();
}
final protected void _reload()
{
checkNotDeleted();
if( !isStale() )
{
return;
}
TypeSystem.lock();
try
{
if( !isStale() )
{
return;
}
if( !_bReloading )
{
_bReloading = true;
try
{
_resolveType();
}
finally
{
_bReloading = false;
_bStale = false;
}
}
}
finally
{
TypeSystem.unlock();
}
}
private void _resolveType()
{
checkNotDeleted();
IType type = null;
String strType = _getTypeName();
if (strType == null) {
throw new TypeResolveException("This is rather tragic, how are we to resolve a reference to a type with no name, huh?");
}
if( strType.startsWith( ".Proxy" ) )
{
strType = "$" + strType.substring( 1 );
}
IModule module = getModule();
if( _type == null && _componentType != null )
{
TypeSystem.pushModule(module);
try {
type = _componentType.getArrayType();
} finally {
TypeSystem.popModule(module);
}
}
if( (_type == null && _bParameterized) || (_type != null && _type.isParameterizedType()) )
{
if( _type == null )
{
TypeSystem.pushModule(module);
try {
IType theType = TypeSystem.getByFullNameIfValid(_pureGenericTypeName);
if (theType == null) {
throw new TypeMayHaveBeenDeletedException("This is rather tragic. The generic type was once valid, now it cannot be found: " + _typeName, this);
}
type = theType.getParameterizedType(_typeParameters);
} finally {
TypeSystem.popModule(module);
}
}
else
{
TypeSystem.pushModule(module);
try {
type = TypeLord.getPureGenericType(_type).getParameterizedType( _type.getTypeParameters() );
} finally {
TypeSystem.popModule(module);
}
}
}
if( type == null )
{
type = TypeSystem.getByFullNameIfValid( strType, module );
}
if( type instanceof AbstractTypeRef )
{
// Note TypeRefFactory.create() reassigns the type if a type ref already exists, which must be the case here
// i.e., the fact that we are resolving in this type ref handler is proof that a type ref already exists for the type.
// So _type should already be assigned... but just to be safe...
IType rawType = ((AbstractTypeRef)type)._getType(); // Must get type irrespective of call depth because we are assigning it here
if( _type != rawType )
{
_setType( rawType );
_mdChecksum = TypeSystem.getRefreshChecksum();
}
}
else
{
_setType( type );
_mdChecksum = TypeSystem.getRefreshChecksum();
}
if( _type == null )
{
throw new TypeResolveException("This is rather tragic. The type was once valid, now it cannot be found: " + _typeName);
}
}
public String _getTypeName() {
return _type != null ? _type.getName() : _typeName;
}
public int _getIndexForSortingFast(String key) {
if(_type == null) {
return _typeName.length();
}
if(key.endsWith("]")) {
return 5;
} else {
int a = key.indexOf("<");
if(a != -1) {
return a + 2;
}
}
return key.length();
}
public boolean isDiscarded()
{
return false;
}
public void setDiscarded( boolean bDiscarded )
{
}
/**
* This method is called reflectively.
*/
public String _getTypeNameInternal() {
return _typeName;
}
public boolean isTypeRefreshedOutsideOfLock(IType type) {
return !isStale() && _type != null && _type != type;
}
}