Code Monkey home page Code Monkey logo

geantyref's People

Contributors

coekie avatar gselzer avatar hgschmie avatar kaqqao avatar mmerruko avatar solonovamax avatar stevenschlansker avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

geantyref's Issues

TypeVisitor has protected method that refers to package-private type

TypeVisitor has a protected visitCaptureType as if you are supposed to be able to override it.
However, the parameter is a AnnotatedCaptureType which is package-private, so to override it you must be in the geantyref package.

It seems that either the type should be visible, or the method itself should be package private not protected.

GenericTypeReflector.annotate adds invalid annotations

In its current state, GenericTypeReflector.annotate adds class annotations as type annotations. I'm not entirely sure where this would be useful, but it is a serious problem for almost anything that wants to create annotated classes. See, I'm making a library and I need to create AnnotatedTypes given a Type and a set of given annotations, however I need those exact annotations, and don't want any of the random @SuppressWarnings etc. annotations from the class being added to the type on my behalf.

No module name described in MANIFEST

For those using JPMS, it would be nice if geantyref contained an Automatic Module Name (see here for reasons why). This would provide a stable module name that other projects can reference, and is a very small change on the part of geantyref (see here).

Provide a more general type resolution facility

Provide a way to fully or partially resolve a type in the context of another.
E.g. given:

class A<T> {
    Set<T> get();
}

class B extends A<String> {}

there should be a way to resolve Set<T> in the context of B (without having a reference to the get method itself).

This aims to solve the issue outlined in #5

reduceBounded StackOverflowError with self-recursing capture

Hi @kaqqao happy new year,

We received a bug report in Jdbi: jdbi/jdbi#2582
It looks to actually be a bug in geantyref.

Here's the code to reproduce:

public interface UnitX<Q extends QuantityX<Q>> {
}
public interface QuantityX<Q extends QuantityX<Q>> {
}
class TestClass {
    public UnitX<?> returnType() {
        return null;
    }
}

GenericTypeReflector.reduceBounded(GenericTypeReflector.annotate(
        GenericTypeReflector.getExactReturnType(TestClass.class.getMethod("returnType"), TestClass.class)));

This results in a StackOverflowError. Thank you for any insight you can provide!

Inconsistent results when using TypeToken to get AnnotatedType in static and instance methods

jdk version

1.8

example

package com.zyc;

import io.leangen.geantyref.TypeToken;

import java.lang.annotation.*;
import java.lang.reflect.AnnotatedParameterizedType;
import java.util.Arrays;
import java.util.List;

/**
 * @author zyc
 */
public class Test {

