package scotch.compiler.syntax.value; import static java.util.Collections.sort; import static scotch.util.Pair.pair; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Stream; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import scotch.compiler.syntax.type.VariableType; import scotch.compiler.syntax.reference.ClassReference; import scotch.util.Pair; public class InstanceMap { private static final InstanceMap EMPTY = new InstanceMap(ImmutableList.of(), ImmutableMap.of()); public static Builder builder() { return new Builder(); } public static InstanceMap empty() { return EMPTY; } private final List<VariableType> arity; private final Map<VariableType, List<ClassReference>> instances; private InstanceMap(List<VariableType> arity, Map<VariableType, List<ClassReference>> instances) { this.arity = ImmutableList.copyOf(arity); this.instances = ImmutableMap.copyOf(instances); } @Override public boolean equals(Object o) { if (o == this) { return true; } else if (o instanceof InstanceMap) { InstanceMap other = (InstanceMap) o; return Objects.equals(arity, other.arity) && Objects.equals(instances, other.instances); } else { return false; } } public List<ClassReference> getInstances(VariableType type) { return instances.getOrDefault(type.simplify(), ImmutableList.of()); } @Override public int hashCode() { return Objects.hash(arity, instances); } public boolean isEmpty() { return arity.isEmpty(); } public Stream<Pair<VariableType, List<ClassReference>>> stream() { return arity.stream().map(type -> pair(type, instances.get(type))); } public static class Builder { private final List<VariableType> arity; private final Map<VariableType, List<ClassReference>> instances; private Builder() { arity = new ArrayList<>(); instances = new HashMap<>(); } public Builder addInstance(VariableType type, ClassReference instance) { addInstance_(type.simplify(), instance); return this; } public InstanceMap build() { Map<VariableType, List<ClassReference>> sortedInstances = new HashMap<>(); instances.forEach((type, list) -> { sort(list, (left, right) -> left.getSymbol().compareTo(right.getSymbol())); sortedInstances.put(type, list); }); return new InstanceMap(arity, sortedInstances); } private void addInstance_(VariableType type, ClassReference instance) { if (!arity.contains(type)) { arity.add(type); } List<ClassReference> list = instances.computeIfAbsent(type, k -> new ArrayList<>()); if (!list.contains(instance)) { list.add(instance); } } } }