Skip to content

Commit b319128

Browse files
committed
Value API: normalize data parsing
- remove odd/harcoded edge cases - value converter now make a difference between simple and bean values
1 parent 2cf2875 commit b319128

19 files changed

Lines changed: 159 additions & 112 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package io.jooby;
2+
3+
public interface BeanConverter extends ValueConverter {
4+
}

jooby/src/main/java/io/jooby/DefaultContext.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ public interface DefaultContext extends Context {
303303
}
304304

305305
@Override default @Nullable <T> T convert(Value value, Class<T> type) {
306-
T result = ValueConverters.convert(value, type, getRouter().getConverters());
306+
T result = ValueConverters.convert(value, type, getRouter());
307307
if (result == null) {
308308
throw new TypeMismatchException(value.name(), type);
309309
}
@@ -313,7 +313,7 @@ public interface DefaultContext extends Context {
313313
@Override default @Nonnull <T> T decode(@Nonnull Type type, @Nonnull MediaType contentType) {
314314
try {
315315
if (MediaType.text.equals(contentType)) {
316-
T result = ValueConverters.convert(body(), type, getRouter().getConverters());
316+
T result = ValueConverters.convert(body(), type, getRouter());
317317
if (result != null) {
318318
return result;
319319
}

jooby/src/main/java/io/jooby/Jooby.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,10 @@ public Jooby errorCode(@Nonnull Class<? extends Throwable> type,
552552
return router.getConverters();
553553
}
554554

555+
@Nonnull @Override public List<BeanConverter> getBeanConverters() {
556+
return router.getBeanConverters();
557+
}
558+
555559
/**
556560
* Start application, find a web server, deploy application, start router, extension modules,
557561
* etc..

jooby/src/main/java/io/jooby/Router.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,8 @@ default Router error(@Nonnull Predicate<StatusCode> predicate,
680680

681681
@Nonnull List<ValueConverter> getConverters();
682682

683+
@Nonnull List<BeanConverter> getBeanConverters();
684+
683685
/**
684686
* Normalize a path by removing consecutives <code>/</code>(slashes), make it lower case and
685687
* removing trailing slash.

jooby/src/main/java/io/jooby/internal/HashValue.java

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@ private void put(String path, BiConsumer<String, Map<String, Value>> consumer) {
133133
}
134134

135135
private void useIndexes() {
136+
if (hash instanceof TreeMap) {
137+
return;
138+
}
136139
TreeMap<String, Value> ordered = new TreeMap<>();
137140
ordered.putAll(hash);
138141
hash.clear();
@@ -199,21 +202,31 @@ public int size() {
199202
}
200203

201204
@Nonnull @Override public <T> List<T> toList(@Nonnull Class<T> type) {
202-
Collection<Value> values = hash.values();
203-
List<T> result = new ArrayList<>(values.size());
204-
for (Value value : values) {
205-
result.add(value.to(type));
205+
if (hash instanceof TreeMap) {
206+
// indexes access, treat like a list
207+
Collection<Value> values = hash.values();
208+
List<T> result = new ArrayList<>(values.size());
209+
for (Value value : values) {
210+
result.add(value.to(type));
211+
}
212+
return result;
213+
} else {
214+
return Collections.singletonList(to(type));
206215
}
207-
return result;
208216
}
209217

210218
@Nonnull @Override public <T> Set<T> toSet(@Nonnull Class<T> type) {
211-
Collection<Value> values = hash.values();
212-
Set<T> result = new LinkedHashSet<>(values.size());
213-
for (Value value : values) {
214-
result.add(value.to(type));
219+
if (hash instanceof TreeMap) {
220+
// indexes access, treat like a list
221+
Collection<Value> values = hash.values();
222+
Set<T> result = new LinkedHashSet<>(values.size());
223+
for (Value value : values) {
224+
result.add(value.to(type));
225+
}
226+
return result;
227+
} else {
228+
return Collections.singleton(to(type));
215229
}
216-
return result;
217230
}
218231

219232
@Nonnull @Override public <T> Optional<T> toOptional(@Nonnull Class<T> type) {

jooby/src/main/java/io/jooby/internal/RouterImpl.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66
package io.jooby.internal;
77

8+
import io.jooby.BeanConverter;
89
import io.jooby.Context;
910
import io.jooby.RegistryException;
1011
import io.jooby.ServiceKey;
@@ -139,12 +140,15 @@ public Stack executor(Executor executor) {
139140

140141
private List<ValueConverter> converters;
141142

143+
private List<BeanConverter> beanConverters;
144+
142145
public RouterImpl(ClassLoader loader) {
143146
this.source = new ClassSource(loader);
144147
this.analyzer = new RouteAnalyzer(source, false);
145148
stack.addLast(new Stack(""));
146149

147150
converters = ValueConverters.defaultConverters();
151+
beanConverters = new ArrayList<>(3);
148152
}
149153

150154
@Nonnull @Override public Map<String, Object> getAttributes() {
@@ -310,14 +314,22 @@ public Router encoder(@Nonnull MediaType contentType, @Nonnull MessageEncoder en
310314
}
311315

312316
@Nonnull @Override public Router converter(ValueConverter converter) {
313-
converters.add(converter);
317+
if (converter instanceof BeanConverter) {
318+
beanConverters.add((BeanConverter) converter);
319+
} else {
320+
converters.add(converter);
321+
}
314322
return this;
315323
}
316324

317325
@Nonnull @Override public List<ValueConverter> getConverters() {
318326
return converters;
319327
}
320328

329+
@Nonnull @Override public List<BeanConverter> getBeanConverters() {
330+
return beanConverters;
331+
}
332+
321333
@Override
322334
public Route route(@Nonnull String method, @Nonnull String pattern,
323335
@Nonnull Route.Handler handler) {
@@ -394,6 +406,7 @@ private Route defineRoute(@Nonnull String method, @Nonnull String pattern,
394406

395407
// Must be last, as fallback
396408
ValueConverters.addFallbackConverters(converters);
409+
ValueConverters.addFallbackBeanConverters(beanConverters);
397410

398411
ExecutionMode mode = owner.getExecutionMode();
399412
for (Route route : routes) {

jooby/src/main/java/io/jooby/internal/ValueConverters.java

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package io.jooby.internal;
22

3+
import io.jooby.BeanConverter;
34
import io.jooby.FileUpload;
5+
import io.jooby.Router;
46
import io.jooby.TypeMismatchException;
57
import io.jooby.Value;
68
import io.jooby.internal.converter.BigDecimalConverter;
@@ -35,7 +37,6 @@ public class ValueConverters {
3537

3638
static List<ValueConverter> defaultConverters() {
3739
List<ValueConverter> result = new ArrayList<>();
38-
// result.add(new EnumConverter());
3940
result.add(new UUIDConverter());
4041

4142
result.add(new InstantConverter());
@@ -63,25 +64,28 @@ static List<ValueConverter> defaultConverters() {
6364

6465
static void addFallbackConverters(List<ValueConverter> converters) {
6566
converters.add(new ValueOfConverter());
67+
}
68+
69+
static void addFallbackBeanConverters(List<BeanConverter> converters) {
6670
converters.add(new ReflectiveBeanConverter());
6771
}
6872

69-
public static <T> T convert(Value value, Type type, List<ValueConverter> converters) {
73+
public static <T> T convert(Value value, Type type, Router router) {
7074
Class rawType = $Types.getRawType(type);
7175
if (List.class.isAssignableFrom(rawType)) {
7276
return (T) Collections
73-
.singletonList(convert(value, $Types.parameterizedType0(type), converters));
77+
.singletonList(convert(value, $Types.parameterizedType0(type), router));
7478
}
7579
if (Set.class.isAssignableFrom(rawType)) {
76-
return (T) Collections.singleton(convert(value, $Types.parameterizedType0(type), converters));
80+
return (T) Collections.singleton(convert(value, $Types.parameterizedType0(type), router));
7781
}
7882
if (Optional.class.isAssignableFrom(rawType)) {
79-
return (T) Optional.ofNullable(convert(value, $Types.parameterizedType0(type), converters));
83+
return (T) Optional.ofNullable(convert(value, $Types.parameterizedType0(type), router));
8084
}
81-
return convert(value, rawType, converters);
85+
return convert(value, rawType, router);
8286
}
8387

84-
public static <T> T convert(Value value, Class type, List<ValueConverter> converters) {
88+
public static <T> T convert(Value value, Class type, Router router) {
8589
if (type == String.class) {
8690
return (T) first(value).valueOrNull();
8791
}
@@ -123,23 +127,28 @@ public static <T> T convert(Value value, Class type, List<ValueConverter> conver
123127
return (T) (value.isMissing() ? null : Byte.valueOf(first(value).byteValue()));
124128
}
125129
/** File Upload: */
126-
if (Path.class == type) {
127-
if (value.isUpload()) {
130+
if (value.isUpload()) {
131+
if (Path.class == type) {
132+
128133
FileUpload upload = (FileUpload) value;
129134
return (T) upload.path();
130-
}
131-
throw new TypeMismatchException(value.name(), Path.class);
132-
}
133-
if (FileUpload.class == type) {
134-
if (value.isUpload()) {
135+
} else if (FileUpload.class == type) {
135136
return (T) value;
136137
}
137138
throw new TypeMismatchException(value.name(), FileUpload.class);
138139
}
139140

140-
for (ValueConverter converter : converters) {
141-
if (converter.supports(type)) {
142-
return (T) converter.convert(value, type);
141+
if (value.isSingle()) {
142+
for (ValueConverter converter : router.getConverters()) {
143+
if (converter.supports(type)) {
144+
return (T) converter.convert(value, type);
145+
}
146+
}
147+
} else if (value.isObject()) {
148+
for (BeanConverter converter : router.getBeanConverters()) {
149+
if (converter.supports(type)) {
150+
return (T) converter.convert(value, type);
151+
}
143152
}
144153
}
145154
return null;
@@ -162,15 +171,15 @@ private static Object enumValue(Value value, Class type) {
162171
}
163172

164173
private static Value first(Value source) {
165-
if (source.isSingle()) {
166-
return source;
167-
}
168-
if (source.isArray()) {
169-
return source.get(0);
170-
}
171-
if (source.isObject() && source.size() > 0) {
172-
return source.iterator().next();
173-
}
174+
// if (source.isSingle()) {
175+
// return source;
176+
// }
177+
// if (source.isArray()) {
178+
// return source.get(0);
179+
// }
180+
// if (source.isObject() && source.size() > 0) {
181+
// return source.iterator().next();
182+
// }
174183
return source;
175184
}
176185
}

jooby/src/main/java/io/jooby/internal/converter/EnumConverter.java

Lines changed: 0 additions & 34 deletions
This file was deleted.

jooby/src/main/java/io/jooby/internal/converter/ReflectiveBeanConverter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package io.jooby.internal.converter;
22

33
import io.jooby.BadRequestException;
4+
import io.jooby.BeanConverter;
45
import io.jooby.MissingValueException;
56
import io.jooby.ProvisioningException;
67
import io.jooby.Value;
7-
import io.jooby.ValueConverter;
88
import io.jooby.internal.reflect.$Types;
99

1010
import javax.annotation.Nonnull;
@@ -26,7 +26,7 @@
2626

2727
import static io.jooby.SneakyThrows.propagate;
2828

29-
public class ReflectiveBeanConverter implements ValueConverter {
29+
public class ReflectiveBeanConverter implements BeanConverter {
3030
private static final String AMBIGUOUS_CONSTRUCTOR =
3131
"Ambiguous constructor found. Expecting a single constructor or only one annotated with "
3232
+ Inject.class.getName();

jooby/src/test/java/io/jooby/ValueToBeanTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ public void valueOf() {
428428
});
429429

430430
queryString("id=userId", queryString -> {
431-
assertEquals("valueOf:userId", queryString.to(UserId.class).toString());
431+
assertEquals("userId", queryString.to(UserId.class).toString());
432432
});
433433
queryString("id=userId", queryString -> {
434434
assertEquals("valueOf:userId", queryString.get("id").to(UserId.class).toString());

0 commit comments

Comments
 (0)