TypeList.java

package Hoot.Runtime.Notes;

import java.util.*;

import Hoot.Runtime.Emissions.Item;
import Hoot.Runtime.Emissions.Emission;
import static Hoot.Runtime.Functions.Utils.*;
import static Hoot.Runtime.Names.Primitive.SerializedTypes;

/**
 * A detailed type list.
 *
 * @author nik <nikboyd@sonic.net>
 * @see "Copyright 2010,2021 Nikolas S Boyd."
 * @see "Permission is granted to copy this work provided this copyright statement is retained in all copies."
 */
public class TypeList extends Item implements NoteSource {

    public TypeList() { super(); }
    protected TypeList(List<DetailedType> types) { this(); this.details.addAll(types); }
    @Override public int hashCode() { return hashList(); }
    @Override public boolean equals(Object types) {
        return hasAny(types) && getClass() == types.getClass() && falseOr(d -> equals(d.list()), (TypeList) types); }

    protected boolean equals(List<DetailedType> details) {
        final int[] index = { 0 };
        if (isEmpty() && details.isEmpty()) return true;
        return list().size() == details.size() &&
            matchAll(list(), d -> d.equals(details.get(index[0]++))); }

    public static TypeList withDetails(DetailedType... types) { return withDetails(wrap(types)); }
    public static TypeList withDetails(List<DetailedType> types) { return new TypeList(types); }

    private boolean exitsBlock = false;
    public boolean exitsBlock() { return this.exitsBlock; }
    public TypeList withExit(DetailedType exitType) {
        if (exitType == null) return this;
        details.add(exitType);
        exitsBlock = true;
        return this; }

    private final List<DetailedType> details = emptyList(DetailedType.class);
    public List<DetailedType> list() { return this.details; }
    @Override public boolean isEmpty() { return list().isEmpty(); }

    public int hashList() { return reduce(map(list(), d -> d.hashCode()), (ha, hb) -> ha ^ hb, 0); }
    public String firstName() { return isEmpty() ? Empty : list().get(0).typeName().fullName(); }
    public Emission firstType() { return isEmpty() ? null : list().get(0).emitItem(); }
    public List<String> metaNames(boolean asType) { return map(listWithoutNatives(), type -> type.metaName(asType)); }
    public List<DetailedType> listMetaTypes() { return map(listWithoutNatives(), type -> type.metaType(true)); }
    public List<DetailedType> listWithoutNatives() {
        return select(list(), it -> !SerializedTypes.contains(it.typeName().name())); }

    public boolean hasType(String typeName) {
        Emission firstType = firstType();
        if (firstType == null) return false;
        return firstType.render().equals(typeName);
    }

    public List<Emission> detailedTypesCode() {
        return (isEmpty()) ? EmptyList : map(list(), type -> type.emitItem()); }

    public List<Emission> derivedTypesCode() {
        return (isEmpty()) ? EmptyList : map(list(), type -> type.emitItem()); }

    public Map<String, Emission> extractGenerics(boolean wantsBases) {
        HashMap<String, Emission> results = emptyMap(Emission.class);
        if (isEmpty()) return results;

        list().forEach(d -> results.putAll(d.extractGenerics(wantsBases)));
        return results;
    }

    public List<String> inferGenerics() {
        ArrayList<String> results = emptyList(String.class);
        if (isEmpty()) return results;

        list().forEach(d -> results.addAll(d.inferGenerics()));
        return results;
    }

    @Override public Emission emitItem() { return emitList(detailedTypesCode()); }
    public Emission emitCode(boolean wantsBases) { return emitList(emitCodeList(wantsBases)); }
    public List<Emission> emitCodeList(boolean wantsBases) { return map(list(), d -> d.emitCode(wantsBases)); }

    public Emission emitDetails(boolean wantsBases) {
        return (list().isEmpty()) ? emitEmpty() : emitDetails(map(list(), d -> d.emitCode(wantsBases))); }

    public Emission emitMetaNames() {
        return (isEmpty()) ? null : emitList(map(metaNames(true), name -> emitItem(name))); }

} // TypeList