diff --git a/src/main/java/org/springframework/data/util/ParameterizedTypeInformation.java b/src/main/java/org/springframework/data/util/ParameterizedTypeInformation.java index f85d681ea..f3a03f330 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 { @@ -63,7 +64,7 @@ class ParameterizedTypeInformation extends ParentTypeAwareTypeInformation @Nullable protected TypeInformation doGetMapValueType() { - if (Map.class.isAssignableFrom(getType())) { + if (isMap()) { var arguments = type.getActualTypeArguments(); @@ -141,13 +142,13 @@ class ParameterizedTypeInformation extends ParentTypeAwareTypeInformation @Nullable protected TypeInformation doGetComponentType() { - var isCustomMapImplementation = isMap() && !getType().equals(Map.class); + var 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]); } @Override diff --git a/src/main/java/org/springframework/data/util/TypeDiscoverer.java b/src/main/java/org/springframework/data/util/TypeDiscoverer.java index 606423386..940fab109 100644 --- a/src/main/java/org/springframework/data/util/TypeDiscoverer.java +++ b/src/main/java/org/springframework/data/util/TypeDiscoverer.java @@ -45,7 +45,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 { @@ -328,7 +328,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); } @@ -357,7 +357,7 @@ class TypeDiscoverer implements TypeInformation { } if (isMap()) { - return getTypeArgument(getBaseType(MAP_TYPES), 0); + return getTypeArgument(getMapBaseType(), 0); } if (Iterable.class.isAssignableFrom(rawType)) { @@ -469,6 +469,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) { var 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 100a5a524..513ec77e3 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); var propertyType = type.getProperty("param"); var 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 2349e6dcf..d4490ef13 100755 --- a/src/test/java/org/springframework/data/util/TypeDiscovererUnitTests.java +++ b/src/test/java/org/springframework/data/util/TypeDiscovererUnitTests.java @@ -187,6 +187,15 @@ public class TypeDiscovererUnitTests { assertThat(type.isMap()).isTrue(); } + @Test // #2517 + void returnsComponentAndValueTypesForVavrMapExtensions() { + + var discoverer = new TypeDiscoverer<>(CustomVavrMap.class, EMPTY_MAP); + + assertThat(discoverer.getMapValueType().getType()).isEqualTo(Locale.class); + assertThat(discoverer.getComponentType().getType()).isEqualTo(String.class); + } + @Test // #2511 void considerVavrSetToBeCollectionLike() { @@ -260,4 +269,6 @@ public class TypeDiscovererUnitTests { return Collections.emptyIterator(); } } + + interface CustomVavrMap extends io.vavr.collection.Map {} }