Skip to content

Commit 8a4656d

Browse files
committed
DATACMNS-1065 - Added support for Vavr (successor of Javaslang).
Basically duplicated the Javaslang support to now work on Vavr types as well.
1 parent e5ac3cb commit 8a4656d

File tree

6 files changed

+373
-4
lines changed

6 files changed

+373
-4
lines changed

pom.xml

+7
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
<properties>
1919
<dist.key>DATACMNS</dist.key>
2020
<javaslang>2.0.4</javaslang>
21+
<vavr>0.9.0</vavr>
2122
<scala>2.11.7</scala>
2223
<xmlbeam>1.4.8</xmlbeam>
2324
</properties>
@@ -150,6 +151,12 @@
150151
<version>${javaslang}</version>
151152
<optional>true</optional>
152153
</dependency>
154+
<dependency>
155+
<groupId>io.vavr</groupId>
156+
<artifactId>vavr</artifactId>
157+
<version>${vavr}</version>
158+
<optional>true</optional>
159+
</dependency>
153160

154161
<dependency>
155162
<groupId>javax.el</groupId>

src/main/java/org/springframework/data/repository/util/JavaslangCollections.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public Object convert(Object source) {
6666
return ((javaslang.collection.Set<?>) source).toJavaSet();
6767
}
6868

69-
throw new IllegalArgumentException("Unsupported Javaslang collection " + source);
69+
throw new IllegalArgumentException("Unsupported Javaslang collection " + source.getClass());
7070
}
7171
}
7272

src/main/java/org/springframework/data/repository/util/QueryExecutionConverters.java

+105-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
package org.springframework.data.repository.util;
1717

1818
import javaslang.collection.Seq;
19-
import javaslang.collection.Traversable;
2019
import lombok.AccessLevel;
2120
import lombok.RequiredArgsConstructor;
2221
import lombok.Value;
@@ -62,6 +61,7 @@
6261
* <li>{@code javaslang.control.Option} - as of 1.13</li>
6362
* <li>{@code javaslang.collection.Seq}, {@code javaslang.collection.Map}, {@code javaslang.collection.Set} - as of
6463
* 1.13</li>
64+
* <li>{@code io.vavr.collection.Seq}, {@code io.vavr.collection.Map}, {@code io.vavr.collection.Set} - as of 2.0</li>
6565
* </ul>
6666
*
6767
* @author Oliver Gierke
@@ -79,6 +79,8 @@ public abstract class QueryExecutionConverters {
7979
QueryExecutionConverters.class.getClassLoader());
8080
private static final boolean JAVASLANG_PRESENT = ClassUtils.isPresent("javaslang.control.Option",
8181
QueryExecutionConverters.class.getClassLoader());
82+
private static final boolean VAVR_PRESENT = ClassUtils.isPresent("io.vavr.control.Option",
83+
QueryExecutionConverters.class.getClassLoader());
8284

8385
private static final Set<WrapperType> WRAPPER_TYPES = new HashSet<WrapperType>();
8486
private static final Set<Converter<Object, Object>> UNWRAPPERS = new HashSet<Converter<Object, Object>>();
@@ -121,6 +123,16 @@ public abstract class QueryExecutionConverters {
121123

122124
ALLOWED_PAGEABLE_TYPES.add(Seq.class);
123125
}
126+
127+
if (VAVR_PRESENT) {
128+
129+
WRAPPER_TYPES.add(NullableWrapperToVavrOptionConverter.getWrapperType());
130+
WRAPPER_TYPES.add(VavrCollections.ToJavaConverter.INSTANCE.getWrapperType());
131+
132+
UNWRAPPERS.add(VavrOptionUnwrapper.INSTANCE);
133+
134+
ALLOWED_PAGEABLE_TYPES.add(io.vavr.collection.Seq.class);
135+
}
124136
}
125137

126138
private QueryExecutionConverters() {}
@@ -194,6 +206,11 @@ public static void registerConvertersIn(ConfigurableConversionService conversion
194206
conversionService.addConverter(JavaslangCollections.FromJavaConverter.INSTANCE);
195207
}
196208

209+
if (VAVR_PRESENT) {
210+
conversionService.addConverter(new NullableWrapperToVavrOptionConverter(conversionService));
211+
conversionService.addConverter(VavrCollections.FromJavaConverter.INSTANCE);
212+
}
213+
197214
conversionService.addConverter(new NullableWrapperToFutureConverter(conversionService));
198215
}
199216

