Mirror.java

package Hoot.Runtime.Behaviors;

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

import Hoot.Runtime.Names.Name;
import Hoot.Runtime.Faces.Named;
import Hoot.Runtime.Emissions.Item;
import Hoot.Runtime.Names.Selector;
import Hoot.Runtime.Names.Signature;
import static Hoot.Runtime.Functions.Utils.*;
import static Hoot.Runtime.Functions.Exceptional.*;
import static Hoot.Runtime.Names.Name.MetaMember;

/**
 * Provides reflective utilities for dealing with primitive Java classes.
 *
 * @author nik <nikboyd@sonic.net>
 * @see "Copyright 1999,2021 Nikolas S Boyd."
 * @see "Permission is granted to copy this work provided this copyright statement is retained in all copies."
 */
public class Mirror extends Item implements Typified {

    public Mirror() { this(null); }
    public Mirror(Class<?> reflectedClass) { super(null); aClass = reflectedClass; }

    @Override public int hashCode() { return hasNoClass() ? super.hashCode() : aClass.hashCode(); }
    protected boolean equals(Mirror m) { return (hasNoClass() && m.hasNoClass()) || (aClass.equals(m.aClass)); }
    @Override public boolean equals(Object mirror) {
        return hasAny(mirror) && getClass() == mirror.getClass() && falseOr(m -> this.equals(m), (Mirror) mirror); }

    /**
     * @return a Mirror for reflecting upon a type
     * @param aType a type upon which to reflect
     */
    public static Mirror forType(Typified aType) {
        return (aType == null) ? emptyMirror() : forClass(aType.primitiveClass()); }

    /**
     * @return a Mirror for reflecting upon aClass
     * @param aClass a class upon which to reflect
     */
    public static Mirror forClass(Class<?> aClass) {
        if (aClass == null) return emptyMirror();
        if (!Registry.containsKey(aClass)) {
            Registry.put(aClass, new Mirror(aClass));
        }
        return Registry.get(aClass);
    }


    // an empty mirror (for null)
    public static final Mirror EmptyMirror = new Mirror();
    public static Mirror emptyMirror() { return EmptyMirror; }
    @Override public Typified superclass() { return superior(); }
    public Mirror superior() { return hasNoClass() ? emptyMirror() : superMirror(); }
    private Mirror superMirror() { return Mirror.forClass(reflectedSuperclass()); }

    public static Class[] unwrapTypes(List<Typified> types) {
        return unwrap(map(types, type -> type.primitiveClass()), EmptySample); }

    public static List<Typified> argumentTypes(Method m) {
        return map(wrap(m.getParameterTypes()), type -> Mirror.forClass(type)); }

    protected static Class[] EmptySample = { };
    public static Class[] argumentTypes(List<String> classNames) {
        return unwrap(map(classNames, n -> Selector.from(n).toClass()), EmptySample); }

    protected Class<?> aClass; // class upon which to reflect
    @Override public void release() { aClass = null; }
    @Override public boolean hasNoClass() { return hasNone(aClass); }
    @Override public boolean isEmpty() { return hasNone(aClass); }
    @Override public boolean isReflective() { return true; }
    @Override public Class<?> primitiveClass() { return aClass; }
    public boolean isTypical() { return Typified.class.isInstance(aClass); }
    public Typified reflectedType() { return Typified.class.cast(aClass); }

    public Class<?> reflectedClass() { return primitiveClass(); }
    private Class<?> reflectedSuperclass() { return reflectedClass().getSuperclass(); }
    @Override public Class<?> outerClass() { return hasNoClass() ? null : aClass.getEnclosingClass(); }

    @Override public String name() { return hasNoClass() ? Empty : primitiveClass().getName(); }
    @Override public String shortName() { return hasNoClass() ? Empty : Name.typeName(name()); }
    @Override public String description() { return hasNoClass() ? Empty : primitiveClass().getCanonicalName(); }

    public boolean hasMetaclass() { return (fieldType(MetaMember) != null); }
    public Field metaclassField() { return fieldNamed(MetaMember); }
    public Class<?> metaclassType() { return fieldType(MetaMember); }
    @Override public Typified $class() { return hasMetaclass() ? Mirror.forClass(metaclassType()) : null; }
    public Typified metaclassInstance() { return nullOrTryLoudly(() -> (Typified)metaclassField().get(null)); }

