/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.parser;
import gw.lang.parser.ITypeUsesMap;
import gw.lang.parser.statements.IUsesStatement;
import gw.lang.reflect.IType;
import gw.lang.reflect.INamespaceType;
import gw.lang.reflect.TypeSystem;
import gw.util.DynamicArray;
import gw.util.GosuClassUtil;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
/**
*/
public class TypeUsesMap implements ITypeUsesMap
{
private boolean _bSupportRelativePackageResolution;
private HashMap<String, String> _specialTypeUsesByRelativeName;
private HashMap<String, String> _typeUsesByRelativeName;
private DynamicArray<String> _defaultNamespaces;
private DynamicArray<String> _namespaces;
private DynamicArray<String> _specialNamespaces; // Namespaces that get searched by default
private HashMap<String, IUsesStatement> _usesStmts;
private boolean _locked = false;
public TypeUsesMap( List<String> specialTypeUses )
{
this();
for (String typeUse : specialTypeUses) {
addToSpecialTypeUses(typeUse);
}
}
public TypeUsesMap()
{
_specialTypeUsesByRelativeName = new HashMap<String, String>();
_typeUsesByRelativeName = new HashMap<String, String>();
_namespaces = new DynamicArray<String>();
_usesStmts = new HashMap<String, IUsesStatement>();
_defaultNamespaces = new DynamicArray<String>();
_specialNamespaces = new DynamicArray<String>();
_defaultNamespaces.add( "gw.lang." );
_defaultNamespaces.add( "" );
}
/**
* @noinspection CloneDoesntCallSuperClone
*/
@SuppressWarnings({"CloneDoesntDeclareCloneNotSupportedException"})
public Object clone()
{
return copy();
}
@SuppressWarnings({"unchecked"})
public ITypeUsesMap copy()
{
TypeUsesMap copy = new TypeUsesMap();
copy._bSupportRelativePackageResolution = _bSupportRelativePackageResolution;
copy._specialTypeUsesByRelativeName = (HashMap<String, String>) _specialTypeUsesByRelativeName.clone();
copy._defaultNamespaces = (DynamicArray<String>)_defaultNamespaces.clone();
copy._specialNamespaces = (DynamicArray<String>)_specialNamespaces.clone();
copy._typeUsesByRelativeName = (HashMap<String, String>)_typeUsesByRelativeName.clone();
copy._namespaces = (DynamicArray<String>)_namespaces.clone();
return copy;
}
@SuppressWarnings({"unchecked"})
public ITypeUsesMap copyLocalScope()
{
TypeUsesMap copy = new TypeUsesMap();
copy._bSupportRelativePackageResolution = _bSupportRelativePackageResolution;
copy._specialTypeUsesByRelativeName = _specialTypeUsesByRelativeName;
copy._defaultNamespaces = (DynamicArray<String>)_defaultNamespaces.clone();
copy._specialNamespaces = (DynamicArray<String>)_specialNamespaces.clone();
copy._typeUsesByRelativeName = (HashMap<String, String>)_typeUsesByRelativeName.clone();
copy._usesStmts = (HashMap<String, IUsesStatement>)_usesStmts.clone();
copy._namespaces = (DynamicArray<String>)_namespaces.clone();
return copy;
}
public ITypeUsesMap lock()
{
_locked = true;
return this;
}
@Override
public boolean containsType(String typeName) {
for (Object o : getTypeUses()) {
String used = (String) o;
if (used.endsWith(".")) {
if (used.lastIndexOf('.') == typeName.lastIndexOf('.')) {
return typeName.startsWith(used);
}
} else if (used.equals(typeName)) {
return true;
}
}
return false;
}
public Set<String> getTypeUses()
{
Set<String> combined = getNamespaces();
combined.addAll( _specialTypeUsesByRelativeName.values() );
combined.addAll( _typeUsesByRelativeName.values() );
return combined;
}
@Override
public Set<String> getNamespaces() {
Set<String> combined = new LinkedHashSet<String>();
combined.addAll( _namespaces );
combined.addAll( _defaultNamespaces );
combined.addAll( _specialNamespaces );
return combined;
}
public void addToTypeUses( String strType )
{
checkLocked();
if( strType != null )
{
strType = strType.replace( '$', '.' );
try
{
addToTypeUses( strType, _typeUsesByRelativeName, _namespaces );
}
catch( ClassNotFoundException e )
{
throw new RuntimeException( e );
}
}
}
public void addToTypeUses( IUsesStatement usesSmt )
{
String strType = usesSmt.getTypeName();
addToTypeUses( strType );
_usesStmts.put( strType.intern(), usesSmt );
}
public Set<IUsesStatement> getUsesStatements()
{
return new HashSet<IUsesStatement>( _usesStmts.values() );
}
public void addToDefaultTypeUses( String strType )
{
checkLocked();
if( strType.endsWith( ".*" ) )
{
// Store them so that they end with a dot
_defaultNamespaces.add( strType.substring( 0, strType.length() - 1 ).intern() );
} else {
_defaultNamespaces.add( strType.intern() );
}
}
private void checkLocked()
{
if( _locked )
{
throw new IllegalStateException( "You cannot add to a locked TypeUsesMap. You must make a copy of this map " +
"using the .copy() method in order to mutate it." );
}
}
public void addToSpecialTypeUses( String strType )
{
checkLocked();
strType = strType.replace( '$', '.' );
try
{
addToTypeUses( strType, _specialTypeUsesByRelativeName, _specialNamespaces );
}
catch( ClassNotFoundException e )
{
throw new RuntimeException( e );
}
}
public IType resolveType( String strRelativeName )
{
IType result = null;
if (strRelativeName.indexOf('.') != -1 ) {
// Ok, if it might be absolute, ask for it by full name first.
result = TypeLoaderAccess.instance().getByFullNameIfValid(strRelativeName);
}
if (result == null)
{
if( _specialTypeUsesByRelativeName.containsKey( strRelativeName ) )
{
result = TypeLoaderAccess.instance().getByFullNameIfValid(_specialTypeUsesByRelativeName.get(strRelativeName));
}
else if( _typeUsesByRelativeName.containsKey( strRelativeName ) )
{
result = TypeLoaderAccess.instance().getByFullNameIfValid(_typeUsesByRelativeName.get(strRelativeName));
}
else
{
result = resolveTypesInAllNamespaces( strRelativeName );
}
if (result == null) {
result = TypeLoaderAccess.instance().getByFullNameIfValid(strRelativeName);
}
}
if( result == null )
{
result = resolveSubType( strRelativeName );
}
return result;
}
private IType resolveSubType( String strRelativeName )
{
int iDot = strRelativeName.lastIndexOf( '.' );
if( iDot > 0 && strRelativeName.length() > iDot + 1 )
{
String strPrefix = strRelativeName.substring( 0, iDot );
String strRemaining = strRelativeName.substring( iDot );
IType enclosingType = resolveType( strPrefix );
if( enclosingType != null )
{
return TypeLoaderAccess.instance().getByFullNameIfValid(enclosingType.getName() + strRemaining);
}
}
return null;
}
public void clearNonDefaultTypeUses()
{
_typeUsesByRelativeName.clear();
_namespaces.clear();
_usesStmts.clear();
}
private IType resolveTypesInAllNamespaces( String strRelativeName )
{
if (strRelativeName.indexOf('.') < 0) {
for (int i = 0; i < _specialNamespaces.size; i++) {
IType type = resolveType( strRelativeName, (String) _specialNamespaces.data[i]);
if( type != null ) {
return type;
}
}
}
for (int i = 0; i < _namespaces.size; i++) {
IType type = resolveType( strRelativeName, (String) _namespaces.data[i] );
if( type != null ) {
return type;
}
}
for (int i = 0; i < _defaultNamespaces.size; i++) {
IType type = resolveType( strRelativeName, (String) _defaultNamespaces.data[i] );
if( type != null ) {
return type;
}
}
return null;
}
private IType resolveType( String strRelativeName, String strNs ) {
String strQualifiedName = strNs + strRelativeName;
IType type = TypeLoaderAccess.instance().getByFullNameIfValid(strQualifiedName);
if( type != null && !isSupportRelativePackageResolution() )
{
type = verifyTypeNameDoesNotHaveRelativePackage( type, strNs, strRelativeName );
}
return type;
}
// For example,
// type = "com.abc.Foo"
// strNs = "com."
// strRelativeName = "abc.Foo"
//
// ns = abc
// token = abc
// token2 = abc
// return null
private IType verifyTypeNameDoesNotHaveRelativePackage(IType type, String strNs, String strRelativeName) {
String ns = type.getNamespace();
if( ns != null && ns.contains(strNs) ) {
ns = ns.substring( strNs.length() );
//noinspection LoopStatementThatDoesntLoop
for( StringTokenizer tokenizer = new StringTokenizer( ns, "." ); tokenizer.hasMoreTokens(); ) {
String token = tokenizer.nextToken();
for( StringTokenizer t2 = new StringTokenizer( strRelativeName, "." ); t2.hasMoreTokens(); ) {
String token2 = t2.nextToken();
if( token.equals( token2 ) ) {
return null;
}
}
return type;
}
}
return type;
}
public INamespaceType resolveRelativeNamespaceInAllNamespaces( String strRelativeName )
{
if( !isSupportRelativePackageResolution() )
{
return null;
}
for (int i = 0; i < _specialNamespaces.size; i++) {
String strQualifiedName = _specialNamespaces.data[i] + strRelativeName;
INamespaceType type = TypeSystem.getNamespace(strQualifiedName);
if (type != null) {
return type;
}
}
for (int i = 0; i < _namespaces.size; i++) {
String strQualifiedName = _namespaces.data[i] + strRelativeName;
INamespaceType type = TypeSystem.getNamespace(strQualifiedName);
if (type != null) {
return type;
}
}
for (int i = 0; i < _defaultNamespaces.size; i++) {
String strQualifiedName = _defaultNamespaces.data[i] + strRelativeName;
INamespaceType type = TypeSystem.getNamespace(strQualifiedName);
if (type != null) {
return type;
}
}
return null;
}
public boolean isSupportRelativePackageResolution() {
return _bSupportRelativePackageResolution;
}
public void setSupportRelativePackageResolution( boolean bSupportRelativePackageResolution ) {
_bSupportRelativePackageResolution = bSupportRelativePackageResolution;
}
private void addToTypeUses( String strQualifiedType, Map<String, String> mapQualifiedNameByRelativeName, List<String> namespacesSet ) throws ClassNotFoundException
{
checkLocked();
if( strQualifiedType.endsWith( ".*" ) )
{
// Store them so that they end with a dot
String ns = strQualifiedType.substring( 0, strQualifiedType.length() - 1 ).intern();
if( !namespacesSet.contains( ns ) )
{
namespacesSet.add( ns );
}
}
else
{
String strRelativeName = GosuClassUtil.getNameNoPackage( strQualifiedType );
strRelativeName = strRelativeName.replace( '$', '.' );
mapQualifiedNameByRelativeName.put( strRelativeName.intern(), strQualifiedType );
}
}
}