package com.strobel.assembler.metadata;
import org.apache.commons.collections4.map.LRUMap;
import com.strobel.core.VerifyArgument;
/**
* Keeps a set of types which failed to load and also uses an LRUCache to reduce the memory footprint for cached lookups.
*
* @author <a href="mailto:ozizka@redhat.com">Ondrej Zizka</a>
*/
public final class NoRetryMetadataSystem extends MetadataSystem
{
private final static TypeDefinition[] PRIMITIVE_TYPES_BY_NAME = new TypeDefinition['Z' - 'B' + 1];
private final static TypeDefinition[] PRIMITIVE_TYPES_BY_DESCRIPTOR = new TypeDefinition[16];
private final LRUMap<String, Boolean> failedTypes = new LRUMap<>(1000);
private final LRUMap<String, TypeDefinition> resolvedTypes = new LRUMap<>(1000);
private final ITypeLoader typeLoader;
public NoRetryMetadataSystem(final ITypeLoader typeLoader)
{
super(typeLoader);
this.typeLoader = typeLoader;
}
public void addTypeDefinition(final TypeDefinition type)
{
VerifyArgument.notNull(type, "type");
resolvedTypes.put(type.getInternalName(), type);
}
@Override
protected TypeDefinition resolveType(final String descriptor, final boolean mightBePrimitive)
{
if (failedTypes.containsKey(descriptor))
return null;
TypeDefinition result = resolveTypeInternal(descriptor, mightBePrimitive);
if (result == null)
failedTypes.put(descriptor, true);
return result;
}
protected TypeDefinition resolveTypeInternal(final String descriptor, final boolean mightBePrimitive)
{
VerifyArgument.notNull(descriptor, "descriptor");
if (mightBePrimitive)
{
if (descriptor.length() == 1)
{
final int primitiveHash = descriptor.charAt(0) - 'B';
if (primitiveHash >= 0 && primitiveHash < PRIMITIVE_TYPES_BY_DESCRIPTOR.length)
{
final TypeDefinition primitiveType = PRIMITIVE_TYPES_BY_DESCRIPTOR[primitiveHash];
if (primitiveType != null)
{
return primitiveType;
}
}
}
else
{
final int primitiveHash = hashPrimitiveName(descriptor);
if (primitiveHash >= 0 && primitiveHash < PRIMITIVE_TYPES_BY_NAME.length)
{
final TypeDefinition primitiveType = PRIMITIVE_TYPES_BY_NAME[primitiveHash];
if (primitiveType != null && descriptor.equals(primitiveType.getName()))
{
return primitiveType;
}
}
}
}
TypeDefinition cachedDefinition = resolvedTypes.get(descriptor);
if (cachedDefinition != null)
{
return cachedDefinition;
}
final Buffer buffer = new Buffer(0);
if (!this.typeLoader.tryLoadType(descriptor, buffer))
{
return null;
}
final TypeDefinition typeDefinition = ClassFileReader.readClass(ClassFileReader.OPTIONS_DEFAULT, this, buffer);
cachedDefinition = resolvedTypes.put(descriptor, typeDefinition);
typeDefinition.setTypeLoader(this.typeLoader);
if (cachedDefinition != null)
{
return cachedDefinition;
}
return typeDefinition;
}
private static int hashPrimitiveName(final String name)
{
if (name.length() < 3)
{
return 0;
}
return (name.charAt(0) + name.charAt(2)) % 16;
}
}