    @Target(ElementType.TYPE_USE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @interface Example {
    }

    public static void main(String[] args) {

        // Direct execution works
        AnnotatedParameterizedType annotatedParameterizedType = (AnnotatedParameterizedType) new TypeToken<List<@Example String>>() {
        }.getAnnotatedType();
        System.out.println(Arrays.toString(annotatedParameterizedType.getAnnotatedActualTypeArguments()[0].getDeclaredAnnotations()));

        // The static method works
        staticMethod();

        // The instance method does not work
        new Test().instanceMethod();
    }

    /**
     * static method
     */
    private static void staticMethod() {
        AnnotatedParameterizedType annotatedParameterizedType = (AnnotatedParameterizedType) new TypeToken<List<@Example String>>() {
        }.getAnnotatedType();
        System.out.println(Arrays.toString(annotatedParameterizedType.getAnnotatedActualTypeArguments()[0].getDeclaredAnnotations()));
    }

    /**
     * instance method
     */
    private void instanceMethod() {
        AnnotatedParameterizedType annotatedParameterizedType = (AnnotatedParameterizedType) new TypeToken<List<@Example String>>() {
        }.getAnnotatedType();
        System.out.println(Arrays.toString(annotatedParameterizedType.getAnnotatedActualTypeArguments()[0].getDeclaredAnnotations()));
    }

   @org.junit.Test
    public void test() {
        AnnotatedParameterizedType annotatedParameterizedType = (AnnotatedParameterizedType) new TypeToken<List<@Example String>>() {
        }.getAnnotatedType();
        System.out.println(Arrays.toString(annotatedParameterizedType.getAnnotatedActualTypeArguments()[0].getDeclaredAnnotations()));
    }
}

the expected result of the main method

[@com.zyc.Test$Example()]
[@com.zyc.Test$Example()]
[@com.zyc.Test$Example()]

the actual result of the main method

[@com.zyc.Test$Example()]
[@com.zyc.Test$Example()]
[]

junit result

[]

The test results are unexpected. Do you know where the problem is? thanks.

Add `module-info.java`

It would be cool to be able to use Geantyref from Modular Java, i.e. with tools like jlink. But right now, it is not possible, because Geantyref ships an Automatic-Module-Name, instead of a module-info.java.

compiling with JDK 11 leads to test failures

It seems that the toString() method for an annotation has changed post-JDK8. When compiling with JDK 11, set to JDK 8 compatibility, the type factory test fails here:

Screenshot 2024-01-19 at 12 13 40

JDK 11 adds the "" empty string to the definition when calling Annotation#toString(), while JDK8 does not.

getExactParameterTypes() for constructors

I would be useful to support getExactParameterTypes() not only for methods but also for constructors.

Given that geantyref already requires Java 1.8+, the easiest way to accomplish this is to change the method signature

public static AnnotatedType[] getExactParameterTypes(Method m, AnnotatedType declaringType)

to

public static AnnotatedType[] getExactParameterTypes(Executable m, AnnotatedType declaringType)

Likewise for the Type overload. No other changes required.

PS: Since Type overloads are implemented in terms of AnnotatedType overloads but discard annotation information, would it be more efficient to lift Type to AnnotatedType using new AnnotatedTypeImpl(type) rather than annotate(type)?

Calling hashCode on two equal `TypeToken`s returns different results

According to the javadocs for java.lang.Object#hashCode,

If two objects are equal according to the equals method, then calling the hashCode method on each of the two objects must produce the same integer result.

Currently, TypeToken does not abide by this.
It is possible to get two type tokens where first.equals(second) is true, but first.hashCode() == second.hashCode() is false.
This violates the contract of hashCode and breaks any attempt to use TypeToken as the key to a HashMap.

Here is some example code that can reproduce the issue:

import io.leangen.geantyref.TypeToken;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Test {
    public static void main(String[] args) throws NoSuchMethodException {
        TypeToken<Set<? extends Test>> testTypeToken = new TypeToken<>() {};
        Method method = Test.class.getMethod("testFunction", Set.class);
        TypeToken<?> otherTypeToken = TypeToken.get(method.getParameters()[0].getParameterizedType());

        Map<TypeToken<?>, String> testMap = new HashMap<>();
        testMap.put(otherTypeToken, "test");

        System.out.println("testTypeToken: " + testTypeToken);
        System.out.println("testTypeToken.getType(): " + testTypeToken.getType());
        System.out.println("testTypeToken.hashCode(): " + testTypeToken.hashCode());
        System.out.println();
        System.out.println("otherTypeToken: " + otherTypeToken);
        System.out.println("otherTypeToken.getType(): " + otherTypeToken.getType());
        System.out.println("otherTypeToken.hashCode(): " + otherTypeToken.hashCode());
        System.out.println();
        System.out.println("testTypeToken.equals(otherTypeToken): " + testTypeToken.equals(otherTypeToken));
        System.out.println("otherTypeToken.equals(testTypeToken): " + otherTypeToken.equals(testTypeToken));
        System.out.println();
        System.out.println("testMap: " + testMap);
        System.out.println("testMap.get(testTypeToken): " + testMap.get(testTypeToken));
    }

    public static void testFunction(Set<? extends Test> testSet) {}
}

The current output of this program is:

testTypeToken: Test$1@60f4877b
testTypeToken.getType(): java.util.Set<? extends Test>
testTypeToken.hashCode(): 1626638203

otherTypeToken: io.leangen.geantyref.TypeToken$2@17a57871
otherTypeToken.getType(): java.util.Set<? extends Test>
otherTypeToken.hashCode(): 396720241

testTypeToken.equals(otherTypeToken): true
otherTypeToken.equals(testTypeToken): true

testMap: {io.leangen.geantyref.TypeToken$2@17a57871=test}
testMap.get(testTypeToken): null

The expected output is:

