TypeNode.java
package org.klojang.collections;
import java.util.AbstractMap;
import java.util.List;
import java.util.Map;
import static org.klojang.util.ClassMethods.isSupertype;
final class TypeNode {
static final TypeNode[] NO_SUBTYPES = new TypeNode[0];
final Class<?> type;
final Object value;
final TypeNode[] subclasses;
final TypeNode[] extensions;
TypeNode(Class<?> type,
Object value,
TypeNode[] subclasses,
TypeNode[] extensions) {
this.type = type;
this.value = value;
this.subclasses = subclasses;
this.extensions = extensions;
}
@SuppressWarnings({"unchecked"})
<T> T value() {
return (T) value;
}
<T> T get(Class<?> type) {
return type.isInterface() ? findInterface(type) : findClass(type);
}
@SuppressWarnings({"unchecked"})
<T> T getPrimitive(Class<?> type) {
return (T) findClass(type, subclasses);
}
void collectTypes(List<Class<?>> bucket) {
for (var node : extensions) {
bucket.add((node.type));
}
for (var node : subclasses) {
bucket.add(node.type);
}
for (var node : extensions) {
node.collectTypes(bucket);
}
for (var node : subclasses) {
node.collectTypes(bucket);
}
}
<E> void collectValues(List<E> bucket) {
for (var node : extensions) {
bucket.add(node.value());
node.collectValues(bucket);
}
for (var node : subclasses) {
bucket.add(node.value());
node.collectValues(bucket);
}
}
<E> void collectEntries(List<Map.Entry<Class<?>, E>> bucket) {
for (var node : extensions) {
bucket.add(new AbstractMap.SimpleImmutableEntry<>(node.type, node.value()));
node.collectEntries(bucket);
}
for (var node : subclasses) {
bucket.add(new AbstractMap.SimpleImmutableEntry<>(node.type, node.value()));
node.collectEntries(bucket);
}
}
@SuppressWarnings({"unchecked"})
private <T> T findClass(Class<?> type) {
if (!isSupertype(this.type, type)) {
return null;
}
Object val;
if ((val = findAsSubclass(type)) == null) {
if ((val = findAsImpl(type)) == null) {
val = this.value;
}
}
return (T) val;
}
@SuppressWarnings({"unchecked"})
private <T> T findInterface(Class<?> type) {
if (!isSupertype(this.type, type)) {
return null;
}
Object val;
if ((val = findAsExtension(type)) == null) {
val = this.value;
}
return (T) val;
}
private Object findAsSubclass(Class<?> type) {
return findClass(type, subclasses);
}
private Object findAsImpl(Class<?> type) {
return findClass(type, extensions);
}
private Object findAsExtension(Class<?> type) {
for (TypeNode node : extensions) {
Object val = node.findInterface(type);
if (val != null) {
return val;
}
}
return null;
}
private static Object findClass(Class<?> type, TypeNode[] nodes) {
for (TypeNode node : nodes) {
Object val = node.findClass(type);
if (val != null) {
return val;
}
}
return null;
}
}