Signature.java

package Hoot.Runtime.Names;

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

import Hoot.Runtime.Behaviors.*;
import static Hoot.Runtime.Functions.Utils.*;
import static Hoot.Runtime.Names.Keyword.Object;
import static Hoot.Runtime.Names.Keyword.Colon;

/**
 * A method signature.
 *
 * @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 Signature implements Signed {

    public Signature(Method m) { this.m = m; }
    public static Signature from(Method method) { return nullOr(m -> new Signature(m), method); }

    protected Method m;
    public Method method() { return m; }
    protected String selector() { return method().getName(); }
    protected Class<?> methodClass() { return method().getDeclaringClass(); }
    protected Mirror typeMirror() { return Mirror.forClass(methodClass()); }
    protected Mirror resultMirror() { return Mirror.forClass(method().getReturnType()); }
    protected List<Typified> argumentMirrors() {
        return map(wrap(method().getParameterTypes()), type -> Mirror.forClass(type)); }

    public boolean overrides(Signature s) { return matchesSign(s) && inherits(s); }
    @Override public boolean overrides(Signed s) {
        return matchesKind(s) ? overrides((Signature)s) : Signed.super.overrides(s); }

    @Override public boolean isStatic() { return Primitive.isStatic(method()); }
    public boolean matchesKind(Signed s) { return getClass().isInstance(s); }
    public boolean inherits(Signature s) { return !matchesClass(s) && s.methodClass().isAssignableFrom(methodClass()); }
    public boolean matchesClass(Signature s) { return methodClass().equals(s.methodClass()); }
    public boolean matchesSign(Signature s) { return selector().equals(s.selector()) && matchArguments(s); }
    public boolean matchArguments(Signature s) {
        if (argumentCount() != s.argumentCount()) return false;
        if (0 == argumentCount()) return true;

        List<TypeName> argTypeNames = argumentTypeNames();
        List<TypeName> sargTypeNames = s.argumentTypeNames();
        for (int index = 0; index < argTypeNames.size(); index++) {
            if (!argTypeNames.get(index).matches(sargTypeNames.get(index))) return false;
        }
        return true;
    }

    @Override public String methodName() { return Keyword.with(selector()).methodName(); }
    @Override public Typified faceType() { return typeMirror(); }
    @Override public String faceName() { return faceType().fullName(); }

    @Override public int argumentCount() { return m.getParameterCount(); }
    @Override public List<Typified> argumentTypes() { return argumentMirrors(); }
    @Override public List<TypeName> argumentTypeNames() {
        final int[] index = { 0 };
        Class<?>[] argTypes = method().getParameterTypes();
        Type[] genTypes = method().getGenericParameterTypes();
        return map(wrap(genTypes), type -> {
            String argTypeName = argTypes[index[0]].getSimpleName();
            String genTypeName = genTypes[index[0]].getTypeName();
            index[0]++;

            boolean isGeneric = !genTypeName.endsWith(argTypeName);
            whisper(argTypeName + " ?= " + genTypeName);
            TypeName result = TypeName.fromType(type);
            return isGeneric ? result.noteGeneric() : result; }); }

    @Override public String fullSignature() { return shortSignature() + formatTerm(argumentSignatures()); }
    @Override public String erasedSignature() { return shortSignature() + formatTerm(argumentErasures()); }
    @Override public String shortSignature() { return resultMirror().fullName() + Colon + methodName(); }
    @Override public String matchSignature() { return methodName() + formatTerm(argumentSignatures()); }
    @Override public String matchErasure() { return methodName() + formatTerm(argumentErasures()); }

    private List<String> argumentErasures() { return map(argumentTypes(), type -> Object); }
    private List<String> argumentSignatures() { return map(argumentTypeNames(), type -> type.shortName()); }

    public static String formatTerm(List<String> types) { return String.format(Term, formatList(types)); }
    public static String formatList(List<String> types) { return joinWith(Comma, types); }

    static final String Comma = ",";
    static final String Term = "(%s)";
    static final String TermEnds = "[\\(\\)]";
    public static List<String> parse(String sig) {
        String[] parts = { Empty, sig };
        if (sig.contains(Colon)) {
            parts = sig.split(Colon);
        }

        String[] names = parts[1].split(TermEnds);
        ArrayList<String> results = emptyList(String.class);
        results.add(parts[0]);
        results.add(names[0]);
        if (names.length == 1) {
            return results;
        }

        String[] args = names[1].split(Comma);
        results.addAll(wrap(args));
        return results;
    }

    public static Class[] argumentTypes(List<String> sigNames) {
        Class[] empty = { };
        if (sigNames.size() < 3) return empty;
        ArrayList<Class> results = emptyList(Class.class);
        for (int index = 2; index < sigNames.size(); index++) {
            Class argType = Selector.from(sigNames.get(index).trim()).toClass();
            if (hasAny(argType)) results.add(argType);
        }
        return unwrap(results, empty);
    }

} // Signature