NativeTypeMap.java

package org.klojang.collections;

import org.klojang.check.Check;
import org.klojang.util.ArrayType;

import java.util.*;

import static java.util.AbstractMap.SimpleImmutableEntry;
import static org.klojang.check.CommonChecks.instanceOf;
import static org.klojang.util.ClassMethods.*;
import static org.klojang.util.CollectionMethods.implode;

final class NativeTypeMap<V> extends ImmutableMap<Class<?>, V> implements TypeMap<V> {

  private final boolean autobox;
  private final TypeNode root;
  private final int size;

  private Set<Class<?>> keys;
  private Collection<V> values;
  private Set<Entry<Class<?>, V>> entries;

  NativeTypeMap(TypeNode root, int size, boolean autobox) {
    this.autobox = autobox;
    this.root = root;
    this.size = size;
  }

  @Override
  public V get(Object key) {
    Class<?> type = Check.notNull(key)
        .is(instanceOf(), Class.class)
        .ok(Class.class::cast);
    V val;
    if (type.isPrimitive()) {
      if ((val = root.getPrimitive(type)) == null) {
        if (autobox) {
          val = root.get(box(type));
        }
        if (val == null) {
          val = root.value();
        }
      }
    } else if (isDeeplyPrimitiveArray(type)) {
      if ((val = root.getPrimitive(type)) == null) {
        if (autobox) {
          val = root.get(ArrayType.forClass(type).box());
        }
        if (val == null) {
          val = root.value();
        }
      }
    } else {
      val = root.get(type);
    }
    return val;
  }

  @Override
  public boolean containsKey(Object key) {
    Class<?> type = Check.notNull(key)
        .is(instanceOf(), Class.class)
        .ok(Class.class::cast);
    boolean found;
    if (root.value != null) {
      found = true;
    } else if (type.isPrimitive()) {
      if (autobox) {
        found = containsPrimitiveOrBoxedType(type, box(type));
      } else {
        found = containsPrimitiveType(type);
      }
    } else if (isDeeplyPrimitiveArray(type)) {
      if (autobox) {
        Class<?> boxed = ArrayType.forClass(type).box();
        found = containsPrimitiveOrBoxedType(type, boxed);
      } else {
        found = containsPrimitiveType(type);
      }
    } else if (!type.isInterface()) {
      found = containsExactOrSuperType(type, root.subclasses);
      if (!found) {
        found = containsExactOrSuperType(type, root.extensions);
      }
    } else {
      found = containsExactOrSuperType(type, root.extensions);
    }
    return found;
  }

  @Override
  public boolean containsValue(Object value) {
    return values().contains(value);
  }

  /**
   * Returns a breadth-first view of the type hierarchy within this {@code Map}.
   *
   * @return a breadth-first view of the type hierarchy within this {@code Map}
   */
  @Override
  public Set<Class<?>> keySet() {
    if (keys == null) {
      List<Class<?>> bucket = new ArrayList<>(size);
      if (root.value() != null) { // map contains Object.class
        bucket.add(Object.class);
      }
      root.collectTypes(bucket);
      keys = ArraySet.copyOf(bucket);
    }
    return keys;
  }

  @Override
  public Collection<V> values() {
    if (values == null) {
      List<V> bucket = new ArrayList<>(size);
      if (root.value() != null) {
        bucket.add(root.value());
      }
      root.collectValues(bucket);
      values = ArraySet.copyOf(new HashSet<>(bucket));
    }
    return values;
  }

  @Override
  public Set<Entry<Class<?>, V>> entrySet() {
    if (entries == null) {
      List<Entry<Class<?>, V>> bucket = new ArrayList<>(size);
      if (root.value() != null) {
        bucket.add(new SimpleImmutableEntry<>(Object.class, root.value()));
      }
      root.collectEntries(bucket);
      entries = Set.copyOf(bucket);
    }
    return entries;
  }

  @Override
  public int size() {
    return size;
  }

  @Override
  public boolean isEmpty() {
    return size == 0;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o instanceof Map<?, ?> m) {
      if (size == m.size()) {
        return entrySet().equals(m.entrySet());
      }
    }
    return false;
  }

  private int hash;

  @Override
  public int hashCode() {
    if (hash == 0) {
      hash = entrySet().hashCode();
    }
    return hash;
  }

  @Override
  public String toString() {
    return '[' + implode(entrySet()) + ']';
  }

  private boolean containsPrimitiveType(Class<?> type) {
    for (var node : root.subclasses) {
      if (node.type == type) {
        return true;
      }
    }
    return false;
  }

  private boolean containsPrimitiveOrBoxedType(Class<?> primitive, Class<?> boxed) {
    for (var node : root.subclasses) {
      if (node.type == primitive || isSupertype(node.type, boxed)) {
        return true;
      }
    }
    return false;
  }

  private boolean containsExactOrSuperType(Class<?> type, TypeNode[] nodes) {
    for (var node : nodes) {
      if (isSupertype(node.type, type)) {
        return true;
      }
    }
    return false;
  }

}