diff --git a/src/main/java/org/springframework/data/util/ParameterizedTypeInformation.java b/src/main/java/org/springframework/data/util/ParameterizedTypeInformation.java index 832d2620f3..42f5cd6983 100644 --- a/src/main/java/org/springframework/data/util/ParameterizedTypeInformation.java +++ b/src/main/java/org/springframework/data/util/ParameterizedTypeInformation.java @@ -39,6 +39,7 @@ * @author Oliver Gierke * @author Mark Paluch * @author Christoph Strobl + * @author Jürgen Diez */ class ParameterizedTypeInformation extends ParentTypeAwareTypeInformation { @@ -67,7 +68,7 @@ public ParameterizedTypeInformation(ParameterizedType type, TypeDiscoverer pa @Nullable protected TypeInformation doGetMapValueType() { - if (Map.class.isAssignableFrom(getType())) { + if (isMap()) { Type[] arguments = type.getActualTypeArguments(); @@ -157,13 +158,13 @@ public boolean isAssignableFrom(TypeInformation target) { @Nullable protected TypeInformation doGetComponentType() { - boolean isCustomMapImplementation = isMap() && !getType().equals(Map.class); + boolean isCustomMapImplementation = isMap() && !isMapBaseType(); if (isCustomMapImplementation) { - return getRequiredSuperTypeInformation(Map.class).getComponentType(); + return getRequiredSuperTypeInformation(getMapBaseType()).getComponentType(); } - return createInfo(type.getActualTypeArguments()[0]); + return createInfo(this.type.getActualTypeArguments()[0]); } /* diff --git a/src/main/java/org/springframework/data/util/TypeDiscoverer.java b/src/main/java/org/springframework/data/util/TypeDiscoverer.java index 0bef50792c..3931df7458 100644 --- a/src/main/java/org/springframework/data/util/TypeDiscoverer.java +++ b/src/main/java/org/springframework/data/util/TypeDiscoverer.java @@ -52,10 +52,11 @@ * @author Oliver Gierke * @author Christoph Strobl * @author Mark Paluch + * @author Jürgen Diez */ class TypeDiscoverer implements TypeInformation { - private static final Class[] MAP_TYPES; + protected static final Class[] MAP_TYPES; static { @@ -355,7 +356,7 @@ public TypeInformation getMapValueType() { @Nullable protected TypeInformation doGetMapValueType() { - return isMap() ? getTypeArgument(getBaseType(MAP_TYPES), 1) + return isMap() ? getTypeArgument(getMapBaseType(), 1) : getTypeArguments().stream().skip(1).findFirst().orElse(null); } @@ -392,7 +393,7 @@ protected TypeInformation doGetComponentType() { } if (isMap()) { - return getTypeArgument(getBaseType(MAP_TYPES), 0); + return getTypeArgument(getMapBaseType(), 0); } if (Iterable.class.isAssignableFrom(rawType)) { @@ -527,6 +528,27 @@ private TypeInformation getTypeArgument(Class bound, int index) { : null; } + protected boolean isMapBaseType() { + return isBaseType(MAP_TYPES); + } + + private boolean isBaseType(Class[] candidates) { + + Class type = getType(); + + for (Class candidate: candidates) { + if (candidate.equals(type)) { + return true; + } + } + + return false; + } + + protected Class getMapBaseType() { + return getBaseType(MAP_TYPES); + } + private Class getBaseType(Class[] candidates) { Class type = getType(); diff --git a/src/test/java/org/springframework/data/util/ParameterizedTypeInformationUnitTests.java b/src/test/java/org/springframework/data/util/ParameterizedTypeInformationUnitTests.java index 8b728e412b..2df5742993 100755 --- a/src/test/java/org/springframework/data/util/ParameterizedTypeInformationUnitTests.java +++ b/src/test/java/org/springframework/data/util/ParameterizedTypeInformationUnitTests.java @@ -40,6 +40,7 @@ * * @author Oliver Gierke * @author Mark Paluch + * @author Jürgen Diez */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -76,22 +77,39 @@ void considersTypeInformationsWithSameParentsNotEqual() { } @Test // DATACMNS-88 - void resolvesMapValueTypeCorrectly() { + void resolvesMapTypesCorrectly() { TypeInformation type = ClassTypeInformation.from(Foo.class); TypeInformation propertyType = type.getProperty("param"); TypeInformation value = propertyType.getProperty("value"); + assertThat(propertyType.getComponentType().getType()).isEqualTo(Locale.class); assertThat(value.getType()).isEqualTo(String.class); assertThat(propertyType.getMapValueType().getType()).isEqualTo(String.class); propertyType = type.getProperty("param2"); value = propertyType.getProperty("value"); + assertThat(propertyType.getComponentType().getType()).isEqualTo(String.class); assertThat(value.getType()).isEqualTo(String.class); assertThat(propertyType.getMapValueType().getType()).isEqualTo(Locale.class); } + @Test + void resolvesVavrMapTypesCorrectly() { + + TypeInformation type = ClassTypeInformation.from(VavrFoo.class); + TypeInformation propertyType = type.getProperty("param"); + + assertThat(propertyType.getComponentType().getType()).isEqualTo(Locale.class); + assertThat(propertyType.getMapValueType().getType()).isEqualTo(String.class); + + propertyType = type.getProperty("param2"); + + assertThat(propertyType.getComponentType().getType()).isEqualTo(String.class); + assertThat(propertyType.getMapValueType().getType()).isEqualTo(Locale.class); + } + @Test // DATACMNS-446 void createsToStringRepresentation() { @@ -170,6 +188,11 @@ class Foo { Localized2 param2; } + class VavrFoo { + io.vavr.collection.HashMap param; + io.vavr.collection.HashMap param2; + } + class Bar { List param; } diff --git a/src/test/java/org/springframework/data/util/TypeDiscovererUnitTests.java b/src/test/java/org/springframework/data/util/TypeDiscovererUnitTests.java index d0a9dd8f29..94cf98756d 100755 --- a/src/test/java/org/springframework/data/util/TypeDiscovererUnitTests.java +++ b/src/test/java/org/springframework/data/util/TypeDiscovererUnitTests.java @@ -38,6 +38,7 @@ * Unit tests for {@link TypeDiscoverer}. * * @author Oliver Gierke + * @author Jürgen Diez */ @ExtendWith(MockitoExtension.class) public class TypeDiscovererUnitTests { @@ -179,6 +180,23 @@ void detectsSubTypes() { assertThat(type.isSubTypeOf(String.class)).isFalse(); } + @Test + void considerVavrMapToBeAMap() { + + TypeInformation type = from(io.vavr.collection.Map.class); + + assertThat(type.isMap()).isTrue(); + } + + @Test + void returnsComponentAndValueTypesForVavrMapExtensions() { + + TypeInformation discoverer = new TypeDiscoverer<>(CustomVavrMap.class, EMPTY_MAP); + + assertThat(discoverer.getMapValueType().getType()).isEqualTo(Locale.class); + assertThat(discoverer.getComponentType().getType()).isEqualTo(String.class); + } + class Person { Addresses addresses; @@ -228,4 +246,7 @@ public Iterator iterator() { return Collections.emptyIterator(); } } + + interface CustomVavrMap extends io.vavr.collection.Map { + } }