11package io .jooby .spi ;
22
3+ import java .util .ArrayList ;
4+ import java .util .List ;
35import java .util .ServiceLoader ;
4- import java .util .concurrent .CopyOnWriteArrayList ;
56
67import javax .annotation .Nullable ;
78
1920public final class ValueConverters {
2021
2122 // Allow thread safe adding of ValueConverters.
22- private final CopyOnWriteArrayList <ValueConverter > valueConverters ;
23+ private final Iterable <ValueConverter > valueConverters ;
2324
2425 // Initialization on demand
2526 private static final class Hidden {
2627
27- private static final ValueConverters INSTANCE = ValueConverters .create ().fromServiceLoader ();
28+ private static volatile ValueConverters instance = ValueConverters .builder ().fromServiceLoader (). build ();
2829 }
2930
30- private ValueConverters (CopyOnWriteArrayList <ValueConverter > valueConverters ) {
31+ private ValueConverters (Iterable <ValueConverter > valueConverters ) {
3132 super ();
3233 this .valueConverters = valueConverters ;
3334 }
3435
35- static ValueConverters create () {
36- return new ValueConverters (new CopyOnWriteArrayList <>());
36+ static Builder builder () {
37+ return new Builder ();
38+ }
39+
40+ static final class Builder {
41+
42+ private final List <ValueConverter > valueConverters = new ArrayList <>();
43+
44+ Builder fromServiceLoader () {
45+ ServiceLoader <ValueConverter > sl = ServiceLoader .load (ValueConverter .class );
46+ // If any failes to load we will fail entirely.
47+ // The value converters found earlier in the classpath take precedence.
48+ sl .forEach (this ::add );
49+ return this ;
50+ }
51+
52+ /**
53+ * You can add value converters programmatic. For now its protected. Its
54+ * also to aid unit testing since serviceloader is inherently static
55+ * singleton.
56+ *
57+ * @param vc
58+ * @return
59+ */
60+ Builder add (ValueConverter vc ) {
61+ valueConverters .add (vc );
62+ return this ;
63+ }
64+
65+ Builder clear () {
66+ valueConverters .clear ();
67+ return this ;
68+ }
69+
70+ ValueConverters build () {
71+ return new ValueConverters (valueConverters );
72+ }
73+
74+ ValueConverters set () {
75+ ValueConverters vc = build ();
76+ Hidden .instance = vc ;
77+ return vc ;
78+ }
3779 }
3880
3981 /**
@@ -44,7 +86,8 @@ static ValueConverters create() {
4486 * @param c
4587 * desired type
4688 * @return the type if converted or null if conversion was not possible.
47- * @throws TypeMismatchException failure in a converter
89+ * @throws TypeMismatchException
90+ * failure in a converter
4891 */
4992 public @ Nullable Object convert (ValueContainer v , Class <?> c ) throws TypeMismatchException {
5093 Object result = null ;
@@ -59,37 +102,13 @@ static ValueConverters create() {
59102 return result ;
60103 }
61104
62- ValueConverters fromServiceLoader () {
63- ServiceLoader <ValueConverter > sl = ServiceLoader .load (ValueConverter .class );
64- // If any failes to load we will fail entirely.
65- // The value converters found earlier in the classpath take precedence.
66- sl .forEach (this ::add );
67- return this ;
68- }
69-
70- /**
71- * You can add value converters programmatic. For now its protected. Its also
72- * to aid unit testing since serviceloader is inherently static singleton.
73- *
74- * @param vc
75- * @return
76- */
77- /* private */ ValueConverters add (ValueConverter vc ) {
78- valueConverters .add (vc );
79- return this ;
80- }
81-
82- ValueConverters clear () {
83- valueConverters .clear ();
84- return this ;
85- }
86-
87105 /**
88106 * The ValueConverters singleton usually preloaded by the ServiceLoader.
107+ *
89108 * @return the shared singleton used by Jooby
90109 */
91110 public static ValueConverters getInstance () {
92- return ValueConverters .Hidden .INSTANCE ;
111+ return ValueConverters .Hidden .instance ;
93112 }
94113
95114}
0 commit comments