 testTypeToken: Test$1@60f4877b
 testTypeToken.getType(): java.util.Set<? extends Test>
-testTypeToken.hashCode(): 1626638203
+testTypeToken.hashCode(): 396720241

 otherTypeToken: io.leangen.geantyref.TypeToken$2@17a57871
 otherTypeToken.getType(): java.util.Set<? extends Test>
 otherTypeToken.hashCode(): 396720241

 testTypeToken.equals(otherTypeToken): true
 otherTypeToken.equals(testTypeToken): true

 testMap: {io.leangen.geantyref.TypeToken$2@17a57871=test}
-testMap.get(testTypeToken): null
+testMap.get(testTypeToken): "test"

As you can see, if reflection is used to get the parameterized type of a method, and that type is then added to a hash map, you cannot access that parameterized type using one constructed via new TypeToken<Set<? extends Test>>() {}.

Mitigate Java's annotation parser bug where possible

Java versions before 11 (?) have multiple bugs in the annotation parser (e.g. this one) causing it to lose annotations on nested types.

It is in some cases possible to recover the lost class-level annotations when transforming a type to its canonical form, and GenericTypeReflector#toCanonical should take advantage of these mitigations where applicable.

Current trunk does not build for JDK 11 and has test failures for JDK 8

With JDK 11:

[ERROR] /Users/henning/oss/geantyref/src/main/java/io/leangen/geantyref/AnnotatedArrayTypeImpl.java:[13,1] io.leangen.geantyref.AnnotatedArrayTypeImpl is not abstract and does not override abstract method getAnnotatedOwnerType() in java.lang.reflect.AnnotatedArrayType
[ERROR] /Users/henning/oss/geantyref/src/main/java/io/leangen/geantyref/AnnotatedTypeVariableImpl.java:[13,1] io.leangen.geantyref.AnnotatedTypeVariableImpl is not abstract and does not override abstract method getAnnotatedOwnerType() in java.lang.reflect.AnnotatedTypeVariable
[ERROR] /Users/henning/oss/geantyref/src/main/java/io/leangen/geantyref/AnnotatedParameterizedTypeImpl.java:[15,1] io.leangen.geantyref.AnnotatedParameterizedTypeImpl is not abstract and does not override abstract method getAnnotatedOwnerType() in java.lang.reflect.AnnotatedParameterizedType
[ERROR] /Users/henning/oss/geantyref/src/main/java/io/leangen/geantyref/AnnotatedWildcardTypeImpl.java:[16,1] io.leangen.geantyref.AnnotatedWildcardTypeImpl is not abstract and does not override abstract method getAnnotatedOwnerType() in java.lang.reflect.AnnotatedWildcardType

With JDK 8, the current code compiles but shows errors:

