diff --git a/src/main/java/org/springframework/data/util/ParameterizedTypeInformation.java b/src/main/java/org/springframework/data/util/ParameterizedTypeInformation.java index 832d2620f..42f5cd698 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 @@ import org.springframework.util.StringUtils; * @author Oliver Gierke * @author Mark Paluch * @author Christoph Strobl + * @author Jürgen Diez */ class ParameterizedTypeInformation extends ParentTypeAwareTypeInformation { @@ -67,7 +68,7 @@ class ParameterizedTypeInformation extends ParentTypeAwareTypeInformation @Nullable protected TypeInformation doGetMapValueType() { - if (Map.class.isAssignableFrom(getType())) { + if (isMap()) { Type[] arguments = type.getActualTypeArguments(); @@ -157,13 +158,13 @@ class ParameterizedTypeInformation extends ParentTypeAwareTypeInformation @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 7198a11f2..eb7db2785 100644 --- a/src/main/java/org/springframework/data/util/TypeDiscoverer.java +++ b/src/main/java/org/springframework/data/util/TypeDiscoverer.java @@ -47,7 +47,7 @@ import org.springframework.util.ReflectionUtils; */ class TypeDiscoverer implements TypeInformation { - private static final Class[] MAP_TYPES; + protected static final Class[] MAP_TYPES; private static final Class[] COLLECTION_TYPES; static { @@ -361,7 +361,7 @@ class TypeDiscoverer implements TypeInformation { @Nullable protected TypeInformation doGetMapValueType() { - return isMap() ? getTypeArgument(getBaseType(MAP_TYPES), 1) + return isMap() ? getTypeArgument(getMapBaseType(), 1) : getTypeArguments().stream().skip(1).findFirst().orElse(null); } @@ -398,7 +398,7 @@ class TypeDiscoverer implements TypeInformation { } if (isMap()) { - return getTypeArgument(getBaseType(MAP_TYPES), 0); + return getTypeArgument(getMapBaseType(), 0); } if (Iterable.class.isAssignableFrom(rawType)) { @@ -533,6 +533,27 @@ class TypeDiscoverer implements TypeInformation { : 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 8b728e412..2df574299 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 @@ import org.mockito.quality.Strictness; * * @author Oliver Gierke * @author Mark Paluch + * @author Jürgen Diez */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -76,22 +77,39 @@ class ParameterizedTypeInformationUnitTests { } @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 ParameterizedTypeInformationUnitTests { 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 9772a5735..9c538e92b 100755 --- a/src/test/java/org/springframework/data/util/TypeDiscovererUnitTests.java +++ b/src/test/java/org/springframework/data/util/TypeDiscovererUnitTests.java @@ -188,6 +188,15 @@ public class TypeDiscovererUnitTests { assertThat(type.isMap()).isTrue(); } + @Test // #2517 + void returnsComponentAndValueTypesForVavrMapExtensions() { + + TypeInformation discoverer = new TypeDiscoverer<>(CustomVavrMap.class, EMPTY_MAP); + + assertThat(discoverer.getMapValueType().getType()).isEqualTo(Locale.class); + assertThat(discoverer.getComponentType().getType()).isEqualTo(String.class); + } + @Test void considerVavrSetToBeCollectionLike() { @@ -261,4 +270,6 @@ public class TypeDiscovererUnitTests { return Collections.emptyIterator(); } } + + interface CustomVavrMap extends io.vavr.collection.Map {} }