19
19
import scala .Option ;
20
20
import scala .runtime .AbstractFunction0 ;
21
21
22
+ import java .lang .reflect .Method ;
22
23
import java .util .Collections ;
23
24
import java .util .HashSet ;
24
25
import java .util .Set ;
25
26
import java .util .concurrent .CompletableFuture ;
26
27
import java .util .concurrent .Future ;
28
+ import java .util .function .Supplier ;
27
29
28
30
import org .springframework .core .convert .ConversionService ;
29
31
import org .springframework .core .convert .TypeDescriptor ;
33
35
import org .springframework .scheduling .annotation .AsyncResult ;
34
36
import org .springframework .util .Assert ;
35
37
import org .springframework .util .ClassUtils ;
38
+ import org .springframework .util .ReflectionUtils ;
36
39
import org .springframework .util .concurrent .ListenableFuture ;
37
40
38
41
import com .google .common .base .Optional ;
43
46
* <ul>
44
47
* <li>{@code java.util.Optional}</li>
45
48
* <li>{@code com.google.common.base.Optional}</li>
46
- * <li>{@code scala.Option}</li>
49
+ * <li>{@code scala.Option} - as of 1.12 </li>
47
50
* <li>{@code java.util.concurrent.Future}</li>
48
51
* <li>{@code java.util.concurrent.CompletableFuture}</li>
49
52
* <li>{@code org.springframework.util.concurrent.ListenableFuture<}</li>
53
+ * <li>{@code javaslang.control.Option} - as of 1.13</li>
50
54
* </ul>
51
55
*
52
56
* @author Oliver Gierke
@@ -65,6 +69,8 @@ public abstract class QueryExecutionConverters {
65
69
QueryExecutionConverters .class .getClassLoader ());
66
70
private static final boolean SCALA_PRESENT = ClassUtils .isPresent ("scala.Option" ,
67
71
QueryExecutionConverters .class .getClassLoader ());
72
+ private static final boolean JAVASLANG_PRESENT = ClassUtils .isPresent ("javaslang.control.Option" ,
73
+ QueryExecutionConverters .class .getClassLoader ());
68
74
69
75
private static final Set <Class <?>> WRAPPER_TYPES = new HashSet <Class <?>>();
70
76
private static final Set <Converter <Object , Object >> UNWRAPPERS = new HashSet <Converter <Object , Object >>();
@@ -92,6 +98,11 @@ public abstract class QueryExecutionConverters {
92
98
WRAPPER_TYPES .add (NullableWrapperToScalaOptionConverter .getWrapperType ());
93
99
UNWRAPPERS .add (ScalOptionUnwrapper .INSTANCE );
94
100
}
101
+
102
+ if (JAVASLANG_PRESENT ) {
103
+ WRAPPER_TYPES .add (NullableWrapperToJavaSlangOptionConverter .getWrapperType ());
104
+ UNWRAPPERS .add (JavaSlangOptionUnwrapper .INSTANCE );
105
+ }
95
106
}
96
107
97
108
private QueryExecutionConverters () {}
@@ -137,6 +148,10 @@ public static void registerConvertersIn(ConfigurableConversionService conversion
137
148
conversionService .addConverter (new NullableWrapperToScalaOptionConverter (conversionService ));
138
149
}
139
150
151
+ if (JAVASLANG_PRESENT ) {
152
+ conversionService .addConverter (new NullableWrapperToJavaSlangOptionConverter (conversionService ));
153
+ }
154
+
140
155
conversionService .addConverter (new NullableWrapperToFutureConverter (conversionService ));
141
156
}
142
157
@@ -375,6 +390,51 @@ public static Class<?> getWrapperType() {
375
390
}
376
391
}
377
392
393
+ /**
394
+ * Converter to convert from {@link NullableWrapper} into JavaSlang's {@link javaslang.control.Option}.
395
+ *
396
+ * @author Oliver Gierke
397
+ * @since 1.13
398
+ */
399
+ private static class NullableWrapperToJavaSlangOptionConverter extends AbstractWrapperTypeConverter {
400
+
401
+ private static final Method OF_METHOD ;
402
+ private static final Method NONE_METHOD ;
403
+
404
+ static {
405
+ OF_METHOD = ReflectionUtils .findMethod (getWrapperType (), "of" , Object .class );
406
+ NONE_METHOD = ReflectionUtils .findMethod (getWrapperType (), "none" );
407
+ }
408
+
409
+ /**
410
+ * Creates a new {@link NullableWrapperToJavaSlangOptionConverter} using the given {@link ConversionService}.
411
+ *
412
+ * @param conversionService must not be {@literal null}.
413
+ */
414
+ public NullableWrapperToJavaSlangOptionConverter (ConversionService conversionService ) {
415
+ super (conversionService , createEmptyOption (), getWrapperType ());
416
+ }
417
+
418
+ public static Class <?> getWrapperType () {
419
+ return javaslang .control .Option .class ;
420
+ }
421
+
422
+ /*
423
+ * (non-Javadoc)
424
+ * @see org.springframework.data.repository.util.QueryExecutionConverters.AbstractWrapperTypeConverter#wrap(java.lang.Object)
425
+ */
426
+ @ Override
427
+ @ SuppressWarnings ("unchecked" )
428
+ protected Object wrap (Object source ) {
429
+ return (javaslang .control .Option <Object >) ReflectionUtils .invokeMethod (OF_METHOD , null , source );
430
+ }
431
+
432
+ @ SuppressWarnings ("unchecked" )
433
+ private static javaslang .control .Option <Object > createEmptyOption () {
434
+ return (javaslang .control .Option <Object >) ReflectionUtils .invokeMethod (NONE_METHOD , null );
435
+ }
436
+ }
437
+
378
438
/**
379
439
* A {@link Converter} to unwrap Guava {@link Optional} instances.
380
440
*
@@ -447,4 +507,37 @@ public Object convert(Object source) {
447
507
return source instanceof Option ? ((Option <?>) source ).getOrElse (alternative ) : source ;
448
508
}
449
509
}
510
+
511
+ /**
512
+ * Converter to unwrap JavaSlang {@link javaslang.control.Option} instances.
513
+ *
514
+ * @author Oliver Gierke
515
+ * @since 1.13
516
+ */
517
+ private static enum JavaSlangOptionUnwrapper implements Converter <Object , Object > {
518
+
519
+ INSTANCE ;
520
+
521
+ private static final Supplier <Object > NULL_SUPPLIER = new Supplier <Object >() {
522
+
523
+ /*
524
+ * (non-Javadoc)
525
+ * @see java.util.function.Supplier#get()
526
+ */
527
+ public Object get () {
528
+ return null ;
529
+ }
530
+ };
531
+
532
+ /*
533
+ * (non-Javadoc)
534
+ * @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
535
+ */
536
+ @ Override
537
+ @ SuppressWarnings ("unchecked" )
538
+ public Object convert (Object source ) {
539
+ return source instanceof javaslang .control .Option
540
+ ? ((javaslang .control .Option <Object >) source ).getOrElse (NULL_SUPPLIER ) : source ;
541
+ }
542
+ }
450
543
}
0 commit comments