    public Field fieldNamed(String fieldName) { return findField(fieldName); }
    public Class<?> fieldType(String fieldName) {
        return nullOr(f -> f.getType(), fieldNamed(fieldName)); }

    @Override public Signed getSigned(Signed s) { return nullOr(m -> Signature.from(m), methodSigned(s)); }
    public Method methodSigned(Signed s) {
        return findFirst(select(wrap(aClass.getDeclaredMethods()),
            m -> m.getName().equals(s.methodName())),
            m -> Signature.from(m).matchesArgumentTypes(s)); }

    public Method methodSigned(String signature) {
        List<String> parts = Signature.parse(signature);
        return methodNamed(parts.get(1), Signature.argumentTypes(parts)); }

    public Method methodNamed(String methodName, Class[] arguments) { return findMethod(methodName, arguments); }
    public Class<?> methodType(String methodName, Class[] arguments) {
        return nullOr(m -> m.getReturnType(), methodNamed(methodName, arguments)); }

    @Override public Class<?> resolveType(Named reference) { return fieldType(reference.name().toString()); }
    @Override public String resolveTypeName(Named reference) {
        return emptyOr(type -> type.getCanonicalName(), resolveType(reference)); }

    @Override public List<Typified> simpleHeritage() {
        ArrayList<Typified> results = emptyList(Typified.class);
        if (!hasNoClass()) {
            Typified superior = superior();
            if (!superior.hasNoClass()) {
                results.add(superior);
                results.addAll(superior.simpleHeritage());
            }
        }
        return results;
    }

    @Override public List<Typified> typeHeritage() {
        Set<Typified> results = emptySet(Typified.class);
        wrap(primitiveClass().getInterfaces()).forEach(type -> {
            Typified mirror = Mirror.forClass(type);
            results.add(mirror);
            results.addAll(mirror.typeHeritage());
        });
        if (!superior().hasNoClass()) {
            simpleHeritage().forEach(superType -> {
                wrap(superType.primitiveClass().getInterfaces()).forEach(impType -> {
                    Typified aType = Mirror.forClass(impType);
                    results.add(aType);
                    results.addAll(aType.typeHeritage());
                });
            });
        }
        return copyList(results);
    }

    @Override public boolean resolves(Named reference) { return fieldNamed(reference.shortName()) != null; }
    @Override public boolean overridenBy(Signed s) {
        Typified mFace = s.faceType();
        if (mFace.inheritsFrom(this)) {
            Method result = methodSigned(s);
            if (hasAny(result)) return true;
//            if (result == null) return false;
//            return s.overrides(Signature.from(result));
        }
        return false;
    }

    @Override public Signed getSigned(String s) { return Signature.from(methodSigned(s)); }
    @Override public String matchSignatures(Signed s) {
        String fullSig = s.fullSignature();
        if (getSigned(fullSig) != null) return fullSig;

        String erasedSig = s.erasedSignature();
        if (getSigned(erasedSig) != null) return erasedSig;

        String shortSig = s.shortSignature();
        List<String> parts = Signature.parse(shortSig);
        if (s.argumentCount() > 0) {
            for (Method m : aClass.getMethods()) {
                Signature sig = Signature.from(m);
                if (sig.shortSignature().equals(parts.get(1))) {
                    return sig.shortSignature();
                }
            }
        }

        return Empty;
    }

    // registered class mirrors
    protected static Map<Class, Mirror> Registry = new HashMap<>();
    public static void releaseAll() {
        Registry.entrySet().forEach(entry -> entry.getValue().release());
        Registry.clear();
    }


    private Field findField(String fieldName) {
        try {
            if (hasNoClass()) return null;
            return aClass.getDeclaredField(fieldName);
        } catch (NoSuchFieldException ex) {
            return superior().fieldNamed(fieldName);
        }
    }

    @SuppressWarnings("UseSpecificCatch")
    public Method findMethod(String methodName, Class[] arguments) {
        try {
            if (hasNoClass()) return null;
            return aClass.getDeclaredMethod(methodName, arguments);
        } catch (Throwable ex) {
            return superior().methodNamed(methodName, arguments);
        }
    }

    public Method findMethod(String methodName, List<String> typeNames) {
        return null; // ??
    }

} // Mirror