NonExpandingTypeMap.java
package org.klojang.collections;
import org.klojang.check.Check;
import org.klojang.util.ArrayType;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.klojang.check.CommonChecks.instanceOf;
import static org.klojang.util.ClassMethods.*;
abstract sealed class NonExpandingTypeMap<V> extends
ImmutableMap<Class<?>, V> implements TypeMap<V> permits
TypeTreeMap, FixedTypeMap {
private final boolean autobox;
NonExpandingTypeMap(boolean autobox) {
this.autobox = autobox;
}
abstract Map<Class<?>, V> backend();
@Override
public V get(Object key) {
Class<?> type = Check.notNull(key)
.is(instanceOf(), Class.class)
.ok(Class.class::cast);
return find(type);
}
@Override
public boolean containsKey(Object key) {
Class<?> type = Check.notNull(key)
.is(instanceOf(), Class.class)
.ok(Class.class::cast);
return find(type) != null;
}
private V find(Class<?> type) {
V val;
if ((val = backend().get(type)) != null) {
return val;
}
if (type.isArray()) {
val = findArrayType(type);
} else if (type.isPrimitive()) {
if (autobox) {
val = find(box(type));
}
} else if (type.isInterface()) {
val = findInterface(type);
} else if ((val = findSuperClass(type)) == null) {
val = findInterface(type);
}
if (val == null) {
return getDefaultValue();
}
return val;
}
private V findSuperClass(Class<?> type) {
List<Class<?>> supertypes = getAncestors(type);
for (Class<?> c : supertypes) {
if (c == Object.class) {
break; // that's our last resort
}
V val = backend().get(c);
if (val != null) {
return val;
}
}
return null;
}
private V findInterface(Class<?> type) {
Set<Class<?>> supertypes = getAllInterfaces(type);
for (Class<?> c : supertypes) {
V val = backend().get(c);
if (val != null) {
return val;
}
}
return null;
}
private V findArrayType(Class<?> type) {
ArrayType arrayType = ArrayType.forClass(type);
if (arrayType.baseType().isPrimitive()) {
if (autobox) {
return find(arrayType.box());
}
}
V result;
if (arrayType.baseType().isInterface()) {
if ((result = findInterfaceArray(arrayType)) != null) {
return result;
}
} else if ((result = findSuperClassArray(arrayType)) != null) {
return result;
} else if ((result = findInterfaceArray(arrayType)) != null) {
return result;
}
return backend().get(Object[].class);
}
private V findSuperClassArray(ArrayType arrayType) {
List<Class<?>> supertypes = getAncestors(arrayType.baseType());
for (Class<?> c : supertypes) {
if (c == Object.class) {
break;
}
V val = backend().get(arrayType.toClass(c));
if (val != null) {
return val;
}
}
return null;
}
private V findInterfaceArray(ArrayType arrayType) {
Set<Class<?>> supertypes = getAllInterfaces(arrayType.baseType());
for (Class<?> c : supertypes) {
V val = backend().get(arrayType.toClass(c));
if (val != null) {
return val;
}
}
return null;
}
private V defVal;
// The value associated with Object.class, or null if
// the map does not contain key Object.class
private V getDefaultValue() {
if (defVal == null) {
defVal = backend().get(Object.class);
}
return defVal;
}
@Override
public int size() {
return backend().size();
}
@Override
public boolean isEmpty() {
return backend().isEmpty();
}
@Override
public boolean containsValue(Object value) {
return backend().containsValue(value);
}
@Override
public int hashCode() {
return backend().hashCode();
}
@Override
public boolean equals(Object obj) {
return backend().equals(obj);
}
@Override
public String toString() {
return backend().toString();
}
}