Browse Source

Consistent support for MultiValueMap and common Map implementations

Closes gh-30440
pull/30762/head
Juergen Hoeller 3 years ago
parent
commit
0211016957
  1. 48
      spring-core/src/main/java/org/springframework/core/CollectionFactory.java
  2. 54
      spring-core/src/test/java/org/springframework/core/CollectionFactoryTests.java

48
spring-core/src/main/java/org/springframework/core/CollectionFactory.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -68,6 +68,7 @@ public final class CollectionFactory {
approximableCollectionTypes.add(SortedSet.class); approximableCollectionTypes.add(SortedSet.class);
approximableCollectionTypes.add(NavigableSet.class); approximableCollectionTypes.add(NavigableSet.class);
approximableMapTypes.add(Map.class); approximableMapTypes.add(Map.class);
approximableMapTypes.add(MultiValueMap.class);
approximableMapTypes.add(SortedMap.class); approximableMapTypes.add(SortedMap.class);
approximableMapTypes.add(NavigableMap.class); approximableMapTypes.add(NavigableMap.class);
@ -80,6 +81,7 @@ public final class CollectionFactory {
approximableCollectionTypes.add(EnumSet.class); approximableCollectionTypes.add(EnumSet.class);
approximableMapTypes.add(HashMap.class); approximableMapTypes.add(HashMap.class);
approximableMapTypes.add(LinkedHashMap.class); approximableMapTypes.add(LinkedHashMap.class);
approximableMapTypes.add(LinkedMultiValueMap.class);
approximableMapTypes.add(TreeMap.class); approximableMapTypes.add(TreeMap.class);
approximableMapTypes.add(EnumMap.class); approximableMapTypes.add(EnumMap.class);
} }
@ -121,13 +123,7 @@ public final class CollectionFactory {
*/ */
@SuppressWarnings({"rawtypes", "unchecked", "cast"}) @SuppressWarnings({"rawtypes", "unchecked", "cast"})
public static <E> Collection<E> createApproximateCollection(@Nullable Object collection, int capacity) { public static <E> Collection<E> createApproximateCollection(@Nullable Object collection, int capacity) {
if (collection instanceof LinkedList) { if (collection instanceof EnumSet) {
return new LinkedList<>();
}
else if (collection instanceof List) {
return new ArrayList<>(capacity);
}
else if (collection instanceof EnumSet) {
// Cast is necessary for compilation in Eclipse 4.4.1. // Cast is necessary for compilation in Eclipse 4.4.1.
Collection<E> enumSet = (Collection<E>) EnumSet.copyOf((EnumSet) collection); Collection<E> enumSet = (Collection<E>) EnumSet.copyOf((EnumSet) collection);
enumSet.clear(); enumSet.clear();
@ -136,6 +132,12 @@ public final class CollectionFactory {
else if (collection instanceof SortedSet) { else if (collection instanceof SortedSet) {
return new TreeSet<>(((SortedSet<E>) collection).comparator()); return new TreeSet<>(((SortedSet<E>) collection).comparator());
} }
if (collection instanceof LinkedList) {
return new LinkedList<>();
}
else if (collection instanceof List) {
return new ArrayList<>(capacity);
}
else { else {
return new LinkedHashSet<>(capacity); return new LinkedHashSet<>(capacity);
} }
@ -191,8 +193,8 @@ public final class CollectionFactory {
else if (LinkedList.class == collectionType) { else if (LinkedList.class == collectionType) {
return new LinkedList<>(); return new LinkedList<>();
} }
else if (TreeSet.class == collectionType || NavigableSet.class == collectionType else if (TreeSet.class == collectionType || NavigableSet.class == collectionType ||
|| SortedSet.class == collectionType) { SortedSet.class == collectionType) {
return new TreeSet<>(); return new TreeSet<>();
} }
else if (EnumSet.class.isAssignableFrom(collectionType)) { else if (EnumSet.class.isAssignableFrom(collectionType)) {
@ -251,6 +253,9 @@ public final class CollectionFactory {
else if (map instanceof SortedMap) { else if (map instanceof SortedMap) {
return new TreeMap<>(((SortedMap<K, V>) map).comparator()); return new TreeMap<>(((SortedMap<K, V>) map).comparator());
} }
else if (map instanceof MultiValueMap) {
return new LinkedMultiValueMap(capacity);
}
else { else {
return new LinkedHashMap<>(capacity); return new LinkedHashMap<>(capacity);
} }
@ -297,26 +302,21 @@ public final class CollectionFactory {
@SuppressWarnings({"rawtypes", "unchecked"}) @SuppressWarnings({"rawtypes", "unchecked"})
public static <K, V> Map<K, V> createMap(Class<?> mapType, @Nullable Class<?> keyType, int capacity) { public static <K, V> Map<K, V> createMap(Class<?> mapType, @Nullable Class<?> keyType, int capacity) {
Assert.notNull(mapType, "Map type must not be null"); Assert.notNull(mapType, "Map type must not be null");
if (mapType.isInterface()) { if (LinkedHashMap.class == mapType || HashMap.class == mapType || Map.class == mapType) {
if (Map.class == mapType) { return new LinkedHashMap<>(capacity);
return new LinkedHashMap<>(capacity); }
} else if (LinkedMultiValueMap.class == mapType || MultiValueMap.class == mapType) {
else if (SortedMap.class == mapType || NavigableMap.class == mapType) { return new LinkedMultiValueMap();
return new TreeMap<>(); }
} else if (TreeMap.class == mapType || SortedMap.class == mapType || NavigableMap.class == mapType) {
else if (MultiValueMap.class == mapType) { return new TreeMap<>();
return new LinkedMultiValueMap();
}
else {
throw new IllegalArgumentException("Unsupported Map interface: " + mapType.getName());
}
} }
else if (EnumMap.class == mapType) { else if (EnumMap.class == mapType) {
Assert.notNull(keyType, "Cannot create EnumMap for unknown key type"); Assert.notNull(keyType, "Cannot create EnumMap for unknown key type");
return new EnumMap(asEnumType(keyType)); return new EnumMap(asEnumType(keyType));
} }
else { else {
if (!Map.class.isAssignableFrom(mapType)) { if (mapType.isInterface() || !Map.class.isAssignableFrom(mapType)) {
throw new IllegalArgumentException("Unsupported Map type: " + mapType.getName()); throw new IllegalArgumentException("Unsupported Map type: " + mapType.getName());
} }
try { try {

54
spring-core/src/test/java/org/springframework/core/CollectionFactoryTests.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -209,21 +209,23 @@ class CollectionFactoryTests {
@Test @Test
void createsCollectionsCorrectly() { void createsCollectionsCorrectly() {
// interfaces // interfaces
assertThat(createCollection(List.class, 0)).isInstanceOf(ArrayList.class); testCollection(List.class, ArrayList.class);
assertThat(createCollection(Set.class, 0)).isInstanceOf(LinkedHashSet.class); testCollection(Set.class, LinkedHashSet.class);
assertThat(createCollection(Collection.class, 0)).isInstanceOf(LinkedHashSet.class); testCollection(Collection.class, LinkedHashSet.class);
assertThat(createCollection(SortedSet.class, 0)).isInstanceOf(TreeSet.class); testCollection(SortedSet.class, TreeSet.class);
assertThat(createCollection(NavigableSet.class, 0)).isInstanceOf(TreeSet.class); testCollection(NavigableSet.class, TreeSet.class);
assertThat(createCollection(List.class, String.class, 0)).isInstanceOf(ArrayList.class);
assertThat(createCollection(Set.class, String.class, 0)).isInstanceOf(LinkedHashSet.class);
assertThat(createCollection(Collection.class, String.class, 0)).isInstanceOf(LinkedHashSet.class);
assertThat(createCollection(SortedSet.class, String.class, 0)).isInstanceOf(TreeSet.class);
assertThat(createCollection(NavigableSet.class, String.class, 0)).isInstanceOf(TreeSet.class);
// concrete types // concrete types
assertThat(createCollection(HashSet.class, 0)).isInstanceOf(HashSet.class); testCollection(ArrayList.class, ArrayList.class);
assertThat(createCollection(HashSet.class, String.class, 0)).isInstanceOf(HashSet.class); testCollection(HashSet.class, LinkedHashSet.class);
testCollection(LinkedHashSet.class, LinkedHashSet.class);
testCollection(TreeSet.class, TreeSet.class);
}
private void testCollection(Class<?> collectionType, Class<?> resultType) {
assertThat(CollectionFactory.isApproximableCollectionType(collectionType)).isTrue();
assertThat(createCollection(collectionType, 0)).isInstanceOf(resultType);
assertThat(createCollection(collectionType, String.class, 0)).isInstanceOf(resultType);
} }
@Test @Test
@ -258,20 +260,22 @@ class CollectionFactoryTests {
@Test @Test
void createsMapsCorrectly() { void createsMapsCorrectly() {
// interfaces // interfaces
assertThat(createMap(Map.class, 0)).isInstanceOf(LinkedHashMap.class); testMap(Map.class, LinkedHashMap.class);
assertThat(createMap(SortedMap.class, 0)).isInstanceOf(TreeMap.class); testMap(SortedMap.class, TreeMap.class);
assertThat(createMap(NavigableMap.class, 0)).isInstanceOf(TreeMap.class); testMap(NavigableMap.class, TreeMap.class);
assertThat(createMap(MultiValueMap.class, 0)).isInstanceOf(LinkedMultiValueMap.class); testMap(MultiValueMap.class, LinkedMultiValueMap.class);
assertThat(createMap(Map.class, String.class, 0)).isInstanceOf(LinkedHashMap.class);
assertThat(createMap(SortedMap.class, String.class, 0)).isInstanceOf(TreeMap.class);
assertThat(createMap(NavigableMap.class, String.class, 0)).isInstanceOf(TreeMap.class);
assertThat(createMap(MultiValueMap.class, String.class, 0)).isInstanceOf(LinkedMultiValueMap.class);
// concrete types // concrete types
assertThat(createMap(HashMap.class, 0)).isInstanceOf(HashMap.class); testMap(HashMap.class, LinkedHashMap.class);
testMap(LinkedHashMap.class, LinkedHashMap.class);
testMap(TreeMap.class, TreeMap.class);
testMap(LinkedMultiValueMap.class, LinkedMultiValueMap.class);
}
assertThat(createMap(HashMap.class, String.class, 0)).isInstanceOf(HashMap.class); private void testMap(Class<?> mapType, Class<?> resultType) {
assertThat(CollectionFactory.isApproximableMapType(mapType)).isTrue();
assertThat(createMap(mapType, 0)).isInstanceOf(resultType);
assertThat(createMap(mapType, String.class, 0)).isInstanceOf(resultType);
} }
@Test @Test

Loading…
Cancel
Save