  • [ERROR] Issue17Test.testTypeLiteralUsingTypeToken:25 Both TypeTokens should have equal hash codes expected:<391447681> but was:<148218870> (fixed by #21)
  • [ERROR] Issue20Test.testRecursiveTypes:60 array lengths differed, expected.length=5 actual.length=2; arrays first differed at element [0]; expected:<interface io.leangen.geantyref.Annotations$A1> but was:<interface io.leangen.geantyref.Annotations$A2>
  • [ERROR] TypeFactoryTest.testAnnotationEqualityAndHashCode:597 expected:<...eFactoryTest$A1(def=[, longs=[2, 4, 6], value=Test value, values=[value 1, value 2]])> but was:<...eFactoryTest$A1(def=["", longs={2L, 4L, 6L}, value="Test value", values={"value 1", "value 2"}])>

StackOverflowError in VarMap.map when calling GenericTypeReflector.getParameterTypes()

mfriedenhagen reported an issue in Spock spockframework/spock#1909 where geantyref throws a StackOverflowError.

I have removed the Spock dependency from the reproducer attached to the Spock issue, and the error is still there:

import java.lang.reflect.Method;
import java.util.Objects;

public class StackOverflowReproducer {

  static abstract class Status<StatusT, StatusBuilderT> {
    static class Builder<StatusT, StatusBuilderT> {
    }
  }

  static abstract class Resource<StatusT, StatusBuilderT> {
  }

  static class ProjectValidator {
    public <
      StatusT extends Status<StatusT, StatusBuilderT>,
      StatusBuilderT extends Status.Builder<StatusT, StatusBuilderT>,
      T extends Resource<StatusT, StatusBuilderT>>
    void validate(long id, Class<T> klass) {
      throw new UnsupportedOperationException("Just for testing");
    }
  }

  public static void main(String[] args) throws NoSuchMethodException {
    Class<?> cls = ProjectValidator.class;
    Method method = Objects.requireNonNull(cls.getMethod("validate", long.class, Class.class));
    io.leangen.geantyref.GenericTypeReflector.getParameterTypes(method, cls);
  }
}

The exception stack looks like:

java.lang.StackOverflowError
	at java.base/sun.reflect.generics.reflectiveObjects.TypeVariableImpl.getGenericDeclaration(TypeVariableImpl.java:144)
	at java.base/sun.reflect.generics.reflectiveObjects.TypeVariableImpl.typeVarIndex(TypeVariableImpl.java:227)
	at java.base/sun.reflect.generics.reflectiveObjects.TypeVariableImpl.getAnnotatedBounds(TypeVariableImpl.java:220)
	at java.base/sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeVariableImpl.getAnnotatedBounds(AnnotatedTypeFactory.java:383)
	at io.leangen.geantyref.VarMap.map(VarMap.java:98)
	at io.leangen.geantyref.VarMap.map(VarMap.java:115)
	at io.leangen.geantyref.VarMap.map(VarMap.java:148)
	at io.leangen.geantyref.VarMap.map(VarMap.java:98)

The used geantyref version is io.leangen.geantyref:geantyref:1.3.15
Tested for Java 8 and 17

Add a way to resolve bounded types directly

Coming from coekie#5 (comment)

I gave it a spin and came up with this:

  public static Type getResolvedReturnType(Method m, Type declaringType) {
    return resolveTypeVariableIfNecessary(GenericTypeReflector.getReturnType(m, declaringType));
  }

  public static Type getResolvedFieldType(Field f, Type declaringType) {
    return resolveTypeVariableIfNecessary(GenericTypeReflector.getFieldType(f, declaringType));
  }

  public static List<Type> getResolvedParameterTypes(Method m, Type declaringType) {
    return Arrays.stream(GenericTypeReflector.getParameterTypes(m, declaringType))
      .map(ReflectionUtil::resolveTypeVariableIfNecessary)
      .collect(Collectors.toList());
  }

  private static Type resolveTypeVariableIfNecessary(Type returnType) {
    if (returnType instanceof TypeVariable) {
      AnnotatedType annotatedType = GenericTypeReflector.annotate(returnType);
      return GenericTypeReflector.reduceBounded(annotatedType).getType();
    }
    return returnType;
  }

it seems to work, but I'm not sure its the best way. You can see the full change here https://github.com/leonard84/spock/tree/replace_gentyref_with_geantyref

I think that it might have value to have something like this in GenericTypeReflector directly, this way it could be optimized as well, the current implementation has to re-annotate the type that was discarded in GenericTypeReflector internally.

Type parameters don't resolve when enclosing class has a type parameter

Hi @kaqqao , we ran into some behavior that we don't understand over at jdbi/jdbi#2306

    @Test
    void testGenericTypeWorksInParameterizedClasses() {
        abstract class TypeCapture<T> {
            private final Type type;

            protected TypeCapture() {
                this.type = GenericTypeReflector.getTypeParameter(getClass(), TypeCapture.class.getTypeParameters()[0]);
            }

            public final Type getType() {
                return type;
            }
        }

        class Foo<T> {
            void checkType() {
                assertThat(new TypeCapture<List<String>>() {}.getType()).isEqualTo(TypeFactory.parameterizedClass(List.class, String.class));
            }
        }

        new Foo<>().checkType();
    }

This code works as you'd expect when the enclosing class Foo has no type parameter - but when you introduce a parameter Foo<T>, suddenly the type parameter is lost in the TypeCapture class and this test fails.

Is this a bug in GeAnTyRef, or are we perhaps using it incorrectly? Thank you!

Unexpected exception when erasing method return type from a wildcard generic class

Hi @kaqqao , thank you again for this great library. I think I might have found a bug:

@Test
public void testErase() throws Exception {
    GenericTypeReflector.erase(
            GenericTypeReflector.getExactReturnType(
                    E2.class.getMethod("getVal"),
                    GenericTypeReflector.addWildcardParameters(E2.class)));
}

public static class E2<T extends String> {
    private final T val;
    E2(final T val) {
        this.val = val;
    }
    public T getVal() {
        return val;
    }
}

Expected result: String.class
Actual result: RuntimeException

java.lang.RuntimeException: not supported: class io.leangen.geantyref.CaptureTypeImpl
	at io.leangen.geantyref.GenericTypeReflector.erase(GenericTypeReflector.java:87)
	at org.jdbi.v3.core.mapper.TestEnums.testErase(TestEnums.java:111)

GenericTypeReflector.transform visitor visitVariable cannot change type away from variable type

Hi @kaqqao thank you for your continued help, I wonder what you think of this.

Given the class,

public class GenericBean<T extends String> {
    public List<T> getProperty() {
        return Collections.emptyList();
    }
}

I want to figure out the most specific type for the return value of getProperty given that I don't have the parameterized GenericBean<T>, only GenericBean.class - replacing T with String (the bound) and then figure out List<String> as the property type.

To do this, I am trying to use GenericTypeReflector.transform and override visitVariable to replace variables with their bound.

private AnnotatedType resolveBounds(final Type type) {
    return GenericTypeReflector.transform(GenericTypeReflector.annotate(type), new TypeVisitor() {
        @Override
        protected AnnotatedType visitVariable(final AnnotatedTypeVariable type) {
            return type.getAnnotatedBounds()[0]; // T extends String -> String
        }
    });
}

        assertThat(Arrays.stream(GenericBean.class.getTypeParameters())
                .map(this::resolveBounds)
                .toArray(AnnotatedType[]::new))
            .containsExactly(GenericTypeReflector.annotate(String.class));

Unfortunately, despite this being statically safe, it does not actually work:

java.lang.ClassCastException: class sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeBaseImpl cannot be cast to class java.lang.reflect.AnnotatedTypeVariable (sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeBaseImpl and java.lang.reflect.AnnotatedTypeVariable are in module java.base of loader 'bootstrap')
	at io.leangen.geantyref.TypeVisitor.visitCaptureType(TypeVisitor.java:75)
	at io.leangen.geantyref.GenericTypeReflector.transform(GenericTypeReflector.java:1129)
	at io.leangen.geantyref.TypeVisitor.lambda$visitParameterizedType$0(TypeVisitor.java:27)
...
	at io.leangen.geantyref.TypeVisitor.visitParameterizedType(TypeVisitor.java:28)
	at io.leangen.geantyref.GenericTypeReflector.transform(GenericTypeReflector.java:1117)
	at org.jdbi.v3.core.argument.TestBeanArguments.resolveBounds(TestBeanArguments.java:292)
	at org.jdbi.v3.core.argument.TestBeanArguments.wut(TestBeanArguments.java:284)

It looks like the visitor assumes it can cast the result:

AnnotatedCaptureType annotatedCapture = new AnnotatedCaptureTypeImpl((CaptureType) type.getType(),
				(AnnotatedWildcardType) transform(type.getAnnotatedWildcardType(), this),
				(AnnotatedTypeVariable) transform(type.getAnnotatedTypeVariable(), this),
				null, type.getAnnotations());

Please let me know if I'm on the wrong track, or if there's something I'm missing. Thank you!

ParameterizedTypeImpl `toString()` method returns wrong method name for nested classes

Consider this:

Screenshot 2024-01-19 at 12 17 55

While the ParameterizedTypeImpl (which is part of geantryref) returns io.leangen.geantyref.Issue20Test.UnitX<io.leangen.geantyref.Issue20Test$QuantityX>, the actual class name is io.leangen.geantyref.Issue20Test$UnitX as UnitX is a nested class within the Issue20Test class. But the toString() method omits the $ as a marker for a nested class and uses a .

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.