NativeTypeMapBuilder.java
package org.klojang.collections;
import org.klojang.check.Check;
import org.klojang.check.Tag;
import org.klojang.check.aux.DuplicateValueException;
import java.util.*;
import static org.klojang.check.CommonChecks.in;
import static org.klojang.check.aux.DuplicateValueException.Usage.KEY;
import static org.klojang.collections.TypeNode.NO_SUBTYPES;
import static org.klojang.util.ClassMethods.isSubtype;
import static org.klojang.util.ClassMethods.isSupertype;
final class NativeTypeMapBuilder<V> implements TypeMapBuilder<V> {
// ================================================================== //
// ======================= [ WritableTypeNode ] ===================== //
// ================================================================== //
private static class WritableTypeNode {
private final WiredList<WritableTypeNode> subclasses = new WiredList<>();
private final WiredList<WritableTypeNode> extensions = new WiredList<>();
private final Class<?> type;
private Object value;
WritableTypeNode(Class<?> type, Object val) {
this.type = type;
this.value = val;
}
TypeNode toTypeNode() {
var subclasses = this.subclasses.stream()
.map(WritableTypeNode::toTypeNode)
.toArray(TypeNode[]::new);
if (subclasses.length == 0) {
subclasses = NO_SUBTYPES;
}
var extensions = this.extensions.stream()
.map(WritableTypeNode::toTypeNode)
.toArray(TypeNode[]::new);
if (extensions.length == 0) {
extensions = NO_SUBTYPES;
}
return new TypeNode(type, value, subclasses, extensions);
}
void addClass(WritableTypeNode node) {
for (var itr = subclasses.wiredIterator(); itr.hasNext(); ) {
var child = itr.next();
if (isSupertype(node.type, child.type)) {
itr.remove();
node.addClass(child);
} else if (isSubtype(node.type, child.type)) {
child.addClass(node);
return;
}
}
for (var itr = extensions.wiredIterator(); itr.hasNext(); ) {
var child = itr.next();
if (isSubtype(node.type, child.type)) {
child.addClass(node);
return;
}
}
subclasses.add(node);
}
void addInterface(WritableTypeNode node) {
for (var itr = subclasses.wiredIterator(); itr.hasNext(); ) {
var child = itr.next();
if (isSupertype(node.type, child.type)) {
itr.remove();
node.addClass(child);
}
}
for (var itr = extensions.wiredIterator(); itr.hasNext(); ) {
var child = itr.next();
if (isSupertype(node.type, child.type)) {
itr.remove();
node.addInterface(child);
} else if (isSubtype(node.type, child.type)) {
child.addInterface(node);
return;
}
}
extensions.add(node);
}
}
// ================================================================== //
// ===================== [ TypeGraphMapBuilder ] ==================== //
// ================================================================== //
private final Set<Class<?>> all = new HashSet<>();
private final List<WritableTypeNode> classes = new ArrayList<>();
private final List<WritableTypeNode> interfaces = new ArrayList<>();
private final WritableTypeNode root;
private boolean autobox = true;
NativeTypeMapBuilder() {
this.root = new WritableTypeNode(Object.class, null);
}
@Override
public NativeTypeMapBuilder<V> autobox(boolean autobox) {
this.autobox = autobox;
return this;
}
@Override
public NativeTypeMapBuilder<V> add(Class<?> type, V value) {
Check.notNull(type, Tag.TYPE)
.isNot(in(), all, () -> new DuplicateValueException(KEY, type));
Check.notNull(value, Tag.VALUE);
if (type == Object.class) {
root.value = value;
} else if (type.isInterface()) {
interfaces.add(new WritableTypeNode(type, value));
} else {
classes.add(new WritableTypeNode(type, value));
}
all.add(type);
return this;
}
@Override
public NativeTypeMapBuilder<V> addMultiple(V value, Class<?>... types) {
Check.notNull(types, "types");
Arrays.stream(types).forEach(t -> add(t, value));
return this;
}
@Override
public NativeTypeMap<V> freeze() {
for (var node : classes) {
root.addClass(node);
}
for (var node : interfaces) {
root.addInterface(node);
}
return new NativeTypeMap<>(root.toTypeNode(), all.size(), autobox);
}
}