DATACMNS-940 - Support for Javaslang collections as repository return types.
Javaslang's collection and map types can now be used on repository query methods and get adapted similarly to nullable wrapper types (like JDK's Optional). Also made TypeInformation infrastructure aware of the map type so that a value type lookup gets handle correctly.
This commit is contained in:
@@ -17,7 +17,11 @@ package org.springframework.data.repository.util;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.repository.util.QueryExecutionConverters.*;
|
||||
|
||||
import javaslang.collection.HashMap;
|
||||
import javaslang.collection.HashSet;
|
||||
import javaslang.collection.Traversable;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import rx.Completable;
|
||||
@@ -26,6 +30,11 @@ import rx.Single;
|
||||
import scala.Option;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
@@ -136,7 +145,7 @@ public class QueryExecutionConvertersUnitTests {
|
||||
public void turnsNullIntoGuavaOptional() {
|
||||
|
||||
Optional<Object> optional = conversionService.convert(new NullableWrapper(null), Optional.class);
|
||||
assertThat(optional, is(Optional.<Object>absent()));
|
||||
assertThat(optional, is(Optional.<Object> absent()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,7 +157,7 @@ public class QueryExecutionConvertersUnitTests {
|
||||
|
||||
java.util.Optional<Object> optional = conversionService.convert(new NullableWrapper(null),
|
||||
java.util.Optional.class);
|
||||
assertThat(optional, is(java.util.Optional.<Object>empty()));
|
||||
assertThat(optional, is(java.util.Optional.<Object> empty()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -205,7 +214,7 @@ public class QueryExecutionConvertersUnitTests {
|
||||
public void turnsNullIntoScalaOptionEmpty() {
|
||||
|
||||
assertThat((Option<Object>) conversionService.convert(new NullableWrapper(null), Option.class),
|
||||
is(Option.<Object>empty()));
|
||||
is(Option.<Object> empty()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -262,6 +271,71 @@ public class QueryExecutionConvertersUnitTests {
|
||||
assertThat(QueryExecutionConverters.unwrap(option("string")), is((Object) "string"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-940
|
||||
*/
|
||||
@Test
|
||||
public void conversListToJavaslang() {
|
||||
|
||||
assertThat(conversionService.canConvert(List.class, javaslang.collection.Traversable.class), is(true));
|
||||
assertThat(conversionService.canConvert(List.class, javaslang.collection.List.class), is(true));
|
||||
assertThat(conversionService.canConvert(List.class, javaslang.collection.Set.class), is(true));
|
||||
assertThat(conversionService.canConvert(List.class, javaslang.collection.Map.class), is(false));
|
||||
|
||||
List<Integer> integers = Arrays.asList(1, 2, 3);
|
||||
|
||||
Traversable<?> result = conversionService.convert(integers, Traversable.class);
|
||||
|
||||
assertThat(result, is(instanceOf(javaslang.collection.List.class)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-940
|
||||
*/
|
||||
@Test
|
||||
public void convertsSetToJavaslang() {
|
||||
|
||||
assertThat(conversionService.canConvert(Set.class, javaslang.collection.Traversable.class), is(true));
|
||||
assertThat(conversionService.canConvert(Set.class, javaslang.collection.Set.class), is(true));
|
||||
assertThat(conversionService.canConvert(Set.class, javaslang.collection.List.class), is(true));
|
||||
assertThat(conversionService.canConvert(Set.class, javaslang.collection.Map.class), is(false));
|
||||
|
||||
Set<Integer> integers = Collections.singleton(1);
|
||||
|
||||
Traversable<?> result = conversionService.convert(integers, Traversable.class);
|
||||
|
||||
assertThat(result, is(instanceOf(javaslang.collection.Set.class)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-940
|
||||
*/
|
||||
@Test
|
||||
public void convertsMapToJavaslang() {
|
||||
|
||||
assertThat(conversionService.canConvert(Map.class, javaslang.collection.Traversable.class), is(true));
|
||||
assertThat(conversionService.canConvert(Map.class, javaslang.collection.Map.class), is(true));
|
||||
assertThat(conversionService.canConvert(Map.class, javaslang.collection.Set.class), is(false));
|
||||
assertThat(conversionService.canConvert(Map.class, javaslang.collection.List.class), is(false));
|
||||
|
||||
Map<String, String> map = Collections.singletonMap("key", "value");
|
||||
|
||||
Traversable<?> result = conversionService.convert(map, Traversable.class);
|
||||
|
||||
assertThat(result, is(instanceOf(javaslang.collection.Map.class)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-940
|
||||
*/
|
||||
@Test
|
||||
public void unwrapsJavaslangCollectionsToJavaOnes() {
|
||||
|
||||
assertThat(unwrap(javaslangList(1, 2, 3)), is(instanceOf(List.class)));
|
||||
assertThat(unwrap(javaslangSet(1, 2, 3)), is(instanceOf(Set.class)));
|
||||
assertThat(unwrap(javaslangMap("key", "value")), is(instanceOf(Map.class)));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static javaslang.control.Option<Object> optionNone() {
|
||||
|
||||
@@ -275,4 +349,26 @@ public class QueryExecutionConvertersUnitTests {
|
||||
Method method = ReflectionUtils.findMethod(javaslang.control.Option.class, "of", Object.class);
|
||||
return (javaslang.control.Option<T>) ReflectionUtils.invokeMethod(method, null, source);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> javaslang.collection.List<T> javaslangList(T... values) {
|
||||
|
||||
Method method = ReflectionUtils.findMethod(javaslang.collection.List.class, "ofAll", Iterable.class);
|
||||
return (javaslang.collection.List<T>) ReflectionUtils.invokeMethod(method, null, Arrays.asList(values));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> javaslang.collection.Set<T> javaslangSet(T... values) {
|
||||
|
||||
Method method = ReflectionUtils.findMethod(HashSet.class, "ofAll", Iterable.class);
|
||||
return (javaslang.collection.Set<T>) ReflectionUtils.invokeMethod(method, null, Arrays.asList(values));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <K, V> javaslang.collection.Map<K, V> javaslangMap(K key, V value) {
|
||||
|
||||
Method method = ReflectionUtils.findMethod(HashMap.class, "ofAll", Map.class);
|
||||
return (javaslang.collection.Map<K, V>) ReflectionUtils.invokeMethod(method, null,
|
||||
Collections.singletonMap(key, value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.util.ClassTypeInformation.*;
|
||||
|
||||
import javaslang.collection.Traversable;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
@@ -106,7 +108,7 @@ public class ClassTypeInformationUnitTests {
|
||||
|
||||
property = information.getProperty("rawSet");
|
||||
assertEquals(Set.class, property.getType());
|
||||
assertThat(property.getComponentType().getType(), is(Matchers.<Class<?>>equalTo(Object.class)));
|
||||
assertThat(property.getComponentType().getType(), is(Matchers.<Class<?>> equalTo(Object.class)));
|
||||
assertNull(property.getMapValueType());
|
||||
}
|
||||
|
||||
@@ -413,6 +415,29 @@ public class ClassTypeInformationUnitTests {
|
||||
assertThat(information.getProperty("field").getType(), is(typeCompatibleWith(Nested.class)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-940
|
||||
*/
|
||||
@Test
|
||||
public void detectsJavaslangTraversableComponentType() {
|
||||
|
||||
ClassTypeInformation<SampleTraversable> information = ClassTypeInformation.from(SampleTraversable.class);
|
||||
|
||||
assertThat(information.getComponentType().getType(), is(typeCompatibleWith(Integer.class)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-940
|
||||
*/
|
||||
@Test
|
||||
public void detectsJavaslangMapComponentAndValueType() {
|
||||
|
||||
ClassTypeInformation<SampleMap> information = ClassTypeInformation.from(SampleMap.class);
|
||||
|
||||
assertThat(information.getComponentType().getType(), is(typeCompatibleWith(String.class)));
|
||||
assertThat(information.getMapValueType().getType(), is(typeCompatibleWith(Integer.class)));
|
||||
}
|
||||
|
||||
static class StringMapContainer extends MapContainer<String> {
|
||||
|
||||
}
|
||||
@@ -611,4 +636,8 @@ public class ClassTypeInformationUnitTests {
|
||||
static class Nested extends SomeType<String> {}
|
||||
|
||||
static class Concrete extends SomeType<Nested> {}
|
||||
|
||||
static interface SampleTraversable extends Traversable<Integer> {}
|
||||
|
||||
static interface SampleMap extends javaslang.collection.Map<String, Integer> {}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user