@@ -468,7 +485,7 @@ public static WrapperType getWrapperType() {
468485
@Override
469486
@SuppressWarnings("unchecked")
470487
protected Object wrap(Object source) {
471-
return (javaslang.control.Option<Object>) ReflectionUtils.invokeMethod(OF_METHOD, null, source);
488+
return ReflectionUtils.invokeMethod(OF_METHOD, null, source);
472489
}
473490

474491
@SuppressWarnings("unchecked")
@@ -477,6 +494,50 @@ private static javaslang.control.Option<Object> createEmptyOption() {
477494
}
478495
}
479496

497+
/**
498+
* Converter to convert from {@link NullableWrapper} into JavaSlang's {@link io.vavr.control.Option}.
499+
*
500+
* @author Oliver Gierke
501+
* @since 2.0
502+
*/
503+
private static class NullableWrapperToVavrOptionConverter extends AbstractWrapperTypeConverter {
504+
505+
private static final Method OF_METHOD;
506+
private static final Method NONE_METHOD;
507+
508+
static {
509+
OF_METHOD = ReflectionUtils.findMethod(io.vavr.control.Option.class, "of", Object.class);
510+
NONE_METHOD = ReflectionUtils.findMethod(io.vavr.control.Option.class, "none");
511+
}
512+
513+
/**
514+
* Creates a new {@link NullableWrapperToJavaslangOptionConverter} using the given {@link ConversionService}.
515+
*
516+
* @param conversionService must not be {@literal null}.
517+
*/
518+
public NullableWrapperToVavrOptionConverter(ConversionService conversionService) {
519+
super(conversionService, createEmptyOption(), io.vavr.control.Option.class);
520+
}
521+
522+
/*
523+
* (non-Javadoc)
524+
* @see org.springframework.data.repository.util.QueryExecutionConverters.AbstractWrapperTypeConverter#wrap(java.lang.Object)
525+
*/
526+
@Override
527+
protected Object wrap(Object source) {
528+
return ReflectionUtils.invokeMethod(OF_METHOD, source);
529+
}
530+
531+
public static WrapperType getWrapperType() {
532+
return WrapperType.singleValue(io.vavr.control.Option.class);
533+
}
534+
535+
@SuppressWarnings("unchecked")
536+
private static io.vavr.control.Option<Object> createEmptyOption() {
537+
return (io.vavr.control.Option<Object>) ReflectionUtils.invokeMethod(NONE_METHOD, null);
538+
}
539+
}
540+
480541
/**
481542
* A {@link Converter} to unwrap Guava {@link Optional} instances.
482543
*
@@ -583,14 +644,55 @@ public Object convert(Object source) {
583644
return ((javaslang.control.Option<Object>) source).getOrElse(NULL_SUPPLIER);
584645
}
585646

586-
if (source instanceof Traversable) {
647+
if (source instanceof javaslang.collection.Traversable) {
587648
return JavaslangCollections.ToJavaConverter.INSTANCE.convert(source);
588649
}
589650

590651
return source;
591652
}
592653
}
593654

655+
/**
656+
* Converter to unwrap Vavr {@link io.vavr.control.Option} instances.
657+
*
658+
* @author Oliver Gierke
659+
* @since 2.0
660+
*/
661+
private static enum VavrOptionUnwrapper implements Converter<Object, Object> {
662+
663+
INSTANCE;
664+
665+
private static final Supplier<Object> NULL_SUPPLIER = new Supplier<Object>() {
666+
667+
/*
668+
* (non-Javadoc)
669+
* @see java.util.function.Supplier#get()
670+
*/
671+
public Object get() {
672+
return null;
673+
}
674+
};
675+
676+
/*
677+
* (non-Javadoc)
678+
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
679+
*/
680+
@Override
681+
@SuppressWarnings("unchecked")
682+
public Object convert(Object source) {
683+
684+
if (source instanceof io.vavr.control.Option) {
685+
return ((io.vavr.control.Option<Object>) source).getOrElse(NULL_SUPPLIER);
686+
}
687+
688+
if (source instanceof io.vavr.collection.Traversable) {
689+
return VavrCollections.ToJavaConverter.INSTANCE.convert(source);
690+
}
691+
692+
return source;
693+
}
694+
}
695+
594696
@Value
595697
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
596698
public static class WrapperType {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
* Copyright 2016-2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.repository.util;
17+
18+
import io.vavr.collection.Traversable;
19+
20+
import java.lang.reflect.Method;
21+
import java.util.Collection;
22+
import java.util.Collections;
23+
import java.util.HashSet;
24+
import java.util.List;
25+
import java.util.Map;
26+
import java.util.Set;
27+
28+
import org.springframework.core.convert.TypeDescriptor;
29+
import org.springframework.core.convert.converter.ConditionalGenericConverter;
30+
import org.springframework.core.convert.converter.Converter;
31+
import org.springframework.data.repository.util.QueryExecutionConverters.WrapperType;
32+
import org.springframework.util.ReflectionUtils;
33+
34+
/**
35+
* Converter implementations to map from and to Vavr collections.
36+
*
37+
* @author Oliver Gierke
38+
* @author Christoph Strobl
39+
* @since 1.13
40+
*/
41+
class VavrCollections {
42+
43+
public enum ToJavaConverter implements Converter<Object, Object> {
44+
45+
INSTANCE;
46+
47+
public WrapperType getWrapperType() {
48+
return WrapperType.multiValue(io.vavr.collection.Traversable.class);
49+
}
50+
51+
/*
52+
* (non-Javadoc)
53+
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
54+
*/
55+
@Override
56+
public Object convert(Object source) {
57+
58+
if (source instanceof io.vavr.collection.Seq) {
59+
return ((io.vavr.collection.Seq<?>) source).toJavaList();
60+
}
61+
62+
if (source instanceof io.vavr.collection.Map) {
63+
return ((io.vavr.collection.Map<?, ?>) source).toJavaMap();
64+
}
65+
66+
if (source instanceof io.vavr.collection.Set) {
67+
return ((io.vavr.collection.Set<?>) source).toJavaSet();
68+
}
69+
70+
throw new IllegalArgumentException("Unsupported Javaslang collection " + source.getClass());
71+
}
72+
}
73+
74+
public enum FromJavaConverter implements ConditionalGenericConverter {
75+
76+
INSTANCE {
77+
78+
/*
79+
* (non-Javadoc)
80+
* @see org.springframework.core.convert.converter.GenericConverter#getConvertibleTypes()
81+
*/
82+
@Override
83+
public java.util.Set<ConvertiblePair> getConvertibleTypes() {
84+
return CONVERTIBLE_PAIRS;
85+
}
86+
87+
/*
88+
* (non-Javadoc)
89+
* @see org.springframework.core.convert.converter.ConditionalConverter#matches(org.springframework.core.convert.TypeDescriptor, org.springframework.core.convert.TypeDescriptor)
90+
*/
91+
@Override
92+
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
93+
94+
// Prevent collections to be mapped to maps
95+
if (sourceType.isCollection() && io.vavr.collection.Map.class.isAssignableFrom(targetType.getType())) {
96+
return false;
97+
}
98+
99+
// Prevent maps to be mapped to collections
100+
if (sourceType.isMap() && !(io.vavr.collection.Map.class.isAssignableFrom(targetType.getType())
101+
|| targetType.getType().equals(Traversable.class))) {
102+
return false;
103+
}
104+
105+
return true;
106+
}
107+
108+
/*
109+
* (non-Javadoc)
110+
* @see org.springframework.core.convert.converter.GenericConverter#convert(java.lang.Object, org.springframework.core.convert.TypeDescriptor, org.springframework.core.convert.TypeDescriptor)
111+
*/
112+
@Override
113+
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
114+
115+
if (source instanceof List) {
116+
return ReflectionUtils.invokeMethod(LIST_FACTORY_METHOD, null, source);
117+
}
118+
119+
if (source instanceof java.util.Set) {
120+
return ReflectionUtils.invokeMethod(SET_FACTORY_METHOD, null, source);
121+
}
122+
123+
if (source instanceof java.util.Map) {
124+
return ReflectionUtils.invokeMethod(MAP_FACTORY_METHOD, null, source);
125+
}
126+
127+
return source;
128+
}
129+
};
130+
131+
private static final Set<ConvertiblePair> CONVERTIBLE_PAIRS;
132+
private static final Method LIST_FACTORY_METHOD;
133+
private static final Method SET_FACTORY_METHOD;
134+
private static final Method MAP_FACTORY_METHOD;
135+
136+
static {
137+
138+
Set<ConvertiblePair> pairs = new HashSet<ConvertiblePair>();
139+
pairs.add(new ConvertiblePair(Collection.class, io.vavr.collection.Traversable.class));
140+
pairs.add(new ConvertiblePair(Map.class, io.vavr.collection.Traversable.class));
141+
142+
CONVERTIBLE_PAIRS = Collections.unmodifiableSet(pairs);
143+
144+
MAP_FACTORY_METHOD = ReflectionUtils.findMethod(io.vavr.collection.LinkedHashMap.class, "ofAll", Map.class);
145+
LIST_FACTORY_METHOD = ReflectionUtils.findMethod(io.vavr.collection.List.class, "ofAll", Iterable.class);
146+
SET_FACTORY_METHOD = ReflectionUtils.findMethod(io.vavr.collection.LinkedHashSet.class, "ofAll", Iterable.class);
147+
}
148+
}
149+
}

0 commit comments

Comments
 (0)