Support for Eclipse Collections.

Using the newly introduced CustomCollectionsRegistrar SPI to provide support for Eclipse Collections (Imm|M)utable(List|Set|Bag|Map).

Fixes #2618.
This commit is contained in:
Oliver Drotbohm
2022-05-03 13:32:52 +02:00
parent b3ec06f479
commit deceb867c5
5 changed files with 381 additions and 3 deletions

View File

@@ -27,8 +27,8 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.stream.Stream;
import org.eclipse.collections.api.list.ImmutableList;
import org.junit.jupiter.api.Test;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
@@ -248,6 +248,15 @@ class QueryMethodUnitTests {
assertThat(returnedType.getDomainType()).isEqualTo(User.class);
}
@Test // #1817
void considersEclipseCollectionCollectionQuery() throws Exception {
var method = SampleRepository.class.getMethod("returnsEclipseCollection");
var queryMethod = new QueryMethod(method, metadata, factory);
assertThat(queryMethod.isCollectionQuery()).isTrue();
}
interface SampleRepository extends Repository<User, Serializable> {
String pagingMethodWithInvalidReturnType(Pageable pageable);
@@ -294,6 +303,8 @@ class QueryMethodUnitTests {
Future<Option<User>> returnsFutureOfOption();
Mono<Slice<User>> reactiveSlice();
ImmutableList<User> returnsEclipseCollection();
}
class User {

View File

@@ -27,6 +27,23 @@ import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
import org.eclipse.collections.api.RichIterable;
import org.eclipse.collections.api.bag.ImmutableBag;
import org.eclipse.collections.api.bag.MutableBag;
import org.eclipse.collections.api.factory.Lists;
import org.eclipse.collections.api.factory.Maps;
import org.eclipse.collections.api.factory.Sets;
import org.eclipse.collections.api.list.ImmutableList;
import org.eclipse.collections.api.list.ListIterable;
import org.eclipse.collections.api.list.MutableList;
import org.eclipse.collections.api.map.ImmutableMap;
import org.eclipse.collections.api.map.MapIterable;
import org.eclipse.collections.api.map.MutableMap;
import org.eclipse.collections.api.set.ImmutableSet;
import org.eclipse.collections.api.set.MutableSet;
import org.eclipse.collections.api.set.SetIterable;
import org.eclipse.collections.impl.map.immutable.ImmutableUnifiedMap;
import org.eclipse.collections.impl.map.mutable.UnifiedMap;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.Test;
@@ -61,6 +78,17 @@ class CustomCollectionsUnitTests {
.verify();
}
@TestFactory // #1817
Stream<DynamicTest> registersEclipseCollections() {
return new CustomCollectionTester()
.withCollections(ImmutableSet.class, ImmutableList.class, ImmutableBag.class, MutableSet.class,
MutableList.class, MutableBag.class)
.withMaps(ImmutableMap.class, MutableMap.class)
.withMapImplementations(ImmutableUnifiedMap.class, UnifiedMap.class)
.verify();
}
@Test // DATACMNS-1065, #1817
void conversListToVavr() {
@@ -106,6 +134,63 @@ class CustomCollectionsUnitTests {
assertThat(result).isInstanceOf(io.vavr.collection.Map.class);
}
@Test // #1817
void conversListToEclipse() {
assertThat(conversionService.canConvert(List.class, RichIterable.class)).isTrue();
assertThat(conversionService.canConvert(List.class, ImmutableList.class)).isTrue();
assertThat(conversionService.canConvert(List.class, ImmutableSet.class)).isTrue();
assertThat(conversionService.canConvert(List.class, ImmutableBag.class)).isTrue();
assertThat(conversionService.canConvert(List.class, ImmutableMap.class)).isFalse();
List<Integer> integers = Arrays.asList(1, 2, 3);
assertThat(conversionService.convert(integers, RichIterable.class)).isInstanceOf(MutableList.class);
assertThat(conversionService.convert(integers, MutableList.class)).isInstanceOf(MutableList.class);
assertThat(conversionService.convert(integers, MutableSet.class)).isInstanceOf(MutableSet.class);
assertThat(conversionService.convert(integers, MutableBag.class)).isInstanceOf(MutableBag.class);
assertThat(conversionService.convert(integers, ImmutableList.class)).isInstanceOf(ImmutableList.class);
assertThat(conversionService.convert(integers, ImmutableSet.class)).isInstanceOf(ImmutableSet.class);
assertThat(conversionService.convert(integers, ImmutableBag.class)).isInstanceOf(ImmutableBag.class);
}
@Test // #1817
void convertsSetToEclipse() {
assertThat(conversionService.canConvert(Set.class, ImmutableSet.class)).isTrue();
assertThat(conversionService.canConvert(Set.class, ImmutableBag.class)).isTrue();
assertThat(conversionService.canConvert(Set.class, ImmutableList.class)).isTrue();
assertThat(conversionService.canConvert(Set.class, ImmutableMap.class)).isFalse();
var integers = Collections.singleton(1);
assertThat(conversionService.convert(integers, RichIterable.class)).isInstanceOf(MutableSet.class);
assertThat(conversionService.convert(integers, MutableList.class)).isInstanceOf(MutableList.class);
assertThat(conversionService.convert(integers, MutableSet.class)).isInstanceOf(MutableSet.class);
assertThat(conversionService.convert(integers, MutableBag.class)).isInstanceOf(MutableBag.class);
assertThat(conversionService.convert(integers, ImmutableList.class)).isInstanceOf(ImmutableList.class);
assertThat(conversionService.convert(integers, ImmutableSet.class)).isInstanceOf(ImmutableSet.class);
assertThat(conversionService.convert(integers, ImmutableBag.class)).isInstanceOf(ImmutableBag.class);
}
@Test // #1817
void convertsMapToEclipse() {
assertThat(conversionService.canConvert(Map.class, RichIterable.class)).isTrue();
assertThat(conversionService.canConvert(Map.class, MapIterable.class)).isTrue();
assertThat(conversionService.canConvert(Map.class, SetIterable.class)).isFalse();
assertThat(conversionService.canConvert(Map.class, ListIterable.class)).isFalse();
var map = Collections.singletonMap("key", "value");
assertThat(conversionService.convert(map, RichIterable.class)).isInstanceOf(MutableMap.class);
assertThat(conversionService.convert(map, ImmutableMap.class)).isInstanceOf(ImmutableMap.class);
// Required as MutableMap implements both Iterable and Map and for the standard
// Java compiler this creates an ambiguity
assertThat((Map<?, ?>) conversionService.convert(map, MutableMap.class)).isInstanceOf(MutableMap.class);
}
@Test // DATACMNS-1065, #1817
void unwrapsVavrCollectionsToJavaOnes() {
@@ -114,6 +199,18 @@ class CustomCollectionsUnitTests {
assertThat(unwrap(io.vavr.collection.LinkedHashMap.of("key", "value"))).isInstanceOf(Map.class);
}
@Test // #1817
void unwrapsEclipseCollectionsToJavaOnes() {
assertThat(unwrap(Lists.immutable.of(1, 2, 3))).isInstanceOf(List.class);
assertThat(unwrap(Sets.immutable.of(1, 2, 3))).isInstanceOf(Set.class);
assertThat(unwrap(Maps.immutable.of("key", "value"))).isInstanceOf(Map.class);
assertThat(unwrap(Lists.mutable.of(1, 2, 3))).isInstanceOf(List.class);
assertThat(unwrap(Sets.mutable.of(1, 2, 3))).isInstanceOf(Set.class);
assertThat(unwrap(Maps.mutable.of("key", "value"))).isInstanceOf(Map.class);
}
@Test // #1817
void rejectsInvalidMapType() {
assertThatIllegalArgumentException().isThrownBy(() -> CustomCollections.getMapBaseType(Object.class));
@@ -124,7 +221,15 @@ class CustomCollectionsUnitTests {
assertThat(CustomCollections.getPaginationReturnTypes()).contains(io.vavr.collection.Seq.class);
}
@Test // DATAJPA-1258. #1817
@TestFactory // #1817
Stream<DynamicTest> eclipseSupportedPaginationReturnTypes() {
return DynamicTest.stream(Stream.of(ImmutableList.class, MutableList.class),
it -> it.getSimpleName() + " is a pagination return type",
it -> assertThat(CustomCollections.getPaginationReturnTypes()).contains(it));
}
@Test // DATAJPA-1258, #1817
void convertsJavaListsToVavrSet() {
assertThat(conversionService.convert(Collections.singletonList("foo"), io.vavr.collection.Set.class)) //
.isInstanceOf(io.vavr.collection.Set.class);