/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.parser;
import gw.config.BaseService;
import gw.config.CommonServices;
import gw.fs.IFile;
import gw.fs.IResource;
import gw.internal.gosu.compiler.SingleServingGosuClassLoader;
import gw.internal.gosu.module.Module;
import gw.lang.GosuShop;
import gw.lang.gosuc.Gosuc;
import gw.lang.gosuc.ICustomParser;
import gw.lang.gosuc.IGosuc;
import gw.lang.parser.ISymbolTable;
import gw.lang.parser.ITypeUsesMap;
import gw.lang.parser.TypeVarToTypeMap;
import gw.lang.parser.exceptions.ParseResultsException;
import gw.lang.parser.expressions.ITypeLiteralExpression;
import gw.lang.reflect.FunctionType;
import gw.lang.reflect.IDefaultTypeLoader;
import gw.lang.reflect.IErrorType;
import gw.lang.reflect.IFunctionType;
import gw.lang.reflect.IMetaType;
import gw.lang.reflect.IMethodInfo;
import gw.lang.reflect.INamespaceType;
import gw.lang.reflect.INonLoadableType;
import gw.lang.reflect.IPlaceholder;
import gw.lang.reflect.IType;
import gw.lang.reflect.ITypeLoader;
import gw.lang.reflect.ITypeLoaderListener;
import gw.lang.reflect.ITypeRef;
import gw.lang.reflect.ITypeRefFactory;
import gw.lang.reflect.ITypeSystem;
import gw.lang.reflect.ITypeVariableType;
import gw.lang.reflect.NoReferenceFoundException;
import gw.lang.reflect.RefreshKind;
import gw.lang.reflect.RefreshRequest;
import gw.lang.reflect.TypeSystem;
import gw.lang.reflect.TypeSystemShutdownListener;
import gw.lang.reflect.gs.GosuClassTypeLoader;
import gw.lang.reflect.gs.IGenericTypeVariable;
import gw.lang.reflect.gs.IGosuClassLoader;
import gw.lang.reflect.gs.IGosuEnhancement;
import gw.lang.reflect.java.IJavaClassInfo;
import gw.lang.reflect.java.IJavaType;
import gw.lang.reflect.java.JavaTypes;
import gw.lang.reflect.module.IExecutionEnvironment;
import gw.lang.reflect.module.IJreModule;
import gw.lang.reflect.module.IModule;
import gw.lang.reflect.module.IProject;
import gw.lang.reflect.module.ITypeLoaderStack;
import gw.util.GosuExceptionUtil;
import gw.util.IdentitySet;
import gw.util.concurrent.LockingLazyVar;
import java.io.FileNotFoundException;
import java.lang.ref.WeakReference;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
/**
*/
public class TypeLoaderAccess extends BaseService implements ITypeSystem
{
private Map<IType, IType> _boxToPrimitiveMap;
private Map<IType, IType> _primitiveToBoxMap;
public static LockingLazyVar<Map<String, IJavaType>> PRIMITIVE_TYPES_BY_NAME = new LockingLazyVar<Map<String, IJavaType>>() {
protected Map<String, IJavaType> init() {
HashMap<String, IJavaType> returnMap = new HashMap<String, IJavaType>( 9 );
returnMap.put("byte", JavaTypes.pBYTE());
returnMap.put("char", JavaTypes.pCHAR());
returnMap.put("double", JavaTypes.pDOUBLE());
returnMap.put("float", JavaTypes.pFLOAT());
returnMap.put("int", JavaTypes.pINT());
returnMap.put("long", JavaTypes.pLONG());
returnMap.put("short", JavaTypes.pSHORT());
returnMap.put("boolean", JavaTypes.pBOOLEAN());
returnMap.put("void", JavaTypes.pVOID());
return returnMap;
}
};
private static final Map<CharSequence, TypeGetter> EMPTY_DEFAULT_TYPES = new HashMap<CharSequence, TypeGetter>();
private static final ThreadLocal<ArrayList<IModule>> g_moduleStack = new ThreadLocal<ArrayList<IModule>>();
// This integer is incremented every time the typesystem is flushed so that a type can know if it
// is curent or not
private int _iRefreshChecksum = 0;
private int _iSingleRefreshChecksum = 0;
//------------------------------------------------------------
// Type system caches
//------------------------------------------------------------
private Map<CharSequence, TypeGetter> _defaultTypes;
private boolean _defaultTypesIniting = false;
//A weak cache of listeners who will be notified when typesystem events occur
private final CopyOnWriteArrayList<WeakReference<ITypeLoaderListener>> _listeners;
private final List<TypeSystemShutdownListener> _shutdownListeners = new ArrayList<TypeSystemShutdownListener>();
public static TypeLoaderAccess instance()
{
return (TypeLoaderAccess) CommonServices.getTypeSystem();
}
private ModuleTypeLoader getCurrentModuleTypeLoader()
{
Module module = getCurrentModule();
return module == null ? null : module.getModuleTypeLoader();
}
public Module getCurrentModule()
{
if( TypeSystem.isSingleModuleMode() )
{
return (Module)TypeSystem.getGlobalModule();
}
ArrayList list = (ArrayList)g_moduleStack.get();
if( list != null && !list.isEmpty() )
{
return (Module)list.get(list.size() - 1);
}
return null;
//throw new RuntimeException("Current module should never be null");
}
public void pushModule(IModule module) {
if (module == null) {
throw new IllegalStateException("Attempted to push NULL module on Gosu module stack:");
}
ArrayList<IModule> list = g_moduleStack.get();
if (list == null) {
g_moduleStack.set(list = new ArrayList<IModule>());
}
list.add(module);
}
public void popModule(IModule module) {
List<IModule> list = g_moduleStack.get();
if (list == null) {
return;
}
if (list.size() == 0) {
throw new IllegalStateException("Tried to pop a module when module stack was empty:\n" +
"Tried: " + module);
}
int last = list.size() - 1;
IModule remove = list.get(last);
if (module != remove) {
throw new IllegalStateException("Tried to pop a module that was not at the top of the stack.\n" +
"Tried: " + module + " Current: " + Arrays.toString(list.toArray()));
}
list.remove(last);
}
public TypeLoaderAccess()
{
_listeners = new CopyOnWriteArrayList<WeakReference<ITypeLoaderListener>>();
addShutdownListener(new TypeSystemShutdownListener() {
public void shutdown() {
GosuShop.clearThreadLocal(g_moduleStack);
}
});
}
@Override
public IGosuc makeGosucCompiler( String gosucProjectFile, ICustomParser custParser ) {
try {
return new Gosuc( gosucProjectFile, custParser );
}
catch( FileNotFoundException e ) {
throw new RuntimeException( e );
}
}
public IType getDefaultType(String name) {
TypeGetter getter = getDefaultTypes().get(name);
return getter != null ? getter.get() : null;
}
Map<CharSequence, TypeGetter> getDefaultTypes()
{
if (_defaultTypesIniting) {
return EMPTY_DEFAULT_TYPES;
}
if (_defaultTypes == null) {
_defaultTypesIniting = true;
Map<CharSequence, TypeGetter> defaultTypes = new HashMap<CharSequence, TypeGetter>();
getDefaultTypes( defaultTypes );
_defaultTypes = defaultTypes;
_defaultTypesIniting = false;
}
return _defaultTypes;
}
public List<ITypeLoader> getAllTypeLoaders() {
List<ITypeLoader> loaders = new ArrayList<ITypeLoader>();
IModule[] moduleTraversalList = getExecutionEnv().getGlobalModule().getModuleTraversalList();
for (IModule module : moduleTraversalList) {
loaders.addAll(module.getModuleTypeLoader().getTypeLoaderStack());
}
return loaders;
}
public void pushTypeLoader( IModule module, ITypeLoader typeLoader )
{
if( module == null )
{
module = getCurrentModule();
if( module == null )
{
throw new IllegalStateException( "There is no current module. If you are not running/testing the Eclipse Gosu Plugin, maybe ExecutionEnvironment.initDefault() was not called first?" );
}
}
((Module)module).getModuleTypeLoader().pushTypeLoader(typeLoader);
}
public void removeTypeLoader( final Class<? extends ITypeLoader> loaderType )
{
for (IModule module : getExecutionEnv().getModules()) {
((ModuleTypeLoader)module.getModuleTypeLoader()).removeTypeLoader(loaderType);
}
}
private List<ITypeLoaderListener> getListeners() {
List<ITypeLoaderListener> listeners = new ArrayList<ITypeLoaderListener>(_listeners.size());
List<WeakReference<ITypeLoaderListener>> obsoleteListeners = null;
for (WeakReference<ITypeLoaderListener> ref : _listeners) {
ITypeLoaderListener typeLoaderListener = ref.get();
if (typeLoaderListener != null) {
listeners.add(typeLoaderListener);
}
else {
if (obsoleteListeners == null) {
obsoleteListeners = new ArrayList<WeakReference<ITypeLoaderListener>>();
}
obsoleteListeners.add(ref);
}
}
if (obsoleteListeners != null) {
_listeners.removeAll( obsoleteListeners );
}
return listeners;
}
public ITypeRef getTypeReference(final IType type)
{
ITypeLoader typeLoader = type.getTypeLoader();
ITypeRef ref = typeLoader.getModule().getModuleTypeLoader().getTypeRefFactory().get(type);
if( ref == null )
{
throw new NoReferenceFoundException( type );
}
return ref;
}
public ITypeRef getOrCreateTypeReference(final IType type)
{
ITypeLoader typeLoader = type.getTypeLoader();
return typeLoader.getModule().getModuleTypeLoader().getTypeRefFactory().create(type);
}
public <T extends ITypeLoader> T getTypeLoader( final Class<? extends T> loaderType, IModule module )
{
return ((Module)module).getModuleTypeLoader().getTypeLoader(loaderType);
}
private final ArgCallable<IType, String> getNamespaceTypeIfValid_Callable =
new ArgCallable<IType, String>()
{
@Override
public IType call(String strNamespace) throws Exception
{
return getCurrentModuleTypeLoader().getNamespaceType(strNamespace);
}
};
public INamespaceType getNamespace(String strFqNamespace) {
for(int i = 0; i < strFqNamespace.length(); ++i) {
char ch = strFqNamespace.charAt(i);
if (!(Character.isJavaIdentifierPart(ch) || ch == '.' || ch == '*' )) {
return null;
}
}
IType namespaceType = returnFirstNonNull(getNamespaceTypeIfValid_Callable, strFqNamespace);
if( namespaceType instanceof INamespaceType )
{
return (INamespaceType)namespaceType;
}
return null;
}
private final ArgCallable<IType, Object> getIntrinsicTypeFromObject_Callable =
new ArgCallable<IType, Object>()
{
@Override
public IType call(Object object) throws Exception
{
return getCurrentModuleTypeLoader().getIntrinsicTypeFromObject(object);
}
};
public IType getIntrinsicTypeFromObject( final Object object )
{
return returnFirstNonNull( getIntrinsicTypeFromObject_Callable, object );
}
public IType getIntrinsicTypeByFullName( String fullyQualifiedName ) throws ClassNotFoundException
{
IType type = FrequentUsedJavaTypeCache.instance(getExecutionEnv()).getHighUsageType(fullyQualifiedName);
if (type != null) {
return type;
}
type = getByFullNameIfValid(fullyQualifiedName);
if( type == null )
{
throw new ClassNotFoundException( fullyQualifiedName + " in module " + getCurrentModule());
}
else
{
return type;
}
}
/**
* Gets an intrinsic type based on a relative name. This could either be the name of an entity,
* like "User", the name of a typekey, like "SystemPermission", or a class name, like
* "java.lang.String" (relative and fully qualified class names are the same as far as this factory
* is concerned). Names can have [] appended to them to create arrays, and multi-dimensional arrays
* are supported.
*
* @param relativeName the relative name of the type
* @param typeUses the map of used types to use when resolving
* @return the corresponding IType
* @throws ClassNotFoundException if the specified name doesn't correspond to any type
*/
public IType getByRelativeName(String relativeName, ITypeUsesMap typeUses) throws ClassNotFoundException {
String relativeName1 = relativeName;
IType type = FrequentUsedJavaTypeCache.instance( getExecutionEnv() ).getHighUsageType(relativeName1);
if (type != null) {
return type;
}
//## todo: consider handling requests to find a parameterized type... the following is a giant hack
int i = relativeName1 == null ? -1 : relativeName1.indexOf( '<' );
if( i >= 0 )
{
assert relativeName1 != null;
relativeName1 = relativeName1.substring( 0, i );
}
//##
type = getTypeByRelativeNameIfValid_NoGenerics(relativeName1, typeUses);
if( type == null )
{
throw new ClassNotFoundException(relativeName1);
}
else
{
return type;
}
}
public IType getTypeByRelativeNameIfValid_NoGenerics(String relativeName, ITypeUsesMap typeUses)
{
IType type;
IType defaultType = getDefaultType(relativeName);
if( defaultType != null )
{
return defaultType;
}
if( typeUses != null )
{
// Then if we have a type uses map and we are resolving let it try
type = typeUses.resolveType(relativeName);
} else {
// Else just ask for it by full name
type = getByFullNameIfValid(relativeName);
}
return type;
}
public Set<? extends CharSequence> getAllTypeNames() {
TypeSystem.lock();
try {
Set<CharSequence> names = new HashSet<CharSequence>();
names.addAll(getDefaultTypes().keySet());
// Walk the list backward so we resolve type dependencies in the correct order
List<ITypeLoader> loaders = getAllTypeLoaders();
for (int i = loaders.size() - 1; i >= 0; i--) {
names.addAll(loaders.get(i).getAllTypeNames());
}
return names;
} finally {
TypeSystem.unlock();
}
}
public void clearErrorTypes()
{
for (IModule module : getExecutionEnv().getModules()) {
((ModuleTypeLoader) module.getModuleTypeLoader()).clearErrorTypes();
}
}
public void refresh( final boolean clearCachedTypes )
{
IModule globalModule = TypeSystem.getGlobalModule();
// dlank: should an exception be thrown if the server is not in dev mode?
pushModule(globalModule);
TypeSystem.lock();
try
{
dumpMaps();
++_iRefreshChecksum;
++_iSingleRefreshChecksum;
for (IModule module : getExecutionEnv().getModules()) {
module.getModuleTypeLoader().refreshed();
}
CommonServices.getPlatformHelper().refresh(null);
fireRefreshed();
if( clearCachedTypes )
{
for (IModule module : getExecutionEnv().getModules()) {
module.getModuleTypeLoader().getTypeRefFactory().clearCaches();
}
MetaType.clearCaches();
}
SingleServingGosuClassLoader.clearCache();
_defaultTypes = null;
}
finally
{
TypeSystem.unlock();
popModule(globalModule);
}
}
public void shutdown()
{
TypeSystem.lock();
try
{
ExecutionEnvironment executionEnv = getExecutionEnv();
for (IModule module : executionEnv.getModules()) {
module.getModuleTypeLoader().shutdown();
}
for (TypeSystemShutdownListener shutdownListener : _shutdownListeners) {
shutdownListener.shutdown();
}
TypeSystem.refresh(false);
executionEnv.shutdown();
}
finally
{
TypeSystem.unlock();
}
}
public void refreshTypes(final RefreshRequest request) {
TypeRefFactory typeRefFactory = (TypeRefFactory) request.module.getModuleTypeLoader().getTypeRefFactory();
pushModule(request.module);
TypeSystem.lock();
try {
++_iSingleRefreshChecksum;
CommonServices.getMemoryMonitor().reclaimMemory(request);
// Step 1: Find all top-level types that need to be refreshed
Set<IType> typesToRefresh = new HashSet<IType>(10);
for (String typeName : request.types) {
IType type = typeRefFactory.get(typeName);
if (type != null) {
typesToRefresh.add(type);
if (!((AbstractTypeRef) type).isStale()) {
ITypeRef topLevelType = getTopLevelType(type);
if (topLevelType != null) {
typesToRefresh.add(topLevelType);
}
if (type instanceof IGosuEnhancement) {
// add the current enhanced type
IGosuEnhancement enhancement = (IGosuEnhancement) type;
IType enhancedType = enhancement.getEnhancedType();
if (enhancedType != null && !(enhancedType instanceof INonLoadableType) && !TypeSystem.isDeleted(enhancedType)) {
ITypeRef topLevelType1 = getTopLevelType(enhancedType);
if (topLevelType != null) {
typesToRefresh.add(topLevelType1);
}
}
}
// add the old enhanced type
for (IModule module : request.module.getModuleTraversalList()) {
ITypeLoaderStack moduleTypeLoader = module.getModuleTypeLoader();
GosuClassTypeLoader gosuClassTypeLoader = moduleTypeLoader.getTypeLoader(GosuClassTypeLoader.class);
String orphanedEnhancementName = gosuClassTypeLoader.getEnhancementIndex().getOrphanedEnhancement(type.getName());
if (orphanedEnhancementName != null) {
IType orphanedEnhancement = typeRefFactory.get(orphanedEnhancementName);
if (orphanedEnhancement instanceof AbstractTypeRef && !((AbstractTypeRef) orphanedEnhancement).isStale()) {
typesToRefresh.add(orphanedEnhancement);
}
}
}
}
}
}
// Step 2: Find all subordinate types that need to be refreshed
IdentitySet<ITypeRef> typesToMakeStaleSet = new IdentitySet<ITypeRef>(typesToRefresh.size() * 2);
for (IType type : typesToRefresh) {
typesToMakeStaleSet.add((ITypeRef) type);
addGosuProxyClass(typesToMakeStaleSet, type);
List<ITypeRef> subordinateRefs = typeRefFactory.getSubordinateRefs(type.getName());
for (ITypeRef typeRef : subordinateRefs) {
typesToMakeStaleSet.add(typeRef);
addGosuProxyClass(typesToMakeStaleSet, typeRef);
}
}
// Step 3: Some stuff
Iterator<ITypeRef> iterator = typesToMakeStaleSet.iterator();
while (iterator.hasNext()) {
IType typeToRefresh = iterator.next();
if (typeToRefresh instanceof IGosuClassInternal && !((AbstractTypeRef) typeToRefresh).isStale()) {
IGosuClassInternal gsClass = (IGosuClassInternal) typeToRefresh;
((IGosuClassInternal) gsClass.dontEverCallThis()).unloadBackingClass();
}
}
// Step 4: Sort all the types
ITypeRef[] typesToMakeStaleArray = typesToMakeStaleSet.toArray(new ITypeRef[typesToMakeStaleSet.size()]);
Arrays.sort(typesToMakeStaleArray,
new Comparator<IType>() {
public int compare(IType t1, IType t2) {
return computeSortIndex((AbstractTypeRef) t2) - computeSortIndex((AbstractTypeRef) t1);
}
private int computeSortIndex(AbstractTypeRef ref) {
return ref != null ? ref._getIndexForSortingFast(ref._getTypeName()) : 10000;
}
}
);
Map<ITypeLoader, Set<String>> typeLoaderToTypeMap = new HashMap<ITypeLoader, Set<String>>();
typeLoaderToTypeMap.put(request.typeLoader, new HashSet<String>(Arrays.asList(request.types)));
for (ITypeRef type : typesToMakeStaleSet) {
ITypeLoader typeLoader = type.getTypeLoaderDirectly();
Set<String> typeRefs = typeLoaderToTypeMap.get(typeLoader);
if (typeRefs == null) {
typeRefs = new HashSet<String>(typesToMakeStaleSet.size());
typeLoaderToTypeMap.put(typeLoader, typeRefs);
}
typeRefs.add(((AbstractTypeRef)type)._getTypeName());
}
// Step 5: Make the references stale
for (IType type : typesToMakeStaleArray) {
if (type != null) {
((ITypeRef) type)._setStale(request.kind);
}
}
// Step 6: Clear all caches
getGlobalModuleTypeLoader().clearFromCaches(request);
for (IModule module : request.module.getModuleTraversalList()) {
((ModuleTypeLoader) module.getModuleTypeLoader()).clearFromCaches(request);
}
// Step 7: notify the typeloader
for (ITypeLoader typeLoader : typeLoaderToTypeMap.keySet()) {
final Set<String> strings = typeLoaderToTypeMap.get(typeLoader);
RefreshRequest theRequest = new RefreshRequest(strings.toArray(new String[strings.size()]), request, typeLoader);
typeLoader.refreshedTypes(theRequest);
}
// Step 8: Call all listeners
for (ITypeLoaderListener listener : getListeners()) {
listener.refreshedTypes(request);
}
} finally {
TypeSystem.unlock();
popModule(request.module);
}
}
private ModuleTypeLoader getGlobalModuleTypeLoader() {
IModule rootModule = getExecutionEnv().getGlobalModule();
return rootModule != null ? ((ModuleTypeLoader) rootModule.getModuleTypeLoader()) : null;
}
private void addGosuProxyClass( IdentitySet<ITypeRef> allTypes, IType type ) {
if( type instanceof IJavaTypeInternal && !((AbstractTypeRef)type).isStale()) {
IGosuClassInternal gsProxyClass = ((IJavaTypeInternal)type).getAdapterClassDirectly();
if( gsProxyClass != null ) {
allTypes.add( (ITypeRef)gsProxyClass );
}
}
}
private ITypeRef getTopLevelType(IType type) {
IType topLevelType = TypeLord.getTopLevelType(type);
if (topLevelType instanceof ITypeRef) {
return (ITypeRef) topLevelType;
} else {
return null;
}
}
public int getRefreshChecksum()
{
return _iRefreshChecksum;
}
public int getSingleRefreshChecksum()
{
return _iSingleRefreshChecksum;
}
/**
* Maintains weak refs to listeners. This is primarily so that tests don't
* accumulate a bunch of listeners over time. Otherwise this is a potential
* memory gobbler in tests.
* <p>
* Note! Callers must manage the lifecycle of the listener, otherwise since this
* method creates a weak ref, it will be collected when it goes out of scope.
*
* @param l Your type loader listener
*/
public void addTypeLoaderListenerAsWeakRef( ITypeLoaderListener l )
{
if(!hasListener(l)) {
_listeners.add(new WeakReference<ITypeLoaderListener>(l));
}
}
public void removeTypeLoaderListener( ITypeLoaderListener l )
{
for(WeakReference<ITypeLoaderListener> ref : _listeners) {
if(ref.get() == l) {
_listeners.remove(ref);
break;
}
}
}
private boolean hasListener( ITypeLoaderListener l ) {
for(WeakReference<ITypeLoaderListener> ref : _listeners) {
if(ref.get() == l) {
return true;
}
}
return false;
}
private void fireRefreshed()
{
TypeSystem.lock();
try
{
for (IModule module : getExecutionEnv().getModules()) {
module.getModuleTypeLoader().refreshed();
}
for( ITypeLoaderListener listener : getListeners())
{
listener.refreshed();
}
}
finally
{
TypeSystem.unlock();
}
}
public void incrementChecksums() {
_iRefreshChecksum++;
_iSingleRefreshChecksum++;
}
private static ExecutionEnvironment getExecutionEnv()
{
return ExecutionEnvironment.instance();
}
private <E, A> E returnFirstNonNull(ArgCallable<E, A> callable, A argument) {
// look in the global module
TypeSystem.pushGlobalModule();
try {
try {
E result = callable.call(argument);
if (result != null) {
return result;
}
} catch (Exception e) {
throw GosuExceptionUtil.forceThrow(e);
}
} finally {
TypeSystem.popGlobalModule();
}
// look in the module stack
IModule module = getCurrentModule();
if (module == null) {
throw new RuntimeException("Current module must not be null.");
} else {
for (IModule m : module.getModuleTraversalList()) {
if (m != TypeSystem.getGlobalModule()) {
pushModule(m);
try {
E result = callable.call(argument);
if (result != null) {
return result;
}
} catch (Exception e) {
throw GosuExceptionUtil.forceThrow(e);
} finally {
popModule(m);
}
}
}
}
return null;
}
public void addShutdownListener(TypeSystemShutdownListener listener) {
_shutdownListeners.add(listener);
}
static interface TypeGetter {
IType get();
}
private static void getDefaultTypes( Map<CharSequence, TypeGetter> typeMap )
{
// primitives
typeMap.put( "void", new TypeGetter() { public IType get() { return JavaType.get(Void.TYPE, _getDefaultLoader() ); }} );
typeMap.put( "boolean", new TypeGetter() { public IType get() { return JavaType.get(Boolean.TYPE, _getDefaultLoader()); }} );
typeMap.put( "byte", new TypeGetter() { public IType get() { return JavaType.get(Byte.TYPE, _getDefaultLoader()); }} );
typeMap.put( "char", new TypeGetter() { public IType get() { return JavaType.get(Character.TYPE, _getDefaultLoader()); }} );
typeMap.put( "double", new TypeGetter() { public IType get() { return JavaType.get(Double.TYPE, _getDefaultLoader()); }} );
typeMap.put( "float", new TypeGetter() { public IType get() { return JavaType.get(Float.TYPE, _getDefaultLoader()); }} );
typeMap.put( "int", new TypeGetter() { public IType get() { return JavaType.get(Integer.TYPE, _getDefaultLoader()); }} );
typeMap.put( "long", new TypeGetter() { public IType get() { return JavaType.get(Long.TYPE, _getDefaultLoader()); }} );
typeMap.put( "short", new TypeGetter() { public IType get() { return JavaType.get(Short.TYPE, _getDefaultLoader()); }} );
typeMap.put( "void", new TypeGetter() { public IType get() { return JavaType.get(Void.TYPE, _getDefaultLoader()); }} );
// build-in types
typeMap.put( "Number", new TypeGetter() { public IType get() { return JavaType.get(Double.class, _getDefaultLoader()); }} );
typeMap.put( "String", new TypeGetter() { public IType get() { return JavaType.get(String.class, _getDefaultLoader()); }} );
typeMap.put( "Boolean", new TypeGetter() { public IType get() { return JavaType.get(Boolean.class, _getDefaultLoader()); }} );
typeMap.put( "DateTime",new TypeGetter() { public IType get() { return JavaType.get(Date.class, _getDefaultLoader()); }} );
typeMap.put( "List", new TypeGetter() { public IType get() { return JavaType.get(List.class, _getDefaultLoader()); }} );
typeMap.put( "Object", new TypeGetter() { public IType get() { return JavaType.get(Object.class, _getDefaultLoader()); }} );
typeMap.put( "Array", new TypeGetter() { public IType get() { return JavaType.get(Object.class, _getDefaultLoader()).getArrayType(); }} );
typeMap.put( "Bean", new TypeGetter() { public IType get() { return JavaType.get(Object.class, _getDefaultLoader()); }} );
typeMap.put( "Type", new TypeGetter() { public IType get() { return MetaType.ROOT_TYPE_TYPE.get(); }} );
typeMap.put( "Key", new TypeGetter() { public IType get() { return TypeSystem.getKeyType(); }} );
}
private static DefaultTypeLoader _getDefaultLoader() {
//!!
//!! Ensure we use the execution environment in conext -- we handle multiple exec environments now
//!!
IModule jreModule = ExecutionEnvironment.instance().getJreModule();
return (DefaultTypeLoader) jreModule.getModuleTypeLoader().getDefaultTypeLoader();
}
private interface ArgCallable<V, A1> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call(A1 arg1) throws Exception;
}
/**
* Gets the intrinsic type for a given class.<p>
* <p/>
* <b>Note:</b> you should use this method only if you do not have an
* Object of class <code>javaClass</code> to get the type from. If you
* do have such an object, use {@link #getFromObject} instead.
*
* @param javaClass the Class to convert to an intrinsic type
* @return the IType that corresponds to that class
* @see #getFromObject(Object)
*/
public IType get(Class<?> javaClass) {
assert javaClass != null;
IType type = FrequentUsedJavaTypeCache.instance( getExecutionEnv() ).getHighUsageType(javaClass);
if (type != null) {
return type;
}
String fqn = computeFullyQualifiedName(javaClass);
if (IType.class.isAssignableFrom(javaClass) && fqn.endsWith(ITypeRefFactory.SYSTEM_PROXY_SUFFIX)) {
String typeName = unproxy(fqn);
IType theType = TypeSystem.getByFullName(typeName, TypeSystem.getGlobalModule());
IModule module = theType.getTypeLoader().getModule();
DefaultTypeLoader typeLoader = module.getTypeLoaders(DefaultTypeLoader.class).get(0);
type = JavaType.get(javaClass, typeLoader);
if (type != null) {
return type;
}
}
type = getByFullNameIfValid(fqn);
if (type != null) {
return type;
}
// NOTE pdalbora 11-Apr-2011 -- If it's a proxy class, then it won't be found by name. Instead, just create
// a JavaType for it. This won't happen in the editor (Eclipse), because a proxy class only exists at runtime;
// therefore, using the DefaultTypeLoader here should be fine.
if (Proxy.isProxyClass(javaClass)) {
return JavaType.get(javaClass, DefaultTypeLoader.instance());
}
return type;
}
private String unproxy(String fqn) {
String suffix = ITypeRefFactory.USER_PROXY_SUFFIX + ITypeRefFactory.SYSTEM_PROXY_SUFFIX;
if (fqn.endsWith(suffix)) {
return fqn.substring(0, fqn.length() - suffix.length());
}
suffix = ITypeRefFactory.SYSTEM_PROXY_SUFFIX;
if (fqn.endsWith(suffix)) {
return fqn.substring(0, fqn.length() - suffix.length());
}
return fqn;
}
private String computeFullyQualifiedName(Class<?> javaClass) {
if (javaClass.isArray()) {
return computeFullyQualifiedName(javaClass.getComponentType()) + "[]";
} else {
return javaClass.getName().replace('$', '.');
}
}
public IType get(IJavaClassInfo javaClassInfo) {
if (javaClassInfo == null) {
throw new NullPointerException("Cannot have a null class info.");
}
if (javaClassInfo instanceof ClassJavaClassInfo) {
pushModule(javaClassInfo.getModule());
try {
return get(((ClassJavaClassInfo) javaClassInfo).getJavaClass());
} finally {
popModule(javaClassInfo.getModule());
}
}
String name = javaClassInfo.getName().replace('$', '.');
IType type = FrequentUsedJavaTypeCache.instance( javaClassInfo.getModule().getExecutionEnvironment() ).getHighUsageType(name);
if (type == null) {
pushModule(javaClassInfo.getModule());
try {
type = getByFullNameIfValid(name);
} finally {
popModule(javaClassInfo.getModule());
}
}
return type;
}
/**
* Returns the intrinsic type for the given Object.
*
* @param object the object to get an IType for
* @return the IType for the object
* @see #get(Class)
*/
public IType getFromObject(Object object) {
return getIntrinsicTypeFromObject(object);
}
public IType getByRelativeName(String relativeName) throws ClassNotFoundException {
return getByRelativeName(relativeName, CommonServices.getEntityAccess().getDefaultTypeUses());
}
/**
* Gets an intrinsic type based on a fully-qualified name. This could either be the name of an entity,
* like "entity.User", the name of a typekey, like "typekey.SystemPermission", or a class name, like
* "java.lang.String". Names can have [] appended to them to create arrays, and multi-dimensional arrays
* are supported.
*
* @param fullyQualifiedName the fully qualified name of the type
* @return the corresponding IType
* @throws RuntimeException if the specified name doesn't correspond to any type
*/
public IType getByFullName(String fullyQualifiedName) {
try {
return getIntrinsicTypeByFullName(fullyQualifiedName);
} catch (ClassNotFoundException e) {
throw new RuntimeExceptionWithNoStacktrace(e);
}
}
/**
* Gets a type based on a fully-qualified name. This could either be the name of an entity,
* like "entity.User", the name of a typekey, like "typekey.SystemPermission", or a class name, like
* "java.lang.String". Names can have [] appended to them to create arrays, and multi-dimensional arrays
* are supported.
* <p/>
* This method behaves the same as getByFullName execept instead of throwing it returns null.
*
* @param fullyQualifiedName the fully qualified name of the type
* @return the corresponding IType or null if the type does not exist
*/
public IType getByFullNameIfValid(String fullyQualifiedName) {
return getByFullNameIfValid(fullyQualifiedName, false);
}
public IType getByFullNameIfValidNoJava(String fullyQualifiedName) {
return getByFullNameIfValid(fullyQualifiedName, true);
}
private IType getByFullNameIfValid(String fullyQualifiedName, boolean skipJava) {
if (!isValidTypeName(fullyQualifiedName)) {
return null;
}
// look in the module stack
IModule module = getCurrentModule();
if (module == null) {
throw new RuntimeException("Current module must not be null.");
} else {
if (module.getExecutionEnvironment().isShadowingMode()) {
// In shadowing mode, always use global module. At runtime, this does not make any difference
// since there is only one module (which is both current module and global module).
// It makes difference when "customer" project is opened there we allow shadowing.
module = TypeSystem.getGlobalModule();
}
for (IModule m : module.getModuleTraversalList()) {
try {
IType type = ((Module) m).getModuleTypeLoader().getTypeByFullNameIfValid(fullyQualifiedName, skipJava);
if (type != null) {
return type;
}
} catch (Exception e) {
throw GosuExceptionUtil.forceThrow(e);
}
}
}
return null;
}
private final boolean isValidTypeName(String fqn) {
// empty names are invalid
if( fqn == null || fqn.length() == 0 ) {
return false;
}
if( fqn.indexOf("block(") != -1 ||
fqn.indexOf("block (") != -1 )
{
return false;
}
int braceIndex = fqn.indexOf('[');
if( braceIndex > 0 ) {
if( fqn.length() < braceIndex + 2 ||
fqn.charAt( braceIndex + 1 ) != ']' ) {
return false;
}
fqn = fqn.substring( 0, braceIndex );
}
// primitives are ok
if (isDefaultType(fqn)) {
return true;
}
// the last char should be an identifier char
if (!Character.isJavaIdentifierPart(fqn.charAt( fqn.length() - 1 ))) {
return false;
}
return true;
}
private boolean isDefaultType(String fqn) {
return getDefaultTypes().containsKey(fqn);
}
@Override
public IType boundTypes(IType targetType, List<IType> typesToBound) {
return TypeLord.boundTypes(targetType, typesToBound);
}
public void refresh(ITypeRef typeRef) {
refreshTypes(new RefreshRequest(null, new String[]{typeRef.getName()}, typeRef.getTypeLoader(), RefreshKind.MODIFICATION));
}
private void dumpMaps() {
_boxToPrimitiveMap = null;
_primitiveToBoxMap = null;
PRIMITIVE_TYPES_BY_NAME.clear();
}
public void refresh(IModule module)
{
// dlank: should an exception be thrown if the server is not in dev mode?
pushModule(module);
TypeSystem.lock();
try
{
if (module instanceof IJreModule) {
dumpMaps();
}
++_iRefreshChecksum;
++_iSingleRefreshChecksum;
getGlobalModuleTypeLoader().refreshed();
((Module)module).getModuleTypeLoader().refreshed();
CommonServices.getPlatformHelper().refresh(module);
fireRefreshed();
((Module)module).getModuleTypeLoader().getTypeRefFactory().clearCaches();
_defaultTypes = null;
}
finally
{
TypeSystem.unlock();
popModule(module);
}
}
@Override
public void refreshed(IResource file, String typeName, RefreshKind refreshKind) {
IModule module = ExecutionEnvironment.instance().getModule(file);
// The module will be null for files that are not part of any source root
if (module != null) {
((ITypeLoaderStackInternal) module.getModuleTypeLoader()).refresh(file, typeName, refreshKind);
// We need to refresh the global loaders as well because the modification of a Java class belonging
// to an entity needs to refresh the entity
getGlobalModuleTypeLoader().refresh(file, typeName, refreshKind);
}
}
@Override
public String[] getTypesForFile(IModule module, IFile file) {
if (module != null) {
// Module
for (ITypeLoader loader : module.getModuleTypeLoader().getTypeLoaderStack()) {
final List<String> typeNames = new ArrayList<String>();
if (loader.handlesFile(file)) {
typeNames.addAll(Arrays.asList(loader.getTypesForFile(file)));
}
if (!typeNames.isEmpty()) {
return typeNames.toArray(new String[typeNames.size()]);
}
}
}
// Global
final List<String> typeNames = new ArrayList<String>();
for (ITypeLoader loader : getGlobalModuleTypeLoader().getTypeLoaderStack()) {
if (loader.handlesFile(file)) {
typeNames.addAll(Arrays.asList(loader.getTypesForFile(file)));
}
}
if (!typeNames.isEmpty()) {
return typeNames.toArray(new String[typeNames.size()]);
}
// Default
return new String[] {"default." + file.getBaseName()};
}
/**
* Converts a String name of a type into an IType.
*
* @throws IllegalArgumentException if the type string doesn't correspond to any known IType
*/
public IType parseType(String typeString) throws IllegalArgumentException {
return parseType(typeString, (ITypeUsesMap)null);
}
public IType parseType(String typeString, ITypeUsesMap typeUsesMap) throws IllegalArgumentException {
IType type = getTypeByRelativeNameIfValid_NoGenerics(typeString, typeUsesMap);
if (type == null) {
throw new IllegalArgumentException("Could not parse type " + typeString);
} else {
return type;
}
}
public IType parseType(String typeString, TypeVarToTypeMap actualParamByVarName) {
return TypeLord.parseType(typeString, actualParamByVarName);
}
public IType parseType(String typeString, TypeVarToTypeMap actualParamByVarName, ITypeUsesMap typeUsesMap) {
return TypeLord.parseType(typeString, actualParamByVarName, typeUsesMap);
}
public ITypeLiteralExpression parseTypeExpression(String typeString, TypeVarToTypeMap actualParamByVarName, ITypeUsesMap typeUsesMap) throws ParseResultsException {
return TypeLord.parseTypeLiteral(typeString, actualParamByVarName, false, typeUsesMap);
}
public IType getComponentType(IType valueType) {
IType componentType;
if (valueType.isArray()) {
componentType = valueType.getComponentType();
} else if (valueType instanceof IPlaceholder && ((IPlaceholder) valueType).isPlaceholder()) {
componentType = valueType.getComponentType();
} else {
IType qrsType = JavaTypes.IQUERY_RESULT_SET();
if (qrsType.isAssignableFrom(valueType)) {
for (IType iType : valueType.getAllTypesInHierarchy()) {
if (iType.isParameterizedType() && iType.getGenericType().equals(qrsType)) {
return iType.getTypeParameters()[0];
}
}
componentType = ErrorType.getInstance();
} else {
componentType = ErrorType.getInstance();
}
}
return componentType;
}
public ITypeVariableType getOrCreateTypeVariableType(String strName, IType boundingType, IType enclosingType) {
IGenericTypeVariable typeVar = new GenericTypeVariable(strName, boundingType);
typeVar.createTypeVariableDefinition(enclosingType);
return new TypeVariableType(enclosingType, typeVar);
}
public IFunctionType getOrCreateFunctionType(IMethodInfo mi) {
return new FunctionType(mi);
}
public IFunctionType getOrCreateFunctionType(String strFunctionName, IType retType, IType[] paramTypes) {
return new FunctionType(strFunctionName, retType, paramTypes);
}
public TypeVarToTypeMap mapTypeByVarName(IType ownersType, IType declaringType, boolean bKeepTypeVars) {
return TypeLord.mapTypeByVarName(ownersType, declaringType, bKeepTypeVars);
}
public IType getActualType(IType type, TypeVarToTypeMap actualParamByVarName, boolean bKeepTypeVars) {
return TypeLord.getActualType(type, actualParamByVarName, bKeepTypeVars);
}
public void inferTypeVariableTypesFromGenParamTypeAndConcreteType(IType genParamType, IType argType, TypeVarToTypeMap map) {
TypeLord.inferTypeVariableTypesFromGenParamTypeAndConcreteType(genParamType, argType, map);
}
public IErrorType getErrorType() {
return ErrorType.getInstance();
}
public IErrorType getErrorType(String strErrantName) {
return ErrorType.getInstance(strErrantName);
}
public IErrorType getErrorType(ParseResultsException pe) {
return new ErrorType(pe);
}
public IDefaultTypeLoader getDefaultTypeLoader() {
return DefaultTypeLoader.instance();
}
public IType findParameterizedType(IType type, IType rhsType) {
return TypeLord.findParameterizedType(type, rhsType);
}
public Set<String> getNamespacesFromTypeNames(Set<? extends CharSequence> allTypeNames, Set<String> namespaces) {
return TypeLord.getNamespacesFromTypeNames(allTypeNames, namespaces);
}
public void pushTypeLoader(ITypeLoader loader) {
pushTypeLoader(null, loader);
}
public boolean areBeansEqual(Object o1, Object o2) {
return BeanAccess.areBeansEqual(o1, o2);
}
public void pushIncludeAll() {
GosuClassTypeInfo.pushIncludeAll();
}
public void popIncludeAll() {
GosuClassTypeInfo.popIncludeAll();
}
public boolean isIncludeAll() {
return GosuClassTypeInfo.isIncludeAll();
}
public IType getCurrentCompilingType() {
return GosuClassCompilingStack.getCurrentCompilingType();
}
public void pushCompilingType(IType type) {
GosuClassCompilingStack.pushCompilingType(type);
}
public void popCompilingType() {
GosuClassCompilingStack.popCompilingType();
}
public IType getCompilingType(String strName) {
return GosuClassCompilingStack.getCompilingType(strName);
}
public void pushSymTableCtx(ISymbolTable ctx) {
CompiledGosuClassSymbolTable.pushSymTableCtx(ctx);
}
public void popSymTableCtx() {
CompiledGosuClassSymbolTable.popSymTableCtx();
}
public ISymbolTable getSymTableCtx() {
return CompiledGosuClassSymbolTable.getSymTableCtx();
}
public IType getTypeFromObject(Object obj) {
return getIntrinsicTypeFromObject(obj);
}
public boolean isExpandable(IType type) {
return TypeLord.isExpandable(type);
}
public String getNameOfParams(IType[] paramTypes, boolean bRelative, boolean bWithEnclosingType) {
return TypeLord.getNameOfParams(paramTypes, bRelative, bWithEnclosingType);
}
public ISymbolTable getCompiledGosuClassSymbolTable() {
return CompiledGosuClassSymbolTable.instance();
}
public IType getJavaType(Class javaClass) {
return TypeSystem.get(javaClass);
}
public String getNameWithQualifiedTypeVariables(IType type) {
return TypeLord.getNameWithQualifiedTypeVariables(type, false);
}
public IType getDefaultParameterizedType(IType type) {
return TypeLord.getDefaultParameterizedType(type);
}
public IType getDefaultParameterizedTypeWithTypeVars(IType type) {
return TypeLord.getDefaultParameterizedTypeWithTypeVars(type);
}
public boolean canCast(IType lhsType, IType rhsType) {
if (lhsType instanceof TypeVariableType) {
// Support casting from a type variable
lhsType = ((TypeVariableType) lhsType).getBoundingType();
}
// Support explicit downcast
if (lhsType != null) {
if (lhsType.isAssignableFrom(rhsType)) {
return true;
} else if (rhsType.isInterface() && lhsType.isInterface()) {
return true;
}
// Support cross-casting to an interface
else if (rhsType.isInterface() && (!lhsType.isFinal() && !lhsType.isPrimitive() && !(lhsType instanceof IFunctionType) && !(lhsType.isArray()))) {
return true;
} else if (lhsType.isInterface() && (!rhsType.isFinal() && !rhsType.isPrimitive() && !(rhsType instanceof IFunctionType) && !(rhsType.isArray()))) {
return true;
}
}
return false;
}
public IJavaType getPrimitiveType(String name) {
return JavaType.getPrimitiveType(name);
}
public IType getPrimitiveType(IType boxType) {
initBoxMapsIfNeeded();
return _boxToPrimitiveMap.get(boxType);
}
public IType getBoxType(IType primitiveType) {
initBoxMapsIfNeeded();
return _primitiveToBoxMap.get(primitiveType);
}
private void initBoxMapsIfNeeded() {
if (_boxToPrimitiveMap == null) {
TypeSystem.lock();
try {
if (_boxToPrimitiveMap == null) {
_boxToPrimitiveMap = new HashMap<IType, IType>();
_primitiveToBoxMap = new HashMap<IType, IType>();
mapBoxType(JavaTypes.VOID(), JavaTypes.pVOID());
mapBoxType(JavaTypes.BOOLEAN(), JavaTypes.pBOOLEAN());
mapBoxType(JavaTypes.BYTE(), JavaTypes.pBYTE());
mapBoxType(JavaTypes.CHARACTER(), JavaTypes.pCHAR());
mapBoxType(JavaTypes.DOUBLE(), JavaTypes.pDOUBLE());
mapBoxType(JavaTypes.FLOAT(), JavaTypes.pFLOAT());
mapBoxType(JavaTypes.INTEGER(), JavaTypes.pINT());
mapBoxType(JavaTypes.LONG(), JavaTypes.pLONG());
mapBoxType(JavaTypes.SHORT(), JavaTypes.pSHORT());
}
} finally {
TypeSystem.unlock();
}
}
}
private void mapBoxType(IType boxType, IType primitiveType) {
_boxToPrimitiveMap.put(boxType, primitiveType);
_primitiveToBoxMap.put(primitiveType, boxType);
}
public IExecutionEnvironment getExecutionEnvironment() {
return ExecutionEnvironment.instance();
}
public IExecutionEnvironment getExecutionEnvironment( IProject project ) {
return ExecutionEnvironment.instance(project);
}
@Override
public IGosuClassLoader getGosuClassLoader() {
DefaultTypeLoader loader = (DefaultTypeLoader) getCurrentModule().getTypeLoaders(IDefaultTypeLoader.class).get(0);
return loader.getGosuClassLoader();
}
@Override
public void dumpGosuClassLoader() {
DefaultTypeLoader loader = (DefaultTypeLoader) getCurrentModule().getTypeLoaders(IDefaultTypeLoader.class).get(0);
loader.dumpGosuClassLoader();
}
@Override
public IType replaceTypeVariableTypeParametersWithBoundingTypes( IType type, IType enclosingType ) {
return TypeLord.replaceTypeVariableTypeParametersWithBoundingTypes( type, enclosingType );
}
@Override
public IMetaType getDefaultType() {
return (IMetaType) MetaType.DEFAULT_TYPE_TYPE.get();
}
@Override
public boolean isSingleModuleMode() {
return ExecutionEnvironment.isDefaultSingleModuleMode();
}
}