Closed mgood7123 closed 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>>>[]}}
}
}
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)
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>>}}
}
}
}
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).
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();
}
}
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>>>>
}
}
}
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
}
}
i think we might be able to detect this by comparing the context.genericType
with the context.currentClass()
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
}
}
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();
}
}
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
im gonna leave this open so others can learn :)
im having trouble figuring out how to create a generics context from a nested generic
and i want to obtain a
GenericsContext
withinstead of
how can i do this?