xvik / generics-resolver

Java generics runtime resolver
https://xvik.github.io/generics-resolver
MIT License
45 stars 9 forks source link

how to obtain a GenericsContext of nested generics? (SOLVED) #27

Closed mgood7123 closed 1 year ago

mgood7123 commented 1 year ago

im having trouble figuring out how to create a generics context from a nested generic

        GenericsContext {
          current class: public abstract interface java.util.List<E>
          generics map: {E=HashMap<Serializable[][], Pair<Serializable[], C__<StringBuilder>>>[]}

and i want to obtain a GenericsContext with

        GenericsContext {
          current class: public class java.util.HashMap<K,V>
          generics map: {K=Serializable[][], V=Pair<Serializable[], C__<StringBuilder>>}

instead of

        GenericsContext {
          current class: public class java.util.HashMap<K,V>
          generics map: {K=class java.lang.Object, V=class java.lang.Object}

how can i do this?

mgood7123 commented 1 year ago

i tried this

            java.lang.reflect.TypeVariable<? extends Class<?>>[] typeParameters = context.currentClass().getTypeParameters();
            StringBuilder acc = new StringBuilder();
            for (int i = 0; i < typeParameters.length; i++) {
                String s = new TypeContext(
                        GenericsResolver.resolve(
                                GenericsUtils.resolveClass(
                                        new ArrayResolver(context.generic(i)).clazz,
                                        GenericsUtils.createGenericsMap(new ArrayResolver(context.generic(i)).clazz, context.resolveGenericsOf(new ArrayResolver(context.generic(i)).clazz))
                                )
                        )
                ).toDetailedString(indent+1);
                Class<?> g = context.generic(i);
                Class<?> c = new ArrayResolver(g).clazz;
                List<Class<?>> classes = context.resolveGenericsOf(c);
                LinkedHashMap<String, Type> map = GenericsUtils.createGenericsMap(c, classes);
                acc.append("\n");
                acc.append("GENERICS MAP OF " +  i + " g: " + g).append("\n");
                acc.append("GENERICS MAP OF " +  i + " c: " + c).append("\n");
                acc.append("GENERICS MAP OF " +  i + " classes: " + classes).append("\n");
                acc.append("GENERICS MAP OF " +  i + " map: " + map).append("\n");
                acc.append("GENERICS MAP OF " +  i + " r c: " + context.resolveGenericsOf(c)).append("\n");
                acc.append("GENERICS MAP OF " +  i + " r c: " + context.resolveType(c)).append("\n");
                acc.append("GENERICS MAP OF " +  i + " r c: " + context.resolveTypeGenerics(c)).append("\n");
                acc.append("GENERICS MAP OF " +  i + " r g: " + context.resolveGenericsOf(g)).append("\n");
                acc.append("GENERICS MAP OF " +  i + " r g: " + context.resolveType(g)).append("\n");
                acc.append("GENERICS MAP OF " +  i + " r g: " + context.resolveTypeGenerics(g)).append("\n");
                extracted(acc, i, g, c, context.genericsMap());
                acc.append(s);
            }
            printStream.println(indent(indent) + "genericParameters: " + typeParameters.length + acc);
            indent--;
            printStream.print(indent(indent) + "}");
            printStream.flush();
            return byteArrayOutputStream.toString();
        }

        private void extracted(StringBuilder acc, int i, Class<?> g, Class<?> c, Map<String, Type> map) {
            acc.append("GENERICS MAP OF " + i + " raw c: " + GenericsResolutionUtils.resolveRawGenerics(c)).append("\n");
            acc.append("GENERICS MAP OF " + i + " raw c: " + GenericsResolutionUtils.resolveGenerics(c, map)).append("\n");
            acc.append("GENERICS MAP OF " + i + " raw g: " + GenericsResolutionUtils.resolveRawGenerics(g)).append("\n");
            acc.append("GENERICS MAP OF " + i + " raw g: " + GenericsResolutionUtils.resolveGenerics(g, map)).append("\n");
            acc.append("GENERICS MAP OF " + i + " raw c: " + GenericsUtils.resolveClass(c, map)).append("\n");
            acc.append("GENERICS MAP OF " + i + " raw c: " + GenericsUtils.extractTypeGenerics(c, map)).append("\n");
            acc.append("GENERICS MAP OF " + i + " raw c: " + Arrays.toString(GenericsUtils.getGenerics(c, map))).append("\n");
            acc.append("GENERICS MAP OF " + i + " raw g: " + GenericsUtils.resolveClass(g, map)).append("\n");
            acc.append("GENERICS MAP OF " + i + " raw g: " + GenericsUtils.extractTypeGenerics(g, map)).append("\n");
            acc.append("GENERICS MAP OF " + i + " raw g: " + Arrays.toString(GenericsUtils.getGenerics(g, map))).append("\n");
        }

but i get this

GENERICS MAP OF 0 g: class [Ljava.util.HashMap;
GENERICS MAP OF 0 c: class java.util.HashMap
GENERICS MAP OF 0 classes: [class java.lang.Object, class java.lang.Object]
GENERICS MAP OF 0 map: {K=class java.lang.Object, V=class java.lang.Object}
GENERICS MAP OF 0 r c: [class java.lang.Object, class java.lang.Object]
GENERICS MAP OF 0 r c: class java.util.HashMap
GENERICS MAP OF 0 r c: [class java.lang.Object, class java.lang.Object]
GENERICS MAP OF 0 r g: []
GENERICS MAP OF 0 r g: class [Ljava.util.HashMap;
GENERICS MAP OF 0 r g: []
GENERICS MAP OF 0 raw c: {K=class java.lang.Object, V=class java.lang.Object}
GENERICS MAP OF 0 raw c: {K=class java.lang.Object, V=class java.lang.Object}
GENERICS MAP OF 0 raw g: {}
GENERICS MAP OF 0 raw g: {}
GENERICS MAP OF 0 raw c: class java.util.HashMap
GENERICS MAP OF 0 raw c: {K=class java.lang.Object, V=class java.lang.Object}
GENERICS MAP OF 0 raw c: [class java.lang.Object, class java.lang.Object]
GENERICS MAP OF 0 raw g: class [Ljava.util.HashMap;
GENERICS MAP OF 0 raw g: {}
GENERICS MAP OF 0 raw g: []

for this

    TypeContext {
      type: interface java.util.List
      context: 
        GenericsContext {
          current class: public abstract interface java.util.List<E>
          is inlying: true
          owner class: null
          owner generics map: {}
          visible generics map: {E=HashMap<Serializable[][], Pair<Serializable[], C__<StringBuilder>>>[]}
          generics: [class [Ljava.util.HashMap;]
          generics map: {E=HashMap<Serializable[][], Pair<Serializable[], C__<StringBuilder>>>[]}
          generics scope: CLASS
          generics source: interface java.util.List
          generics of: [class java.lang.Object]
          type generics: [class java.lang.Object]
          generics info: 
            GenericsInfo {
              root class: interface java.util.List
              composing types: [interface java.lang.Iterable, interface java.util.Collection, interface java.util.List]
              types map: {interface java.lang.Iterable={T=HashMap<Serializable[][], Pair<Serializable[], C__<StringBuilder>>>[]}, interface java.util.Collection={E=HashMap<Serializable[][], Pair<Serializable[], C__<StringBuilder>>>[]}, interface java.util.List={E=HashMap<Serializable[][], Pair<Serializable[], C__<StringBuilder>>>[]}}
            }
        }
xvik commented 1 year ago

GenericsContext { current class: public abstract interface java.util.List generics map: {E=HashMap<Serializable[][], Pair<Serializable[], C__>>[]}

"Sub" context for generic variable should be something like this:

Type genericE = context.genericType("E")

// build sub context from type variable (root context used to resolve possible variables in type)
GenericsContext subContext = context.inlyingType(genericE)

Basically, inlying is a type resolution knowing "context" variables (from containing class). It is also important to select correct context class before (due to possibly same variables in different classes in hierarchy)

mgood7123 commented 1 year ago

so something like this?

try {
    GenericsContext resolve = GenericsResolver.resolve(C__.class);
    GenericsContext foo = resolve.fieldType(TypeContext.getFieldRecursive(C__.class, "foo"));
    TypeContext.printDetailed(foo);
    for (int i = 0; i < foo.currentClass().getTypeParameters().length; i++) {
        Type type = foo.genericType(i);
        while(type instanceof GenericArrayType) {
            type = ((GenericArrayType) type).getGenericComponentType();
        }
        GenericsContext genericsContext = foo.inlyingType(type);
        TypeContext.printDetailed(genericsContext);
    }
} catch (NoSuchFieldException e) {
    throw new RuntimeException(e);
}
TypeContext {
  type: interface java.util.List
  context: 
    GenericsContext {
      current class: public abstract interface java.util.List<E>
      is inlying: true
      owner class: null
      owner generics map: {}
      visible generics map: {E=HashMap<Serializable[][], Pair<Serializable[], C__<StringBuilder>>>[]}
      generics: [class [Ljava.util.HashMap;]
      generics map: {E=HashMap<Serializable[][], Pair<Serializable[], C__<StringBuilder>>>[]}
      generics scope: CLASS
      generics source: interface java.util.List
      generics of: [class java.lang.Object]
      type generics: [class java.lang.Object]
      generics info: 
        GenericsInfo {
          root class: interface java.util.List
          composing types: [interface java.lang.Iterable, interface java.util.Collection, interface java.util.List]
          types map: {interface java.lang.Iterable={T=HashMap<Serializable[][], Pair<Serializable[], C__<StringBuilder>>>[]}, interface java.util.Collection={E=HashMap<Serializable[][], Pair<Serializable[], C__<StringBuilder>>>[]}, interface java.util.List={E=HashMap<Serializable[][], Pair<Serializable[], C__<StringBuilder>>>[]}}
        }
    }
}

TypeContext {
  type: class java.util.HashMap
  context: 
    GenericsContext {
      current class: public class java.util.HashMap<K,V>
      is inlying: true
      owner class: null
      owner generics map: {}
      visible generics map: {K=class [[Ljava.io.Serializable;, V=Pair<Serializable[], C__<StringBuilder>>}
      generics: [class [[Ljava.io.Serializable;, class smallville7123.reflectui.utils.Pair]
      generics map: {K=class [[Ljava.io.Serializable;, V=Pair<Serializable[], C__<StringBuilder>>}
      generics scope: CLASS
      generics source: class java.util.HashMap
      generics of: [class java.lang.Object, class java.lang.Object]
      type generics: [class java.lang.Object, class java.lang.Object]
      generics info: 
        GenericsInfo {
          root class: class java.util.HashMap
          composing types: [class java.util.AbstractMap, class java.util.HashMap, interface java.io.Serializable, interface java.lang.Cloneable, interface java.util.Map]
          types map: {class java.util.AbstractMap={K=class [[Ljava.io.Serializable;, V=Pair<Serializable[], C__<StringBuilder>>}, class java.util.HashMap={K=class [[Ljava.io.Serializable;, V=Pair<Serializable[], C__<StringBuilder>>}, interface java.io.Serializable={}, interface java.lang.Cloneable={}, interface java.util.Map={K=class [[Ljava.io.Serializable;, V=Pair<Serializable[], C__<StringBuilder>>}}
        }
    }
}
xvik commented 1 year ago

for (int i = 0; i < foo.currentClass().getTypeParameters().length; i++) {

could be just

for(Type type: foo.genericTypes())

while(type instanceof GenericArrayType) {

It also could be a pure array class. Here is a few related methods from master:

   /**
     * @param type type to check
     * @return true if type is array or generic array ({@link GenericArrayType}), false otherwise.
     */
   public static boolean isArray(final Type type) {
        return (type instanceof Class && ((Class) type).isArray()) || type instanceof GenericArrayType;
    }

    /**
     * For example, {@code getArrayComponentType(int[]) == int} and
     * {@code getArrayComponentType(List<String>[]) == List<String>}.
     *
     * @param type array type (class or {@link GenericArrayType}
     * @return array component type
     * @throws IllegalArgumentException if provided type is not array
     * @see #isArray(Type)
     */
    public static Type getArrayComponentType(final Type type) {
        if (!isArray(type)) {
            throw new IllegalArgumentException("Provided type is not an array: "
                    + TypeToStringUtils.toStringType(type));
        }
        if (type instanceof GenericArrayType) {
            return ((GenericArrayType) type).getGenericComponentType();
        } else {
            return ((Class) type).getComponentType();
        }
    }

and with it:

        Type type = foo.genericType(i);
        while(isArray(type)) {
            type = getArrayComponentType(type);
        }

And, just in case, when you want to get rid of context (no need deeper investigations) you could simply do:

Type pureType = context.resolveType(type)

This would replace possible variables in type (repackage type without variables).

mgood7123 commented 1 year ago

alright, now im having a bit of trouble resolving a self-bound generic

class SS1 <T extends SS1<T>> { T r; }
Exception in thread "main" java.lang.RuntimeException: OVERFLOW
        at smallville7123.Main.Tests$TypeContext.toDetailedString(Tests.java:817)
        at smallville7123.Main.Tests$TypeContext.toDetailedString(Tests.java:808)
        at smallville7123.Main.Tests$TypeContext.toDetailedString(Tests.java:830)
        at smallville7123.Main.Tests$TypeContext.toDetailedString(Tests.java:808)
new TypeContext(SS1.class).findField("r").printDetailed();
        public TypeContext(Type type) {
            Type t = resolve(type);
            if (t instanceof Class<?>) {
                context = GenericsResolver.resolve((Class<?>)t);
            } else {
                throw new RuntimeException("resulting type is not an instance of class: " + t);
            }
        }

        TypeContext(GenericsContext context, Type type) {
            this.context = context.inlyingType(resolve(type));
        }

        TypeContext(GenericsContext context) {
            this.context = context;
        }

        private Type resolve(Type type) {
            while(type instanceof GenericArrayType) {
                type = ((GenericArrayType) type).getGenericComponentType();
            }
            if (type instanceof Class<?>) {
                Class<?> aClass = (Class<?>) type;

                while (aClass.isArray()) {
                    rank++;
                    aClass = aClass.getComponentType();
                }
                return aClass;
            } else {
                return type;
            }
        }

        public TypeContextField findField(String fieldName) {
            try {
                return new TypeContextField(this, getFieldRecursive(fieldName));
            } catch (NoSuchFieldException e) {
                throw new RuntimeException(e);
            }
        }

// ...

        public static String toDetailedString(GenericsContext context, int indent) {
            if (indent > 20) {
                throw new RuntimeException("OVERFLOW");
            }
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            PrintStream printStream = new PrintStream(byteArrayOutputStream);
            printStream.println();
            printStream.println(indent(indent) + "TypeContext {");
            indent++;
            printStream.println(indent(indent) + "type: " + context.currentClass());

            StringBuilder acc = new StringBuilder();
            java.lang.reflect.TypeVariable<?>[] typeParameters = context.currentClass().getTypeParameters();
            for (int i = 0; i < context.currentClass().getTypeParameters().length; i++) {
                acc.append(new TypeContext(context, context.genericType(i)).toDetailedString(indent+1));
            }
            printStream.println(indent(indent) + "genericParameters: " + typeParameters.length + acc);
            indent--;
            printStream.print(indent(indent) + "}");
            printStream.flush();
            return byteArrayOutputStream.toString();
        }
    static class TypeContextField {
        TypeContext parent;
        Field field;

        TypeContextField(TypeContext parent, Field field) {
            this.parent = parent;
            this.field = field;
        }

        TypeContext getReturnType() {
            return new TypeContext(parent.context.fieldType(field));
        }

        public void printDetailed() {
            System.out.println(toDetailedString());
        }

        public String toDetailedString() {
            return toDetailedString(0);
        }

        private String toDetailedString(int indent) {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            PrintStream printStream = new PrintStream(byteArrayOutputStream);
            printStream.println();
            printStream.println(indent(indent) + "TypeContext {");
            indent++;
            printStream.println(indent(indent) + "field: " + field);
            printStream.println(indent(indent) + "field type: " + getReturnType().toDetailedString(indent + 1));
            indent--;
            printStream.print(indent(indent) + "}");
            printStream.flush();
            return byteArrayOutputStream.toString();
        }
    }
mgood7123 commented 1 year ago

if i just return a string instead i get

TypeContext {
  field: smallville7123.Main.SS1 smallville7123.Main.SS1.r
  field type: 
    TypeContext {
      type: class smallville7123.Main.SS1
      genericParameters: 1
        TypeContext {
          type: class smallville7123.Main.SS1
          genericParameters: 1
            TypeContext {
              type: class smallville7123.Main.SS1
// ...
                                      genericParameters: 1
                                        TypeContext {
                                          type: class smallville7123.Main.SS1
                                          genericParameters: 1<<<<OVERFLOW>>>>
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

and if we print the GenericsContext as well

TypeContext {
  field: smallville7123.Main.SS1 smallville7123.Main.SS1.r
  field type: 
    TypeContext {
      type: class smallville7123.Main.SS1
      context: 
        GenericsContext {
          current class: class smallville7123.Main.SS1<T extends smallville7123.Main.SS1<T>>
          is inlying: true
          owner class: null
          owner generics map: {}
          visible generics map: {T=class smallville7123.Main.SS1}
          generics: [class smallville7123.Main.SS1]
          generics map: {T=class smallville7123.Main.SS1}
          generics scope: CLASS
          generics source: class smallville7123.Main.SS1
          generics of: [class smallville7123.Main.SS1]
          type generics: [class smallville7123.Main.SS1]
          generics info: 
            GenericsInfo {
              root class: class smallville7123.Main.SS1
              composing types: [class smallville7123.Main.SS1]
              types map: {class smallville7123.Main.SS1={T=class smallville7123.Main.SS1}}
            }
        }
      genericParameters: 1
        TypeContext {
          type: class smallville7123.Main.SS1
          context: 
            GenericsContext {
              current class: class smallville7123.Main.SS1<T extends smallville7123.Main.SS1<T>>
              is inlying: true
              owner class: null
              owner generics map: {}
              visible generics map: {T=class smallville7123.Main.SS1}
              generics: [class smallville7123.Main.SS1]
              generics map: {T=class smallville7123.Main.SS1}
              generics scope: CLASS
              generics source: class smallville7123.Main.SS1
              generics of: [class smallville7123.Main.SS1]
              type generics: [class smallville7123.Main.SS1]
              generics info: 
                GenericsInfo {
                  root class: class smallville7123.Main.SS1
                  composing types: [class smallville7123.Main.SS1]
                  types map: {class smallville7123.Main.SS1={T=class smallville7123.Main.SS1}}
                }
            }
          genericParameters: 1<<<<OVERFLOW>>>>
        }
    }
}
mgood7123 commented 1 year ago

however

class SS2 extends SS1<SS2> {}

correctly resolves without overflow

TypeContext {
  field: smallville7123.Main.SS1 smallville7123.Main.SS1.r
  field type: 
    TypeContext {
      type: class smallville7123.Main.SS2
      context: 
        GenericsContext {
          current class: class smallville7123.Main.SS2
          is inlying: true
          owner class: null
          owner generics map: {}
          visible generics map: {}
          generics: []
          generics map: {}
          generics scope: CLASS
          generics source: class smallville7123.Main.SS2
          generics of: []
          type generics: []
          generics info: 
            GenericsInfo {
              root class: class smallville7123.Main.SS2
              composing types: [class smallville7123.Main.SS2, class smallville7123.Main.SS1]
              types map: {class smallville7123.Main.SS2={}, class smallville7123.Main.SS1={T=class smallville7123.Main.SS2}}
            }
        }
      genericParameters: 0
    }
}
mgood7123 commented 1 year ago

i think we might be able to detect this by comparing the context.genericType with the context.currentClass()

mgood7123 commented 1 year ago

ok, so now we have

        new TypeContext(S1.class).findField("r").printDetailed();
        new TypeContext(S3.class).findField("r").printDetailed();
        new TypeContext(S6.class).findField("r").printDetailed();
        new TypeContext(SS1.class).findField("r").printDetailed();
        new TypeContext(SS2.class).findField("r").printDetailed();
        new TypeContext(S2.class).findMethods("compareTo")[0].printDetailed();
        new TypeContext(GenericFields.class).findField("a").printDetailed();
        new TypeContext(GenericFields.class).findMethods("aMeth")[0].printDetailed();
class GenericFields {
    public List<List<String[]>[]>[] a;
    public List<List<String[]>[]>[] aMeth(List<List<String[][]>[][]>[][] b) { return null; };
}

class SS1 <T extends SS1<T>> { T r; }
class SS2 extends SS1<SS2> {}

class S1 <T extends Comparable<T>> { T r; }
class CC <T> implements Comparable<T> {
    @Override
    public int compareTo(@NotNull T o) {
        return 0;
    }
}
class S3 extends S1<S2> {}
class S2 extends CC<S2> {}
class S22 extends CC<S4> {}
class S4 extends CC<S2> {}
class S5 extends S2 {}
class S6 extends S3 {}
press any key to continue

GENERICS RESOLVER

TypeContextField {
  field: Comparable r
  field type: 
    TypeContext {
      type: interface java.lang.Comparable
      genericParameters: 1
        TypeContext {
          type: class java.lang.Object
          genericParameters: 0
        }
    }
}
GENERICS RESOLVER

TypeContextField {
  field: S2 r
  field type: 
    TypeContext {
      type: class smallville7123.Main.S2
      genericParameters: 0
    }
}
GENERICS RESOLVER

TypeContextField {
  field: S2 r
  field type: 
    TypeContext {
      type: class smallville7123.Main.S2
      genericParameters: 0
    }
}
GENERICS RESOLVER

TypeContextField {
  field: SS1<SS1> r
  field type: 
    TypeContext {
      type: class smallville7123.Main.SS1
      genericParameters: 1
        SELF BOUND TYPE: class smallville7123.Main.SS1

    }
}
GENERICS RESOLVER

TypeContextField {
  field: SS2 r
  field type: 
    TypeContext {
      type: class smallville7123.Main.SS2
      genericParameters: 0
    }
}
GENERICS RESOLVER

TypeContextMethod {
  method: int compareTo(S2)
  method return type: 
    TypeContext {
      type: class java.lang.Integer
      genericParameters: 0
    }
  genericParameters: 1
    TypeContext {
      type: class smallville7123.Main.S2
      genericParameters: 0
    }
}
GENERICS RESOLVER

TypeContextField {
  field: List<List<String[]>[]> a
  field type: 
    TypeContext {
      type: interface java.util.List
      rank: 1
      genericParameters: 1
        TypeContext {
          type: interface java.util.List
          rank: 1
          genericParameters: 1
            TypeContext {
              type: class java.lang.String
              rank: 1
              genericParameters: 0
            }
        }
    }
}
GENERICS RESOLVER

TypeContextMethod {
  method: List<List<String[]>[]>[] aMeth(List<List<String[][]>[][]>[][])
  method return type: 
    TypeContext {
      type: interface java.util.List
      rank: 1
      genericParameters: 1
        TypeContext {
          type: interface java.util.List
          rank: 1
          genericParameters: 1
            TypeContext {
              type: class java.lang.String
              rank: 1
              genericParameters: 0
            }
        }
    }
  genericParameters: 1
    TypeContext {
      type: interface java.util.List
      rank: 2
      genericParameters: 1
        TypeContext {
          type: interface java.util.List
          rank: 2
          genericParameters: 1
            TypeContext {
              type: class java.lang.String
              rank: 2
              genericParameters: 0
            }
        }
    }
}

and

        new TypeContext(TypeVariableBoundsSelf2.class).findMethods("setSelfBoundMethod")[0].printDetailed();
        new TypeContext(TypeVariableBoundsSelf2.class).findMethods("setFoo")[0].printDetailed();
        new TypeContext(TypeVariableBoundsSelf2.class).findMethods("next")[0].printDetailed();
        new TypeContext(TypeVariableBoundsSelf2.class).findMethods("compareTo")[0].printDetailed();
abstract class TypeVariableBoundsSelf2 extends TypeVariableBoundsSelf<TypeVariableBoundsSelf2> implements Comparable<TypeVariableBoundsSelf2>, Iterator<TypeVariableBoundsSelf2[]> {

}

class TypeVariableBoundsSelf <X extends Comparable<X> & Iterator<X[]>> {
    //    public X b;
    public void setFoo(X a) {
    }
    public X getFoo(X a) {
        return null;
    }
    public <Y extends List<Y>> void setSelfBoundMethod (Y a) {
    }
    public <Y extends List<Y>> Y getSelfBoundMethod () {
        return null;
    }
}
TypeContextMethod {
  method: void setSelfBoundMethod(List)
  method return type: 
    TypeContext {
      type: class java.lang.Void
      genericParameters: 0
    }
  genericParameters: 1
    TypeContext {
      type: interface java.util.List
      genericParameters: 1
        TypeContext {
          type: class java.lang.Object
          genericParameters: 0
        }
    }
}
GENERICS RESOLVER

TypeContextMethod {
  method: void setFoo(TypeVariableBoundsSelf2)
  method return type: 
    TypeContext {
      type: class java.lang.Void
      genericParameters: 0
    }
  genericParameters: 1
    TypeContext {
      type: class smallville7123.Main.TypeVariableBoundsSelf2
      genericParameters: 0
    }
}
GENERICS RESOLVER

TypeContextMethod {
  method: TypeVariableBoundsSelf2[] next()
  method return type: 
    TypeContext {
      type: class smallville7123.Main.TypeVariableBoundsSelf2
      rank: 1
      genericParameters: 0
    }
  genericParameters: 0
}
GENERICS RESOLVER

TypeContextMethod {
  method: int compareTo(TypeVariableBoundsSelf2)
  method return type: 
    TypeContext {
      type: class java.lang.Integer
      genericParameters: 0
    }
  genericParameters: 1
    TypeContext {
      type: class smallville7123.Main.TypeVariableBoundsSelf2
      genericParameters: 0
    }
}
mgood7123 commented 1 year ago

so yay i think it can now resolve everything correctly \o/

classes:

package smallville7123.reflectui;

import static smallville7123.reflectui.TypeContext.indent;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Field;

public class TypeContextField {
    TypeContext parent;
    Field field;

    TypeContextField(TypeContext parent, Field field) {
        this.parent = parent;
        this.field = field;
    }

    TypeContext getReturnType() {
        return new TypeContext(parent.context, parent.context.resolveFieldType(field));
    }

    public void printDetailed() {
        System.out.println(toDetailedString());
    }

    public String toDetailedString() {
        return toDetailedString(0);
    }

    private String toDetailedString(int indent) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        PrintStream printStream = new PrintStream(byteArrayOutputStream);
        printStream.println();
        printStream.println(indent(indent) + "TypeContextField {");
        indent++;
        TypeContext returnType = getReturnType();
        printStream.println(indent(indent) + "field: " + returnType.context.toStringCurrentClass() + " " + field.getName());
        printStream.println(indent(indent) + "field type: " + returnType.toDetailedString(indent + 1));
        indent--;
        printStream.print(indent(indent) + "}");
        printStream.flush();
        return byteArrayOutputStream.toString();
    }
}
package smallville7123.reflectui;

import static smallville7123.reflectui.TypeContext.indent;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.ArrayList;

import ru.vyarus.java.generics.resolver.context.MethodGenericsContext;

public class TypeContextMethod {
    TypeContext parent;
    MethodGenericsContext methodContext;

    TypeContextMethod(TypeContext parent, MethodGenericsContext methodContext) {
        this.parent = parent;
        this.methodContext = methodContext;
    }

    TypeContext getReturnType() {
        return new TypeContext(parent.context, parent.context.resolveType(methodContext.resolveReturnType()));
    }

    int getParameterCount() {
        return methodContext.currentMethod().getParameterCount();
    }

    TypeContext getParameter(int position) {
        return new TypeContext(methodContext.parameterType(position));
    }

    TypeContext[] getParameters() {
        int parameterCount = methodContext.currentMethod().getParameterCount();
        ArrayList<TypeContext> params = new ArrayList<>(parameterCount);
        for (int i = 0; i < parameterCount; i++) {
            params.add(new TypeContext(parent.context, parent.context.resolveType(methodContext.resolveParameterType(i))));
        }
        return params.toArray(new TypeContext[0]);
    }

    public void printDetailed() {
        System.out.println(toDetailedString());
    }

    public String toDetailedString() {
        return toDetailedString(0);
    }

    private String toDetailedString(int indent) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        PrintStream printStream = new PrintStream(byteArrayOutputStream);
        printStream.println();
        printStream.println(indent(indent) + "TypeContextMethod {");
        indent++;
        printStream.println(indent(indent) + "method: " + methodContext.toStringMethod());
        printStream.println(indent(indent) + "method return type: " + getReturnType().toDetailedString(indent + 1));
        StringBuilder acc = new StringBuilder();
        TypeContext[] parameters = getParameters();
        for (TypeContext typeContext : parameters) {
            acc.append(typeContext.toDetailedString(indent + 1));
        }
        printStream.println(indent(indent) + "genericParameters: " + parameters.length + acc);
        indent--;
        printStream.print(indent(indent) + "}");
        printStream.flush();
        return byteArrayOutputStream.toString();
    }
}
package smallville7123.reflectui;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

import ru.vyarus.java.generics.resolver.GenericsResolver;
import ru.vyarus.java.generics.resolver.context.GenericsContext;
import ru.vyarus.java.generics.resolver.context.GenericsInfo;

public class TypeContext {
    GenericsContext context;
    int rank = 0;

    public TypeContext(Type type) {
        Type t = resolve(type);
        if (t instanceof Class<?>) {
            context = GenericsResolver.resolve((Class<?>) t);
        } else {
            throw new RuntimeException("resulting type is not an instance of class: " + t);
        }
    }

    TypeContext(GenericsContext context, Type type) {
        this.context = context.inlyingType(resolve(type));
    }

    TypeContext(GenericsContext context) {
        this.context = context;
    }

    private Type resolve(Type type) {
        while (type instanceof GenericArrayType) {
            rank++;
            type = ((GenericArrayType) type).getGenericComponentType();
        }
        if (type instanceof Class<?>) {
            Class<?> aClass = (Class<?>) type;
            while (aClass.isArray()) {
                rank++;
                aClass = aClass.getComponentType();
            }
            return aClass;
        } else {
            return type;
        }
    }

    public TypeContextField findField(String fieldName) {
        try {
            return new TypeContextField(this, getFieldRecursive(fieldName));
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        }
    }

// ...

    public TypeContextMethod[] findMethods(String methodName) {
        try {
            List<TypeContextMethod> list = new ArrayList<>();
            for (Method method : getMethodsRecursive(methodName)) {
                TypeContextMethod typeContextField = new TypeContextMethod(this, context.method(method));
                list.add(typeContextField);
            }
            return list.toArray(new TypeContextMethod[0]);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

// ...

    public static String toDetailedString(TypeContext typeContext, int indent) {
        if (indent > 30) {
            return "<<<<OVERFLOW>>>>";
        }
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        PrintStream printStream = new PrintStream(byteArrayOutputStream);
        printStream.println();
        printStream.println(indent(indent) + "TypeContext {");
        indent++;
        printStream.println(indent(indent) + "type: " + typeContext.context.currentClass());
        if (typeContext.rank > 0) {
            printStream.println(indent(indent) + "rank: " + typeContext.rank);
        }

        StringBuilder acc = new StringBuilder();
        List<Type> types = typeContext.context.genericTypes();
        for (Type type : types) {
            if (type == typeContext.context.currentClass()) {
                acc.append("\n" + indent(indent + 1) + "SELF BOUND TYPE: " + typeContext.context.currentClass() + "\n");
            } else {
                acc.append(new TypeContext(typeContext.context, type).toDetailedString(indent + 1));
            }
        }
        printStream.println(indent(indent) + "genericParameters: " + types.size() + acc);
        indent--;
        printStream.print(indent(indent) + "}");
        printStream.flush();
        return byteArrayOutputStream.toString();
    }
}
mgood7123 commented 1 year ago

not sure if there is anything else i need to test, but i think if those work then the rest should also work as expected

mgood7123 commented 1 year ago

im gonna leave this open so others can learn :)