Skip to content

Fix type resolution for static methods (regression in 2.11.3) #2894

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -221,12 +221,6 @@ private List<AnnotatedMethod> _findPotentialFactories(JavaType type, Class<?> pr
if (candidates == null) {
return Collections.emptyList();
}
// 05-Sep-2020, tatu: Important fix wrt [databind#2821] -- static methods
// do NOT have type binding context of the surrounding class and although
// passing that should not break things, it appears to... Regardless,
// it should not be needed or useful as those bindings are only available
// to non-static members
TypeResolutionContext typeResCtxt = new TypeResolutionContext.Empty(_typeFactory);

int factoryCount = candidates.size();
List<AnnotatedMethod> result = new ArrayList<>(factoryCount);
Expand All @@ -250,8 +244,7 @@ private List<AnnotatedMethod> _findPotentialFactories(JavaType type, Class<?> pr
for (int i = 0; i < factoryCount; ++i) {
if (key.equals(methodKeys[i])) {
result.set(i,
constructFactoryCreator(candidates.get(i),
typeResCtxt, mixinFactory));
constructFactoryCreator(candidates.get(i), mixinFactory));
break;
}
}
Expand All @@ -262,8 +255,7 @@ private List<AnnotatedMethod> _findPotentialFactories(JavaType type, Class<?> pr
AnnotatedMethod factory = result.get(i);
if (factory == null) {
result.set(i,
constructFactoryCreator(candidates.get(i),
typeResCtxt, null));
constructFactoryCreator(candidates.get(i), null));
}
}
return result;
Expand Down Expand Up @@ -335,19 +327,18 @@ protected AnnotatedConstructor constructNonDefaultConstructor(ClassUtil.Ctor cto
collectAnnotations(ctor, mixin), resolvedAnnotations);
}

protected AnnotatedMethod constructFactoryCreator(Method m,
TypeResolutionContext typeResCtxt, Method mixin)
protected AnnotatedMethod constructFactoryCreator(Method m, Method mixin)
{
final int paramCount = m.getParameterTypes().length;
if (_intr == null) { // when annotation processing is disabled
return new AnnotatedMethod(typeResCtxt, m, _emptyAnnotationMap(),
return new AnnotatedMethod(_typeContext, m, _emptyAnnotationMap(),
_emptyAnnotationMaps(paramCount));
}
if (paramCount == 0) { // common enough we can slightly optimize
return new AnnotatedMethod(typeResCtxt, m, collectAnnotations(m, mixin),
return new AnnotatedMethod(_typeContext, m, collectAnnotations(m, mixin),
NO_ANNOTATION_MAPS);
}
return new AnnotatedMethod(typeResCtxt, m, collectAnnotations(m, mixin),
return new AnnotatedMethod(_typeContext, m, collectAnnotations(m, mixin),
collectAnnotations(m.getParameterAnnotations(),
(mixin == null) ? null : mixin.getParameterAnnotations()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,25 +44,4 @@ public String toString() {
}
*/
}

/**
* Dummy implementation for case where there are no bindings available
* (for example, for static methods and fields)
*
* @since 2.11.3
*/
public static class Empty
implements TypeResolutionContext
{
private final TypeFactory _typeFactory;

public Empty(TypeFactory tf) {
_typeFactory = tf;
}

@Override
public JavaType resolveType(Type type) {
return _typeFactory.constructType(type);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

import com.fasterxml.jackson.databind.BaseMapTest;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ser.GenericTypeSerializationTest.WrapperWithFactoryMethod.Value;

import static java.util.Objects.requireNonNull;

public class GenericTypeSerializationTest extends BaseMapTest
{
Expand Down Expand Up @@ -164,6 +167,74 @@ public static Attributes2821 dummyMethod(Map attributes) {
}
}

public static class WrapperWithFactoryMethod<T> {
private final List<T> values;

private WrapperWithFactoryMethod(List<T> values) {
this.values = requireNonNull(values, "values is null");
}

@JsonProperty
public List<T> getValues() {
return values;
}

@JsonCreator
public static <T> WrapperWithFactoryMethod<T> fromValues(@JsonProperty("values") List<T> values) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please note that T here is NOT related to type variable T for class, and as such can not realistically be used as a creator method. It just happens to have same name, but being static method is not part of the class or bindings in that sense.

return new WrapperWithFactoryMethod<>(values);
}

@Override
public boolean equals(Object o) {
if (this == o) { return true; }
if (o == null || getClass() != o.getClass()) { return false; }
WrapperWithFactoryMethod<?> wrapper = (WrapperWithFactoryMethod<?>) o;
return Objects.equals(values, wrapper.values);
}

@Override
public int hashCode() {
return Objects.hash(values);
}

@Override
public String toString() {
return values.toString();
}

public static class Value {
public int x;

@JsonCreator
public Value(@JsonProperty("x") int x) {
this.x = x;
}

@JsonProperty
public int getX() {
return x;
}

@Override
public boolean equals(Object o) {
if (this == o) { return true; }
if (o == null || getClass() != o.getClass()) { return false; }
Value rawValue = (Value) o;
return x == rawValue.x;
}

@Override
public int hashCode() {
return Objects.hash(x);
}

@Override
public String toString() {
return String.valueOf(x);
}
}
}

/*
/**********************************************************
/* Unit tests
Expand Down Expand Up @@ -259,4 +330,11 @@ public void testTypeResolution2821() throws Exception
String json = MAPPER.writeValueAsString(val);
assertNotNull(json);
}

public void testTypeResolutionFactoryMethod() throws Exception
{
WrapperWithFactoryMethod<Value> src = new WrapperWithFactoryMethod<>(Arrays.asList(new Value(1), new Value(2)));
WrapperWithFactoryMethod<Value> output = MAPPER.readValue(MAPPER.writeValueAsString(src), new TypeReference<WrapperWithFactoryMethod<Value>>() {});
assertEquals(src, output);
}
}