/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.parser;
import gw.config.CommonServices;
import gw.fs.IFile;
import gw.internal.gosu.annotations.AnnotationMap;
import gw.lang.parser.TypeVarToTypeMap;
import gw.lang.reflect.AbstractType;
import gw.lang.reflect.IErrorType;
import gw.lang.reflect.IType;
import gw.lang.reflect.ITypeInfo;
import gw.lang.reflect.ITypeRef;
import gw.lang.reflect.TypeSystem;
import gw.lang.reflect.gs.ClassType;
import gw.lang.reflect.gs.IGosuClass;
import gw.lang.reflect.gs.IGosuFragment;
import gw.lang.reflect.gs.ISourceFileHandle;
import gw.lang.reflect.java.IJavaBackedTypeData;
import gw.lang.reflect.java.IJavaClassInfo;
import gw.lang.reflect.java.IJavaClassType;
import gw.lang.reflect.java.IJavaType;
import gw.lang.reflect.java.JavaTypes;
import gw.lang.reflect.module.IModule;
import gw.util.GosuClassUtil;
import gw.util.concurrent.LockingLazyVar;
import gw.util.perf.objectsize.IObjectSizeFilter;
import gw.util.perf.objectsize.ObjectSize;
import gw.util.perf.objectsize.ObjectSizeUtil;
import gw.util.perf.objectsize.UnmodifiableArraySet;
import java.beans.MethodDescriptor;
import java.io.File;
import java.io.InvalidClassException;
import java.io.ObjectStreamException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
*/
class JavaType extends AbstractType implements IJavaTypeInternal
{
//
// Persistent fields. See readResolve()
//
private String _strName;
private IType[] _typeParams;
//
// Non-persistent fields
//
transient protected IJavaClassInfo _classInfo;
transient private ITypeInfo _typeInfo;
transient private String _strRelativeName;
transient volatile private Set<IType> _allTypesInHierarchy; //!! Do NOT make this a lazy var, it's init needs to be re-entrant
transient private boolean _bArray;
transient private boolean _bPrimitive;
transient private LockingLazyVar<Boolean> _bHasSuperType = new LockingLazyVar<Boolean>() {
@Override
protected Boolean init() {
if(_classInfo != null) {
return _classInfo.getSuperclass() != null &&
//## hack: avoid cyclic refs from creating AnnotationClass
!_classInfo.getName().equals( AnnotationMap.class.getName() );
} else {
return _classInfo.getSuperclass() != null;
}
}
};
transient volatile private IType _superType; //!! Do NOT make this a lazy var, it's init needs to be re-entrant
transient private List<IType> _tempInterfaces;
transient volatile private IType[] _interfaces; //!! Do NOT make this a lazy var, it's init needs to be re-entrant
transient private IGosuClassInternal _adapterClass;
transient private GenericTypeVariable[] _tempGenericTypeVars;
transient private LockingLazyVar<GenericTypeVariable[]> _lazyGenericTypeVars = new LockingLazyVar<GenericTypeVariable[]>() {
protected GenericTypeVariable[] init() {
return assignGenericTypeVariables();
}
};
transient private boolean _bDefiningGenericTypes;
transient private ConcurrentMap<String, IJavaTypeInternal> _parameterizationByParamsName;
transient volatile private IJavaTypeInternal _arrayType;
private IJavaTypeInternal _componentType;
transient private DefaultTypeLoader _typeLoader;
transient private boolean _bDoesNotHaveExplicitTypeInfo;
private Class<?> _explicitTypeInfoClass;
transient private boolean _bDiscarded;
private IJavaTypeInternal _typeRef;
transient private int _tiChecksum;
public static IJavaTypeInternal get( Class cls, DefaultTypeLoader loader )
{
IJavaType type = TYPES_BY_CLASS.get( cls );
if( type == null || ((ITypeRef)type)._shouldReload() )
{
TypeSystem.lock();
try
{
type = TYPES_BY_CLASS.get( cls );
if( type == null || ((ITypeRef)type)._shouldReload() )
{
type = define( cls, loader );
}
}
finally
{
TypeSystem.unlock();
}
}
return (IJavaTypeInternal)type;
}
public static IJavaType getPrimitiveType( String strPrimitiveClassName )
{
return TypeLoaderAccess.PRIMITIVE_TYPES_BY_NAME.get().get(strPrimitiveClassName);
}
private static IJavaTypeInternal define( Class cls, DefaultTypeLoader loader )
{
IJavaTypeInternal type;
if( cls.isEnum() )
{
JavaType rawType = new JavaEnumType( new ClassJavaClassInfo(cls, loader.getModule()), loader );
type = (IJavaTypeInternal)TypeSystem.getOrCreateTypeReference( rawType );
rawType._typeRef = type;
}
else
{
JavaType rawType = new JavaType( new ClassJavaClassInfo(cls, loader.getModule()), loader );
type = (IJavaTypeInternal)TypeSystem.getOrCreateTypeReference( rawType );
rawType._typeRef = type;
}
TYPES_BY_CLASS.put( cls, type );
return type;
}
public static IJavaTypeInternal create(IJavaClassInfo cls, DefaultTypeLoader loader)
{
IJavaTypeInternal type;
if( cls.isEnum() )
{
JavaType rawType = new JavaEnumType( cls, loader );
type = (IJavaTypeInternal)TypeSystem.getOrCreateTypeReference( rawType );
rawType._typeRef = type;
}
else
{
JavaType rawType = new JavaType( cls, loader );
type = (IJavaTypeInternal)TypeSystem.getOrCreateTypeReference( rawType );
rawType._typeRef = type;
}
return type;
}
public boolean isDefiningGenericTypes()
{
return _bDefiningGenericTypes;
}
public GenericTypeVariable[] assignGenericTypeVariables()
{
if( _typeParams != null && _typeParams.length > 0 )
{
return assignTypeVarsFromTypeParams( _typeParams );
}
else if( _classInfo.getTypeParameters().length > 0 )
{
_bDefiningGenericTypes = true;
try
{
assignGenericTypeVarPlaceholders();
TypeVarToTypeMap actualParamByVarName = TypeLord.mapTypeByVarName( thisRef(), thisRef() );
return GenericTypeVariable.convertTypeVars( thisRef(), _classInfo.getTypeParameters(), actualParamByVarName );
}
finally
{
_bDefiningGenericTypes = false;
}
}
else
{
return GenericTypeVariable.EMPTY_TYPEVARS;
}
}
private void assignGenericTypeVarPlaceholders()
{
_tempGenericTypeVars = new GenericTypeVariable[_classInfo.getTypeParameters().length];
final int n = _classInfo.getTypeParameters().length;
for( int i = 0; i < n; i++ )
{
_tempGenericTypeVars[i] = new GenericTypeVariable( String.valueOf( (char)('A' + i) ), JavaTypes.OBJECT() );
}
}
public JavaType(IJavaClassInfo cls, DefaultTypeLoader loader) {
init(cls, loader);
_strName = computeQualifiedName();
}
private void init(IJavaClassInfo cls, DefaultTypeLoader loader) {
_classInfo = cls;
_bArray = cls.isArray();
_bPrimitive = cls.isPrimitive();
_typeLoader = loader;
_tiChecksum = TypeSystem.getSingleRefreshChecksum();
_strName = computeQualifiedName();
}
JavaType( Class cls, DefaultTypeLoader loader )
{
init(TypeSystem.getJavaClassInfo(cls, loader.getModule()), loader);
_strName = computeQualifiedName();
}
private JavaType( IJavaClassInfo cls, IType[] typeParams, DefaultTypeLoader loader )
{
init(cls, loader);
_typeParams = typeParams;
_strName = computeQualifiedName();
}
private JavaType( IJavaClassInfo arrayClass, IJavaTypeInternal componentType, DefaultTypeLoader loader )
{
init(arrayClass, loader);
_componentType = componentType;
_strName = computeQualifiedName();
}
public JavaType(IJavaClassInfo classInfo, DefaultTypeLoader loader, IType[] typeParams) {
init(classInfo, loader);
_typeParams = typeParams;
_strName = computeQualifiedName();
}
/**
* Note a gosu class can be BOTH parameterzied AND generic. For example,
* <p/>
* class Bar<T> {
* function blah() : T {...}
* }
* class Foo<T extends CharSequence> extends Bar<T> {}
* <p/>
* The class Bar<T> here is parameterized by the type var from Foo, yet it is
* still a generic class. The blah() method in Foo's typeinfo must have a
* return type consistent with Foo's type var upper bound, CharSequence.
*
* //## todo: maybe we don't need this concept any longer? i.e., parameterization should work correctly regardless.
* @param typeParams type parameters
* @return generic type variables
*/
private GenericTypeVariable[] assignTypeVarsFromTypeParams( IType[] typeParams )
{
List<GenericTypeVariable> genTypeVars = new ArrayList<GenericTypeVariable>();
for( IType typeParam : typeParams )
{
if( typeParam instanceof TypeVariableType )
{
genTypeVars.add( (GenericTypeVariable)((TypeVariableType)typeParam).getTypeVarDef().getTypeVar() );
}
}
return genTypeVars.toArray( new GenericTypeVariable[genTypeVars.size()] );
}
public DefaultTypeLoader getTypeLoader()
{
return _typeLoader;
}
public String getName()
{
return _strName;
}
String computeQualifiedName() {
if (isArray()) {
IType cType = getComponentType();
return cType.getName() + "[]";
} else {
String strName = _classInfo.getName().replace('$', '.');
if (isParameterizedType()) {
strName += TypeLord.getNameOfParams(_typeParams, false, false);
}
return strName;
}
}
public String getDisplayName()
{
return getName();
}
public String getRelativeName()
{
if( _strRelativeName != null )
{
return _strRelativeName;
}
if( isArray() )
{
return getComponentType().getRelativeName() + "[]";
}
String strQualifiedClassName = _classInfo.getName();
int iDotIndex = strQualifiedClassName.lastIndexOf( '.' );
String strRelativeName = strQualifiedClassName.substring( iDotIndex + 1 ).replace( '$', '.' );
if( isParameterizedType() )
{
strRelativeName += TypeLord.getNameOfParams( _typeParams, true, false );
}
return _strRelativeName = strRelativeName;
}
public String getNamespace()
{
return _classInfo.getNamespace();
}
public boolean isArray()
{
return _bArray;
}
public boolean isPrimitive()
{
return _bPrimitive;
}
public Object makeArrayInstance( int iLength )
{
Class intrinsicClass = getIntrinsicClass();
if (intrinsicClass == null) {
int i = 0;
}
return Array.newInstance(intrinsicClass, iLength );
}
public boolean isAssignableFrom( IType type )
{
if (TypeSystem.isDeleted(type)) {
return false;
}
IType pThis = thisRef();
// Short-circuit if the types are the same
if( type == pThis )
{
return true;
}
// Short-circuit if the given type is null
if( type == null )
{
return false;
}
// Everything is assignable from Object, except for primitives
if( getBackingClassInfo().getName().equals( Object.class.getName() ) )
{
return type == JavaTypes.pVOID() || !type.isPrimitive();
}
if( isArray() && type.isArray() )
{
// Note an array of primitives and an array of non-primitives are never assignable
return getComponentType().isPrimitive() == type.getComponentType().isPrimitive() &&
getComponentType().isAssignableFrom( type.getComponentType() );
}
if( isArray() || type.isArray() )
{
return false;
}
if( isInterface() && type instanceof IGosuClass && ((IGosuClass)type).isStructure() )
{
// A structure *not* implicitly assignable to an interface because there is no hierarchy, just a structure
// (force an explicit cast if the runtime type is expected to directly implement the interface)
return false;
}
final Set<? extends IType> allTypesInHierarchy = type.getAllTypesInHierarchy();
@SuppressWarnings({"SuspiciousMethodCalls"})
boolean isAssignable = allTypesInHierarchy != null && allTypesInHierarchy.contains(pThis);
if( !isAssignable )
{
isAssignable = TypeLord.areGenericOrParameterizedTypesAssignable( pThis, type );
}
return isAssignable || isAssignableFromJavaBackedType(type);
}
private boolean isAssignableFromJavaBackedType(IType type) {
//HACK! for now we assume that the entity info is class-based so we only
// do this if this class info is also class-based
boolean isAssignable = false;
if( !(type instanceof IJavaTypeInternal) && type instanceof IJavaBackedTypeData && !(type instanceof IGosuFragment))
{
// Handle case e.g., where we are comparing an entity type with a java type,
// we want the backing class of the entity to be assignable to the java class
// if the java classes are assignable
IJavaClassInfo backingClassInfo = ((IJavaBackedTypeData) type).getBackingClassInfo();
isAssignable = backingClassInfo != null && getBackingClassInfo().isAssignableFrom(backingClassInfo);
}
return isAssignable;
}
/**
* Note eventhough some classes are indeed immutable (e.g. java.lang.String)
* there's no such info in a java class, so we default to mutable.
*/
public boolean isMutable()
{
return true;
}
public Class getIntrinsicClass()
{
return _classInfo.getBackingClass();
}
@Override
public IJavaClassInfo getBackingClassInfo() {
return _classInfo;
}
public Class getBackingClass()
{
return getIntrinsicClass();
}
public ITypeInfo getTypeInfo()
{
ITypeInfo typeInfo = _typeInfo;
if( typeInfo == null || hasAncestorBeenUpdated() )
{
TypeSystem.lock();
try
{
typeInfo = _typeInfo;
if( typeInfo == null || hasAncestorBeenUpdated() )
{
typeInfo = loadTypeInfo();
_typeInfo = typeInfo;
if( !isParameterizedType() && _adapterClass != null &&
haveAncestorsBeenUpdated(this, _adapterClass.getTypeInfoChecksum(), new HashSet<IType>()) )
{
_adapterClass = createAdapterClass();
}
_tiChecksum = TypeSystem.getSingleRefreshChecksum();
}
}
finally
{
TypeSystem.unlock();
}
}
return typeInfo;
}
public IGosuClassInternal getAdapterClass()
{
if( isParameterizedType() )
{
return getGenericType().getAdapterClass();
}
if( _adapterClass == null )
{
return null;
}
if( _adapterClass.isStale())
{
_adapterClass = createAdapterClass();
return _adapterClass;
}
// must check against adapter's checksum
if( haveAncestorsBeenUpdated( this, _adapterClass.getTypeInfoChecksum(), new HashSet<IType>() ) )
{
_typeInfo = null; // clear the typeinfo
_tiChecksum = TypeSystem.getSingleRefreshChecksum(); // and update the checksum (the type info will be created on next request)
_adapterClass = createAdapterClass(); // recreate the adapter class
}
return _adapterClass;
}
public IGosuClassInternal getAdapterClassDirectly()
{
return _adapterClass;
}
private ITypeInfo loadTypeInfo()
{
ITypeInfo typeInfo = getExplicitTypeInfo();
if( typeInfo == null )
{
typeInfo = convertToTypeInfo();
}
return typeInfo;
}
public void unloadTypeInfo()
{
TypeSystem.lock();
try
{
if( _parameterizationByParamsName != null )
{
for( IJavaType parameteredClass : new ArrayList<IJavaType>(_parameterizationByParamsName.values()) )
{
parameteredClass.unloadTypeInfo();
}
_parameterizationByParamsName.clear();
}
if( _adapterClass != null )
{
_adapterClass.unloadTypeInfo();
}
if( _arrayType != null )
{
_arrayType.unloadTypeInfo();
}
_lazyGenericTypeVars.clearNoLock();
_allTypesInHierarchy = null;
_typeInfo = null;
}
finally
{
TypeSystem.unlock();
}
}
public boolean isInterface()
{
return _classInfo.isInterface();
}
public boolean isEnum()
{
return _classInfo.isEnum();
}
public IType[] getInterfaces()
{
if( _interfaces != null )
{
return _interfaces;
}
TypeSystem.lock();
try
{
if( _interfaces != null )
{
return _interfaces;
}
boolean bReentered = _tempInterfaces != null;
if( !bReentered )
{
_tempInterfaces = new ArrayList<IType>();
}
ArrayList<IType> interfaces;
IJavaClassType[] genericInterfaces = _classInfo.getGenericInterfaces();
if( genericInterfaces.length == 0 )
{
interfaces = EMPTY_TYPE_LIST;
}
else
{
IJavaClassInfo[] notParameterizedInterfaces = _classInfo.getInterfaces();
if (notParameterizedInterfaces.length != genericInterfaces.length) {
throw new RuntimeException("There should be as many generic interface as non-parameterized interfaces.");
}
interfaces = new ArrayList<IType>( genericInterfaces.length );
for( int i = _tempInterfaces.size(); i < genericInterfaces.length; i++ )
{
IJavaClassType interfaceType = genericInterfaces[i];
IType notParameterizedInterface = notParameterizedInterfaces[i].getJavaType();
if (notParameterizedInterface == null) {
throw new NullPointerException("notParameterizedInterface java type is null for " + notParameterizedInterfaces[i]);
}
_tempInterfaces.add( notParameterizedInterface );
if( !isParameterizedType() && interfaceType instanceof IJavaClassInfo )
{
interfaces.add( notParameterizedInterface );
}
else
{
TypeVarToTypeMap actualParamByVarName = TypeLord.mapTypeByVarName( thisRef(), thisRef(), true );
if( actualParamByVarName.isEmpty() )
{
actualParamByVarName = TypeLord.mapTypeByVarName( notParameterizedInterface, notParameterizedInterface, true );
}
IType parameterizedIface = interfaceType.getActualType( actualParamByVarName, true );
interfaces.add( parameterizedIface );
_tempInterfaces.remove( notParameterizedInterface );
_tempInterfaces.add( parameterizedIface );
}
}
}
if( bReentered )
{
return _tempInterfaces.toArray(new IType[_tempInterfaces.size()]);
}
_tempInterfaces = null;
interfaces.trimToSize();
_interfaces = interfaces.toArray(new IType[interfaces.size()]);
}
finally
{
TypeSystem.unlock();
}
return _interfaces;
}
public IType getSupertype()
{
if( !_bHasSuperType.get() )
{
return null;
}
if( _superType != null )
{
return notDeletedSupertype();
}
TypeSystem.lock();
try
{
if( _superType != null )
{
return notDeletedSupertype();
}
IJavaClassInfo superclass = _classInfo.getSuperclass();
IJavaType notParameterizedSuperType;
if(superclass != null) {
IType superType = superclass.getJavaType();
if (superType instanceof IErrorType) {
return TypeSystem.getErrorType();
}
notParameterizedSuperType = (IJavaType) superType;
} else if(_classInfo != null) {
notParameterizedSuperType = (IJavaType) TypeSystem.get(_classInfo.getSuperclass());
} else {
// ?
notParameterizedSuperType = JavaTypes.OBJECT();
}
// Temporarily set supertype to raw type to short-circuit re-entrant calls to this method (for recursive types)
_superType = notParameterizedSuperType;
IJavaClassType genericSuperclass = _classInfo.getGenericSuperclass();
if( genericSuperclass instanceof IJavaClassInfo )
{
// Super is not generic, we're done
return notDeletedSupertype();
}
// Get fully parameterized version of generic supertype...
TypeVarToTypeMap actualParamByVarName = TypeLord.mapTypeByVarName( thisRef(), thisRef(), true );
if( actualParamByVarName.isEmpty() )
{
actualParamByVarName = TypeLord.mapTypeByVarName( notParameterizedSuperType, notParameterizedSuperType, true );
}
if(genericSuperclass != null) {
_superType = genericSuperclass.getActualType( actualParamByVarName, true );
}
}
finally
{
TypeSystem.unlock();
}
return notDeletedSupertype();
}
private IType notDeletedSupertype() {
if (TypeSystem.isDeleted(_superType)) {
_superType = TypeSystem.getErrorType();
}
return _superType;
}
public List<IJavaType> getInnerClasses()
{
IJavaClassInfo[] innerClasses = getBackingClassInfo().getDeclaredClasses();
if( innerClasses.length == 0 )
{
return Collections.emptyList();
}
List<IJavaType> inners = new ArrayList<IJavaType>( 2 );
for( IJavaClassInfo c : innerClasses )
{
String name = c.getName().replace('$', '.');
IJavaType inner = _typeLoader.getInnerType(name);
if (inner == null) {
throw new IllegalStateException("Cannot load inner class " + name);
}
inners.add(inner);
}
return inners;
}
public IJavaType getInnerClass( CharSequence strTypeName )
{
List<String> innerClassNames = Arrays.asList( strTypeName.toString().split( "\\." ) );
IJavaType enclosingType = this;
for( Iterator<String> it = innerClassNames.iterator(); it.hasNext(); )
{
String name = it.next();
for( IJavaType javaType : enclosingType.getInnerClasses() )
{
if( GosuClassUtil.getNameNoPackage( javaType.getRelativeName() ).equals( name ) )
{
if( it.hasNext() )
{
enclosingType = javaType;
}
else
{
return javaType;
}
}
}
}
return null;
}
@Override
public ISourceFileHandle getSourceFileHandle() {
return _classInfo.getSourceFileHandle();
}
@Override
public List<? extends IType> getLoadedInnerClasses() {
return getInnerClasses();
}
@Override
public ClassType getClassType() {
return ClassType.JavaClass;
}
public IType getEnclosingType()
{
return _classInfo.getEnclosingType();
}
public IJavaTypeInternal getGenericType()
{
if( isParameterizedType() )
{
return (IJavaTypeInternal)TypeSystem.get( _classInfo, getTypeLoader().getModule() );
}
else if( isGenericType() )
{
return thisRef();
}
else
{
return null;
}
}
public boolean isFinal()
{
return Modifier.isFinal( _classInfo.getModifiers() );
}
public boolean isParameterizedType()
{
return _typeParams != null && _typeParams.length > 0;
}
public boolean isGenericType()
{
return _bDefiningGenericTypes || (_lazyGenericTypeVars.get() != null && _lazyGenericTypeVars.get().length > 0);
}
public GenericTypeVariable[] getGenericTypeVariables()
{
if( !isGenericType() )
{
return null;
}
if (_bDefiningGenericTypes) {
return _tempGenericTypeVars;
}
return _lazyGenericTypeVars.get();
}
public IType[] getTypeParameters()
{
return _typeParams;
}
public IType getParameterizedType( IType... paramTypes )
{
if( paramTypes == null || paramTypes.length == 0 )
{
throw new IllegalArgumentException( "Parameter types required." );
}
if( isParameterizedType() )
{
return TypeLord.getPureGenericType( thisRef() ).getParameterizedType( paramTypes );
}
if( _bDefiningGenericTypes )
{
// If this type references itself in its type variables, we kinda have to
// bail out and return the generic type. This can actually happen; behold
// java.lang.Enum...
// public abstract class Enum<E extends Enum<E>>
// Here the Enum<E> references Enum, which is itself, so we just return this
// generic version, which is compatible with any parameterization of itself.
// Essentially this is the same as Enum<E extends Enum>, which not only makes
// more sense, but more importantly avoids the cyclic reference.
return thisRef();
}
// if( !isGenericType() )
// {
// throw new IllegalStateException( "Cannot parameterize non-generic type: " + getName() );
// }
if( _parameterizationByParamsName == null )
{
TypeSystem.lock();
try
{
if( _parameterizationByParamsName == null )
{
_parameterizationByParamsName = new ConcurrentHashMap<String, IJavaTypeInternal>( 2 );
}
}
finally
{
TypeSystem.unlock();
}
}
paramTypes = TypeSystem.boxPrimitiveTypeParams( paramTypes );
String strNameOfParams = TypeLord.getNameOfParams( paramTypes, false, true, true );
IJavaTypeInternal parameterizedType = _parameterizationByParamsName.get( strNameOfParams );
if( parameterizedType == null )
{
TypeSystem.lock();
try
{
parameterizedType = _parameterizationByParamsName.get( strNameOfParams );
if( parameterizedType == null )
{
if( _classInfo != null )
{
parameterizedType = (IJavaTypeInternal)TypeSystem.getOrCreateTypeReference( new JavaType( _classInfo, paramTypes, _typeLoader ) );
}
else
{
parameterizedType = (IJavaTypeInternal)TypeSystem.getOrCreateTypeReference( new JavaType( _classInfo, getTypeLoader(), paramTypes ) );
}
_parameterizationByParamsName.put( strNameOfParams, parameterizedType );
}
}
finally
{
TypeSystem.unlock();
}
}
return parameterizedType;
}
public Set<IType> getAllTypesInHierarchy()
{
if( _allTypesInHierarchy != null )
{
return _allTypesInHierarchy;
}
TypeSystem.lock();
try
{
if( _allTypesInHierarchy != null )
{
return _allTypesInHierarchy;
}
if( _classInfo.isArray() )
{
_allTypesInHierarchy = new UnmodifiableArraySet<IType>(TypeLord.getArrayVersionsOfEachType(getComponentType().getAllTypesInHierarchy()));
}
else
{
_allTypesInHierarchy = TypeLord.getAllClassesInClassHierarchyAsIntrinsicTypes( _classInfo );
Set<IType> includeGenericTypes = new HashSet<IType>( _allTypesInHierarchy );
if( isGenericType() || isParameterizedType() )
{
TypeLord.addAllClassesInClassHierarchy( thisRef(), includeGenericTypes, true );
}
_allTypesInHierarchy = new UnmodifiableArraySet<IType>(includeGenericTypes);
}
}
finally
{
TypeSystem.unlock();
}
return _allTypesInHierarchy;
}
private IJavaTypeInternal thisRef()
{
if (_typeRef == null) {
ITypeRef ref = TypeSystem.getOrCreateTypeReference(this);
try {
_typeRef = (IJavaTypeInternal) ref;
} catch(ClassCastException e) {
throw new RuntimeException("Could not reresolve the java type " + this.getName() + ".\n" +
"The actual type of the reference was " + ref.getClass(), e);
}
}
return _typeRef;
}
public IType getArrayType()
{
if( _arrayType == null )
{
TypeSystem.lock();
try
{
if( _arrayType == null )
{
IJavaTypeInternal thisRef = thisRef();
IModule module = getTypeLoader().getModule();
if( module != null )
{
TypeSystem.pushModule( getTypeLoader().getModule() );
}
try
{
boolean bParameterized = TypeLord.getCoreType( thisRef ).isParameterizedType();
if(_classInfo != null) {
IJavaClassInfo arrayClass = _classInfo.getArrayType();
if( bParameterized )
{
_arrayType = new JavaType( arrayClass, thisRef, _typeLoader ).thisRef();
}
else
{
_arrayType = create( arrayClass, _typeLoader );
_arrayType.setComponentType( thisRef );
}
} else {
if(bParameterized) {
_arrayType = new JavaType(_classInfo.getArrayType(), getTypeLoader(), getTypeParameters()).thisRef();
} else {
_arrayType = new JavaType(_classInfo.getArrayType(), getTypeLoader()).thisRef();
}
}
}
finally
{
if( module != null )
{
TypeSystem.popModule( module );
}
}
}
}
finally
{
TypeSystem.unlock();
}
}
return _arrayType;
}
public Object getArrayComponent( Object array, int iIndex ) throws IllegalArgumentException, ArrayIndexOutOfBoundsException
{
return Array.get( array, iIndex );
}
public void setArrayComponent( Object array, int iIndex, Object value ) throws IllegalArgumentException, ArrayIndexOutOfBoundsException
{
try {
Array.set( array, iIndex, value );
} catch (IllegalArgumentException ex) {
HashSet<Class> interfaces = new HashSet<Class>();
if (value != null) {
getInterfaces(value.getClass(), interfaces);
}
throw new IllegalArgumentException("array element type mismatch. Expected an element of type " +
(array == null ? "<null>" : array.getClass()) + " got " +
interfaces);
}
}
private void getInterfaces(Class clazz, Set<Class> classes) {
if (clazz == null || classes.contains(clazz)) {
return;
}
classes.add(clazz);
getInterfaces(clazz.getSuperclass(), classes);
for (Class aClass : clazz.getInterfaces()) {
getInterfaces(aClass, classes);
}
}
public int getArrayLength( Object array ) throws IllegalArgumentException
{
return Array.getLength( array );
}
public IType getComponentType()
{
IModule module = getTypeLoader().getModule();
TypeSystem.pushModule( module );
try
{
if (isArray()) {
if (_componentType == null) {
IType type = TypeSystem.get(_classInfo.getComponentType());
_componentType = (IJavaTypeInternal) type;
}
return _componentType;
} else {
return null;
}
}
finally
{
TypeSystem.popModule( module );
}
}
public void setComponentType( IJavaTypeInternal componentType )
{
_componentType = componentType;
}
@Override
public int getTypeInfoChecksum() {
if( isParameterizedType() )
{
return getGenericType().getTypeInfoChecksum();
}
return _tiChecksum;
}
@Override
public boolean hasAncestorBeenUpdated() {
return haveAncestorsBeenUpdated(this, _tiChecksum, new HashSet<IType>());
}
private static boolean haveAncestorsBeenUpdated(IJavaTypeInternal type, int tiChecksum, Set<IType> visited) {
final IType supertype = type.getSupertype();
if (supertype instanceof IJavaTypeInternal && !visited.contains(supertype) && hasBeenUpdated((IJavaTypeInternal) supertype, tiChecksum, visited)) {
return true;
}
for (IType anInterface : type.getInterfaces()) {
if (anInterface instanceof IJavaTypeInternal) {
final IJavaTypeInternal iFace = (IJavaTypeInternal) anInterface;
if (!visited.contains(iFace) && hasBeenUpdated(iFace, tiChecksum, visited)) {
return true;
}
} else {
// delegate types can be interfaces of Java types apparently !
}
}
return false;
}
public static boolean hasBeenUpdated(IJavaTypeInternal type, int tiChecksum, Set<IType> visited) {
visited.add(type);
if (TypeSystem.isDeleted(type)) {
return true;
} else {
return type.getTypeInfoChecksum() > tiChecksum || haveAncestorsBeenUpdated(type, tiChecksum, visited);
}
}
public Object writeReplace()
{
// Force _strName to initialize
getName();
return this;
}
public Object readResolve() throws ObjectStreamException
{
try
{
int iIndex = _strName.indexOf( '<' );
if( iIndex > 0 )
{
String strGenName = _strName.substring( 0, iIndex );
IType type = TypeLoaderAccess.instance().getIntrinsicTypeByFullName( strGenName );
return type.getParameterizedType( _typeParams );
}
else
{
return TypeLoaderAccess.instance().getIntrinsicTypeByFullName( _strName );
}
}
catch( ClassNotFoundException cnfe )
{
// Hack attempt to temporarily get around error with pcf parsed elements map containing classes not available in studio e.g., org.jgroups.ChannelClosedException
if( _strName.endsWith( "Exception" ) )
{
System.out.println( "Exception subclass not found: " + _strName + ". Substituting with Exception.class." );
return JavaTypes.EXCEPTION();
}
throw new InvalidClassException( _strName, cnfe.getMessage() );
}
}
public boolean isValid()
{
return true;
}
public int getModifiers()
{
return _classInfo.getModifiers();
}
public boolean isAbstract()
{
return Modifier.isAbstract( _classInfo.getModifiers() );
}
@SuppressWarnings({"EqualsWhichDoesntCheckParameterClass"})
public boolean equals( Object o )
{
// Because these are privately constructed and cached...
return thisRef() == o;
}
public int hashCode()
{
return _classInfo.hashCode();
}
public String toString()
{
return getName();
}
private static Set<String> SPECIAL_TYPE_INFO = new HashSet<String>() {{
add("gw.lang.ScriptParameters");
add("com.guidewire.studio.api.automation.IAutomatable");
add("gw.datatype.gw.datatype.DataTypes");
add("gw.i18n.ILocale");
add("gw.datatype.DataTypes");
}};
public ITypeInfo getExplicitTypeInfo()
{
if(_bDoesNotHaveExplicitTypeInfo)
{
return null;
}
if( CommonServices.getEntityAccess().getLanguageLevel().isStandard() )
{
// Don't lookup explicit beaninfo in standard Gosu
_bDoesNotHaveExplicitTypeInfo = true;
return null;
}
String name = getName();
if (!Character.isLetter(name.charAt(name.length() - 1))) {
_bDoesNotHaveExplicitTypeInfo = true;
return null;
}
try
{
if( SPECIAL_TYPE_INFO.contains(name) )
{
if( _explicitTypeInfoClass == null )
{
_explicitTypeInfoClass = CommonServices.getEntityAccess().getPluginClassLoader().loadClass(name + "TypeInfo");
}
return (ITypeInfo)_explicitTypeInfoClass.newInstance();
}
_bDoesNotHaveExplicitTypeInfo = true;
return null;
}
catch( Throwable t )
{
if( t instanceof ClassNotFoundException )
{
_bDoesNotHaveExplicitTypeInfo = true;
return null;
}
else
{
throw new RuntimeException( t );
}
}
}
private ITypeInfo convertToTypeInfo( )
{
return new JavaTypeInfo( thisRef(), _classInfo);
}
public void setAdapterClass( IGosuClassInternal adapterClass )
{
if( isParameterizedType() )
{
getGenericType().setAdapterClass( adapterClass );
return;
}
_adapterClass = adapterClass;
}
public IGosuClassInternal createAdapterClass()
{
if( isParameterizedType() )
{
return (IGosuClassInternal)getGenericType().createAdapterClass();
}
_adapterClass = GosuClassProxyFactory.instance().createImmediately( thisRef() );
return _adapterClass;
}
public static IJavaType[] convertClassArray( Class[] args )
{
IJavaType[] returnArray = new IJavaType[args.length];
for( int i = 0; i < args.length; i++ )
{
returnArray[i] = (IJavaType) TypeSystem.get( args[i] );
}
return returnArray;
}
public static void unloadTypes()
{
Collection<IJavaType> types = TYPES_BY_CLASS.values();
for( IJavaType type : types )
{
type.unloadTypeInfo();
}
TYPES_BY_CLASS.clear();
}
public boolean isDiscarded()
{
return _bDiscarded;
}
public void setDiscarded( boolean bDiscarded )
{
_bDiscarded = bDiscarded;
}
public boolean isCompoundType()
{
return false;
}
public Set<IType> getCompoundTypeComponents()
{
return null;
}
@Override
public IJavaClassInfo getConcreteClass() {
return _classInfo;
}
@Override
public IType getTypeFromJavaBackedType() {
return TypeSystem.getTypeFromJavaBasedType(thisRef());
}
@Override
public ObjectSize getRetainedMemory() {
return ObjectSizeUtil.deepSizeOf(this, new IObjectSizeFilter() {
@Override
public boolean skipField(Field field) {
return
field.getType().equals(Class.class) ||
field.getType().equals(MethodDescriptor.class) ||
field.getName().equals("_typeLoader") ||
field.getName().equals("_container")
;
}
@Override
public boolean skipObject(Object obj) {
Class<? extends Object> objClass = obj.getClass();
return
objClass.equals(Class.class) ||
(obj instanceof IType && obj != JavaType.this) ||
(obj instanceof IModule) ||
(obj.getClass().getName().startsWith("org.eclipse")) ||
(obj.getClass().getName().startsWith("java.lang.reflect")) ||
(obj.getClass().getName().startsWith("java.beans"))
|| (obj instanceof IJavaClassInfo && obj != JavaType.this._classInfo)
;
}
}, 100000);
}
@Override
public IType[] getLoaderParameterizedTypes() {
if (_parameterizationByParamsName != null) {
Collection<IJavaTypeInternal> values = _parameterizationByParamsName.values();
return values.toArray(new IType[values.size()]);
} else {
return IType.EMPTY_ARRAY;
}
}
@Override
public IFile[] getSourceFiles() {
if (getSourceFileHandle() == null) {
return NO_FILES;
} else {
File file = new File(getSourceFileHandle().getFilePath());
return new IFile[]{CommonServices.getFileSystem().getIFile(file)};
}
}
@Override
public boolean isAnnotation() {
return _classInfo.isAnnotation();
}
}