Skip to content

Commit cf36560

Browse files
committed
Resolve ValueTransformer's Value and Bound type parameters to determine the supported types of a ValueTransformer instead of using the methods' raw return types.
1 parent 24a202c commit cf36560

File tree

4 files changed

+114
-76
lines changed

4 files changed

+114
-76
lines changed

src/main/java/de/zalando/sprocwrapper/globalvaluetransformer/GlobalValueTransformerLoader.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -66,18 +66,20 @@ public boolean apply(final String input) {
6666
final Set<Class<?>> typesAnnotatedWith = reflections.getTypesAnnotatedWith(
6767
GlobalValueTransformer.class);
6868
for (final Class<?> foundGlobalValueTransformer : typesAnnotatedWith) {
69-
final Class<?> valueTransformerReturnType = ValueTransformerUtils.getUnmarshalFromDbClass(
70-
foundGlobalValueTransformer);
71-
if (valueTransformerReturnType != null) {
69+
final Class<?> valueTransformerReturnType;
70+
try {
71+
valueTransformerReturnType = ValueTransformerUtils.getUnmarshalFromDbClass(
72+
foundGlobalValueTransformer);
7273
GlobalValueTransformerRegistry.register(valueTransformerReturnType,
7374
(ValueTransformer<?, ?>) foundGlobalValueTransformer.newInstance());
74-
LOG.debug("Global Value Transformer [{}] for type [{}] registered. ",
75-
foundGlobalValueTransformer.getSimpleName(), valueTransformerReturnType.getSimpleName());
76-
} else {
77-
LOG.error(
78-
"Could add global transformer [{}] to global registry. Could not find method unmarshalFromDb.",
79-
foundGlobalValueTransformer);
75+
} catch (final RuntimeException e) {
76+
LOG.error("Failed to add global transformer [{}] to global registry.",
77+
foundGlobalValueTransformer, e);
78+
continue;
8079
}
80+
81+
LOG.debug("Global Value Transformer [{}] for type [{}] registered. ",
82+
foundGlobalValueTransformer.getSimpleName(), valueTransformerReturnType.getSimpleName());
8183
}
8284
}
8385

src/main/java/de/zalando/sprocwrapper/globalvaluetransformer/ReflectionUtils.java

Lines changed: 0 additions & 53 deletions
This file was deleted.
Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,43 @@
11
package de.zalando.sprocwrapper.globalvaluetransformer;
22

3+
import static com.google.common.base.Preconditions.checkArgument;
4+
35
import java.lang.reflect.Method;
6+
import java.lang.reflect.Type;
7+
8+
import com.google.common.reflect.TypeToken;
49

510
import de.zalando.typemapper.core.ValueTransformer;
611

712
public final class ValueTransformerUtils {
813
private ValueTransformerUtils() { }
914

10-
public static Class<?> getMarshalToDbClass(final Class<?> valueTransformer) {
11-
final Method method = ReflectionUtils.findMethod(valueTransformer, "marshalToDb");
12-
if (method != null) {
13-
return method.getReturnType();
14-
}
15+
public static Class<?> getMarshalToDbClass(final ValueTransformer<?, ?> valueTransformer) {
16+
return resolveReturnType(valueTransformer.getClass(), "marshalToDb", Object.class);
17+
}
1518

16-
return null;
19+
public static Class<?> getUnmarshalFromDbClass(final Class<?> clazz) {
20+
return resolveReturnType(checkValueTransformerClass(clazz), "unmarshalFromDb", String.class);
1721
}
1822

19-
public static Class<?> getUnmarshalFromDbClass(final Class<?> valueTransformerClass) {
20-
final Method method = ReflectionUtils.findMethod(valueTransformerClass, "unmarshalFromDb");
21-
if (method != null) {
22-
return method.getReturnType();
23-
}
23+
private static <T extends ValueTransformer<?, ?>> Class<?> resolveReturnType(final Class<T> actualClass,
24+
final String methodName, final Class<?>... methodParameterTypes) {
25+
final Method valueTransformerMethod = findValueTransformerMethod(methodName, methodParameterTypes);
26+
final Type genericReturnType = valueTransformerMethod.getGenericReturnType();
27+
return TypeToken.of(actualClass).resolveType(genericReturnType).getRawType();
28+
}
2429

25-
return null;
30+
private static Method findValueTransformerMethod(final String name, final Class<?>... parameterTypes) {
31+
try {
32+
return ValueTransformer.class.getMethod(name, parameterTypes);
33+
} catch (final NoSuchMethodException | SecurityException e) {
34+
throw new IllegalArgumentException("couldn't locate ValueTransformer method " + name, e);
35+
}
2636
}
2737

28-
public static Class<?> getMarshalToDbClass(final ValueTransformer<?, ?> valueTransformerForClass) {
29-
return valueTransformerForClass != null ? getMarshalToDbClass(valueTransformerForClass.getClass()) : null;
38+
@SuppressWarnings("unchecked")
39+
private static Class<? extends ValueTransformer<?, ?>> checkValueTransformerClass(final Class<?> clazz) {
40+
checkArgument(ValueTransformer.class.isAssignableFrom(clazz), "not a ValueTransformer: %s", clazz);
41+
return (Class<? extends ValueTransformer<?, ?>>) clazz;
3042
}
3143
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package de.zalando.sprocwrapper.globalvaluetransformer;
2+
3+
import static org.hamcrest.CoreMatchers.is;
4+
import static org.hamcrest.CoreMatchers.sameInstance;
5+
6+
import static org.hamcrest.MatcherAssert.assertThat;
7+
8+
import org.junit.Test;
9+
10+
import de.zalando.typemapper.core.ValueTransformer;
11+
12+
public class ValueTransformerUtilsTest {
13+
14+
/**
15+
* A custom value that shall be transformed using a {@link ValueTransformer}.
16+
*/
17+
public static class CustomValue {
18+
// just a dummy type
19+
}
20+
21+
/**
22+
* A custom bound that shall be transformed using a {@link ValueTransformer}.
23+
*/
24+
public static class CustomBound {
25+
// just a dummy type
26+
}
27+
28+
/**
29+
* An example for an abstract parameterized transformer that does &quot;something&quot; and then delegates to a
30+
* subclass's method.
31+
*/
32+
public abstract static class AbstractParameterizedTransformer<Value, Bound> extends ValueTransformer<Value, Bound> {
33+
34+
@Override
35+
public Bound unmarshalFromDb(final String value) {
36+
return delegatedUnmarshalling(value);
37+
}
38+
39+
@Override
40+
public Value marshalToDb(final Bound bound) {
41+
return delegatedMarshalling(bound);
42+
}
43+
44+
// actual unmarshalling performed by subclasses that operate on a preprocessed value
45+
protected abstract Bound delegatedUnmarshalling(String preprocessed);
46+
47+
// actual marshalling performed by subclasses that operate on a preprocessed value
48+
protected abstract Value delegatedMarshalling(Bound preprocessed);
49+
}
50+
51+
/**
52+
* An example for a concrete transformer that uses an abstract parameterized transformer as a superclass.
53+
*/
54+
public static class ConcreteTransformer extends AbstractParameterizedTransformer<CustomValue, CustomBound> {
55+
@Override
56+
protected CustomBound delegatedUnmarshalling(final String preprocessed) {
57+
return null; // doesn't do anything meaningful, just an example
58+
}
59+
60+
@Override
61+
protected CustomValue delegatedMarshalling(final CustomBound preprocessed) {
62+
return null; // doesn't do anything meaningful, just an example
63+
}
64+
}
65+
66+
@Test
67+
public void resolvesParamterizedUnmarshalReturnType() {
68+
assertThat(ValueTransformerUtils.getUnmarshalFromDbClass(ConcreteTransformer.class),
69+
is(sameInstance((Object) CustomBound.class)));
70+
}
71+
72+
@Test
73+
public void resolvesParamterizedMarshalReturnType() {
74+
assertThat(ValueTransformerUtils.getMarshalToDbClass(new ConcreteTransformer()),
75+
is(sameInstance((Object) CustomValue.class)));
76+
}
77+
}

0 commit comments

Comments
 (0)