/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.gosu.parser; import gw.lang.reflect.AbstractType; import gw.lang.reflect.ICompoundType; import gw.lang.reflect.INonLoadableType; import gw.lang.reflect.IType; import gw.lang.reflect.ITypeInfo; import gw.lang.reflect.ITypeLoader; import gw.lang.reflect.ITypeLoaderListener; import gw.lang.reflect.AbstractTypeSystemListener; import gw.lang.reflect.TypeSystem; import gw.lang.reflect.gs.IGenericTypeVariable; import gw.lang.reflect.java.JavaTypes; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; /** */ public class CompoundType extends AbstractType implements INonLoadableType, ICompoundType { private static final Map<String,CompoundType> CACHE = new ConcurrentHashMap<String,CompoundType>(); private static ITypeLoaderListener g_refreshListener; transient private SortedSet<IType> _types; private String _strRelativeName; private String _strName; private boolean _bInterface; transient private ITypeInfo _typeInfo; public static CompoundType get( IType... types ) { HashSet<IType> typeSet = new HashSet<IType>(); typeSet.addAll( Arrays.asList( types ) ); return get( typeSet ); } public static CompoundType get( Set<IType> types ) { String strName = getNameFrom( types, false ); CompoundType compoundType = CACHE.get( strName ); if( compoundType == null ) { TypeSystem.lock(); try { compoundType = CACHE.get( strName ); if( compoundType == null ) { listenToTypeSystemRefresh(); compoundType = new CompoundType( types, strName ); CACHE.put( strName, compoundType ); } } finally { TypeSystem.unlock(); } } return compoundType; } private static void listenToTypeSystemRefresh() { if( g_refreshListener != null ) { return; } TypeLoaderAccess.instance().addTypeLoaderListenerAsWeakRef( g_refreshListener = new AbstractTypeSystemListener() { @Override public void refreshed() { CACHE.clear(); } } ); } private CompoundType( Set<IType> types, String strName ) { _types = new TreeSet<IType>( TypeComparator.INSTANCE ); _types.addAll(types); _strName = strName; _strRelativeName = getNameFrom( types, true ); _bInterface = areAllTypesInterfaces( types ); } private boolean areAllTypesInterfaces( Set<IType> types ) { for( IType type: types ) { if( !type.isInterface() ) { return false; } } return true; } private static String getNameFrom( Set<IType> types, boolean bRelative ) { StringBuilder sbName = new StringBuilder(); List<IType> sorted = new ArrayList<IType>( types ); Collections.sort( sorted, TypeComparator.INSTANCE ); for( int i = 0; i < sorted.size(); i++ ) { IType type = sorted.get( i ); sbName.append( bRelative ? type.getRelativeName() : type.getName() ); if( i != sorted.size()-1 ) { sbName.append( " & " ); } } return sbName.toString(); } private static class TypeComparator implements Comparator<IType> { private static TypeComparator INSTANCE = new TypeComparator(); @Override public int compare( IType o1, IType o2 ) { return o1.getName().compareTo( o2.getName() ); } } public Set<IType> getTypes() { return _types; } @Override public String getName() { return _strName; } @Override public String getDisplayName() { return getName(); } @Override public String getRelativeName() { return _strRelativeName; } @Override public String getNamespace() { return null; } @Override public ITypeLoader getTypeLoader() { return null; } @Override public IType getSupertype() { return null; } @Override public IType getEnclosingType() { return null; } @Override public IType getGenericType() { return null; } @Override public boolean isFinal() { return true; } @Override public boolean isInterface() { return _bInterface; } @Override public boolean isEnum() { return false; } @Override public IType[] getInterfaces() { ArrayList<IType> interfaces = new ArrayList<IType>(); for (IType type : _types) { if (type.isInterface()) { interfaces.add(type); } for (IType anInterface : type.getInterfaces()) { if (!interfaces.contains(anInterface)) { interfaces.add(type); } } } return interfaces.toArray(new IType[interfaces.size()]); } @Override public boolean isParameterizedType() { return false; } @Override public boolean isGenericType() { return false; } @Override public IGenericTypeVariable[] getGenericTypeVariables() { return new IGenericTypeVariable[0]; } @Override public IType getParameterizedType( IType... ofType ) { return null; } @Override public IType[] getTypeParameters() { return IType.EMPTY_ARRAY; } @Override public Set<? extends IType> getAllTypesInHierarchy() { Set<IType> allTypes = new HashSet<IType>(); for (IType iType : _types) { allTypes.addAll(iType.getAllTypesInHierarchy()); } allTypes.add( this ); return allTypes; } @Override public boolean isArray() { return false; } @Override public boolean isPrimitive() { return false; } @Override public IType getArrayType() { return JavaTypes.OBJECT().getArrayType(); } @Override public Object makeArrayInstance( int iLength ) { return new Object[iLength]; } @Override public Object getArrayComponent( Object array, int iIndex ) throws IllegalArgumentException, ArrayIndexOutOfBoundsException { return ((Object[])array)[iIndex]; } @Override public void setArrayComponent( Object array, int iIndex, Object value ) throws IllegalArgumentException, ArrayIndexOutOfBoundsException { ((Object[])array)[iIndex] = value; } @Override public int getArrayLength( Object array ) throws IllegalArgumentException { return ((Object[])array).length; } @Override public IType getComponentType() { return null; } @Override public boolean isAssignableFrom( IType type ) { // Short-circuit if the types are the same if( type == this ) { return true; } // Short-circuit if the given type is null if( type == null ) { return false; } // Supplied type must be assignable to all composed types for( IType t : _types ) { if( !t.isAssignableFrom( type ) ) { return false; } } return true; } @Override public boolean isMutable() { return false; } @Override public ITypeInfo getTypeInfo() { if( _typeInfo == null ) { _typeInfo = new CompoundTypeInfo( this ); } return _typeInfo; } @Override public void unloadTypeInfo() { _typeInfo = null; } private void writeObject( ObjectOutputStream out ) throws IOException { out.defaultWriteObject(); out.writeInt( _types.size() ); for( IType ref : _types ) { out.writeObject( ref ); } } private void readObject( ObjectInputStream in ) throws IOException, ClassNotFoundException { in.defaultReadObject(); int iSize = in.readInt(); _types = new TreeSet<IType>( ); for( int i = 0; i < iSize; i++ ) { _types.add( (IType)in.readObject() ); } } @Override public Object readResolve() throws ObjectStreamException { return this; } @Override public boolean isValid() { for( IType type : _types ) { if( !type.isValid() ) { return false; } } return true; } @Override public int getModifiers() { return 0; } @Override public boolean isAbstract() { return true; } @Override public String toString() { return getName(); } @Override public boolean isDiscarded() { return false; } @Override public void setDiscarded( boolean bDiscarded ) { } @Override public boolean isCompoundType() { return true; } @Override public Set<IType> getCompoundTypeComponents() { return Collections.unmodifiableSet( getTypes() ); } }