DetailedType.java

package Hoot.Runtime.Notes;

import java.util.*;
import java.lang.reflect.Type;

import Hoot.Runtime.Names.Global;
import Hoot.Runtime.Names.TypeName;
import Hoot.Runtime.Behaviors.Scope;
import Hoot.Runtime.Emissions.Emission;
import Hoot.Runtime.Emissions.NamedItem;
import static Hoot.Runtime.Emissions.Emission.*;
import static Hoot.Runtime.Names.Keyword.Behavior;
import static Hoot.Runtime.Names.Keyword.Object;
import static Hoot.Runtime.Names.Name.Metaclass;
import static Hoot.Runtime.Names.Name.Metatype;
import static Hoot.Runtime.Names.Name.removeTail;
import static Hoot.Runtime.Functions.Utils.*;

/**
 * A detailed type.
 *
 * @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 DetailedType extends NamedItem implements NoteSource {

    protected DetailedType() { super(Scope.current()); }
    protected DetailedType(Global g, TypeList list) { this(); this.global = g; this.details = list; }

    @Override public int hashCode() {
        return isEmpty() ? super.hashCode() : typeName().hashCode() ^ details().hashCode(); }

    protected boolean equals(DetailedType d) {
        return (isEmpty() && d.isEmpty()) ||
            typeName().equals(d.typeName()) && details().equals(d.details()); }

    @Override public boolean equals(Object type) {
        return hasAny(type) && getClass() == type.getClass() && falseOr(d -> this.equals(d), (DetailedType) type); }

    public static DetailedType from(Type aType) { return fromType(aType.getTypeName()); }

    static final String[] Angles = { "<", ">", ",", };
    public static DetailedType fromType(String type) {
        boolean arrayed = type.endsWith(Arrayed);
        if (arrayed) type = removeTail(type, Arrayed);

        if (type.endsWith(Angles[1])) {
            String name = type.substring(0, type.indexOf(Angles[0]));
            String tails = type.substring(1 + type.indexOf(Angles[0]), type.length() - 1);
            return fromTypes(name, tails.split(Angles[2])).makeArrayed(arrayed);
        }
        else {
            return named(type).makeArrayed(arrayed);
        }
    }

    public static DetailedType fromTypes(String name, String... details) {
        return with(Global.named(name), TypeList.withDetails(map(wrap(details), d -> DetailedType.fromType(d)))); }

    public static DetailedType from(TypeName type) { return with(type.toGlobal()); }
    public static DetailedType named(String name) { return with(TypeName.fromTail(name).toGlobal()); }
    public static DetailedType with(Global global) { return with(global, TypeList.withDetails()); }
    public static DetailedType with(Global global, DetailedType detail) { return with(global, TypeList.withDetails(detail)); }
    public static DetailedType with(Global global, TypeList details) { return new DetailedType(global, details); }

    protected Global global;
    public Global typeName() { return this.global; }
    public String simpleName() { return typeName().name(); }
    public boolean isGeneric() { return typeName().isGenericType(); }
    @Override public boolean isEmpty() { return false; }

    public TypeName toTypeName() { return typeName().typeResolver(); }
    public String metaName(boolean asType) { return typeName().metaName(asType); }
    public DetailedType metaType(boolean asType) { return named(metaName(asType)); }

    protected TypeList details;
    public TypeList details() { return this.details; }
    public DetailedType basicType() { return extendsBase() ? details().list().get(0) : null; }

    protected boolean extendsBase = false;
    public boolean extendsBase() { return this.extendsBase; }
    public DetailedType makeExtensive() { this.extendsBase = true; return this; }

    public boolean isArrayed() { return typeName().isArrayed(); }
    public DetailedType makeArrayed(boolean value) { typeName().makeArrayed(value); return this; }

    protected boolean argued = false;
    public DetailedType makeArgued(boolean value) { this.argued = value; return this; }

    public Map<String, Emission> extractGenerics(boolean wantsBases) {
        HashMap<String, Emission> results = emptyMap(Emission.class);
        if (this.isGeneric()) results.put(typeName().shortName(), emitCode(wantsBases));
        results.putAll(details().extractGenerics(wantsBases));
        return results; }

    public List<String> inferGenerics() {
        ArrayList<String> results = emptyList(String.class);
        results.addAll(details().inferGenerics());
        results.addAll(typeName().inferGenerics());
        return results; }

    public Emission emitMetaName(boolean asType) {
        Global typeName = typeName();
        return (typeName.isEmpty() ? emitEmpty() : typeName.emitMetaName(asType)); }

    public Emission emitType() { return (typeName().isEmpty() ? emitEmpty() : typeName().emitItem()); }
    public Emission emitNakedType() { return emitItem(toTypeName().convertedTail()); }

    public Emission emitDetails(boolean wantsBases) { return details().emitDetails(wantsBases); }
    private Emission emitDetailedType(boolean wantsBases) { return emitDetailedType(emitType(), emitDetails(wantsBases)); }
    private Emission emitExtendedType(boolean wantsBases) {
        return emitExtendedType(emitType(), wantsBases ? basicType().emitCode(wantsBases) : NoValue); }

    @Override public Emission emitItem() { return emitCode(true); }
    public Emission emitArrayed() { return this.argued ? emitItem(Etc) : emitItem(Arrayed); }

    public Emission emitCode(boolean wantsBases) {
        if (isEmpty()) return null;
        return extendsBase() ?
                emitExtendedType(wantsBases) :
                emitDetailedTypes(wantsBases) ; }

    public Emission emitDetailedTypes(boolean wantsBases) {
        return isArrayed() ?
                emitSequence(emitNakedType(), emitArrayed()) :
                emitDetailedType(wantsBases) ; }


    public static final DetailedType RootType = DetailedType.named(Object);
    public static final DetailedType MetaType = DetailedType.named(Metatype);
    public static final DetailedType MetaClass = DetailedType.named(Metaclass);
    public static final DetailedType BehaviorClass = DetailedType.named(Behavior);

} // DetailedType