Skip to content

Commit ebc2671

Browse files
committed
QueryString: improve how query string parsing and bean materialization works
- Reflective bean converter doesn't create an object when nothing matches - Work better for empty list - allow to extract partial matching list from bigger input - Added Value.toNullable - Added ConstructorStringValueConverter - See #2525 #2525 #2325
1 parent 52caf91 commit ebc2671

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+719
-118
lines changed

jooby/src/main/java/io/jooby/BeanConverter.java

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@
55
*/
66
package io.jooby;
77

8-
import java.util.List;
9-
108
import edu.umd.cs.findbugs.annotations.NonNull;
11-
import io.jooby.internal.converter.ReflectiveBeanConverter;
129

1310
/**
1411
* Value converter for complex values that come from query, path, form, etc... parameters into more
@@ -17,6 +14,7 @@
1714
* <p>It is an extension point for {@link ValueNode#to(Class)} calls.
1815
*/
1916
public interface BeanConverter {
17+
2018
/**
2119
* True if the converter applies for the given type.
2220
*
@@ -33,17 +31,4 @@ public interface BeanConverter {
3331
* @return Converted value.
3432
*/
3533
Object convert(@NonNull ValueNode node, @NonNull Class type);
36-
37-
/**
38-
* Creates a bean converter that uses reflection.
39-
*
40-
* @return Reflection bean converter.
41-
*/
42-
static BeanConverter reflective() {
43-
return new ReflectiveBeanConverter();
44-
}
45-
46-
static void addFallbackConverters(List<BeanConverter> input) {
47-
input.add(reflective());
48-
}
4934
}

jooby/src/main/java/io/jooby/Body.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import java.util.Set;
1616

1717
import edu.umd.cs.findbugs.annotations.NonNull;
18+
import edu.umd.cs.findbugs.annotations.Nullable;
1819
import io.jooby.exception.MissingValueException;
1920
import io.jooby.internal.ByteArrayBody;
2021
import io.jooby.internal.FileBody;
@@ -99,6 +100,10 @@ default <T> List<T> toList(@NonNull Class<T> type) {
99100
return to((Type) type);
100101
}
101102

103+
default @Nullable @Override <T> T toNullable(@NonNull Class<T> type) {
104+
return toNullable((Type) type);
105+
}
106+
102107
/**
103108
* Convert this body into the given type.
104109
*
@@ -108,6 +113,15 @@ default <T> List<T> toList(@NonNull Class<T> type) {
108113
*/
109114
@NonNull <T> T to(@NonNull Type type);
110115

116+
/**
117+
* Convert this body into the given type.
118+
*
119+
* @param type Type to use.
120+
* @param <T> Generic type.
121+
* @return Converted value or <code>null</code>.
122+
*/
123+
@Nullable <T> T toNullable(@NonNull Type type);
124+
111125
/* **********************************************************************************************
112126
* Factory methods:
113127
* **********************************************************************************************

jooby/src/main/java/io/jooby/Context.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
import edu.umd.cs.findbugs.annotations.NonNull;
3232
import edu.umd.cs.findbugs.annotations.Nullable;
33+
import io.jooby.exception.TypeMismatchException;
3334
import io.jooby.internal.LocaleUtils;
3435
import io.jooby.internal.ParamLookupImpl;
3536
import io.jooby.internal.ReadOnlyContext;
@@ -118,7 +119,23 @@ public interface Context extends Registry {
118119
* @param <T> Generic type.
119120
* @return Converted value.
120121
*/
121-
@NonNull <T> T convert(@NonNull ValueNode value, @NonNull Class<T> type);
122+
default @NonNull <T> T convert(@NonNull ValueNode value, @NonNull Class<T> type) {
123+
T result = convertOrNull(value, type);
124+
if (result == null) {
125+
throw new TypeMismatchException(value.name(), type);
126+
}
127+
return result;
128+
}
129+
130+
/**
131+
* Converts a value (single or hash) into the given type.
132+
*
133+
* @param value Value to convert.
134+
* @param type Expected type.
135+
* @param <T> Generic type.
136+
* @return Converted value or <code>null</code>.
137+
*/
138+
@Nullable <T> T convertOrNull(@NonNull ValueNode value, @NonNull Class<T> type);
122139

123140
/*
124141
* **********************************************************************************************

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

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import edu.umd.cs.findbugs.annotations.NonNull;
3333
import edu.umd.cs.findbugs.annotations.Nullable;
3434
import io.jooby.exception.RegistryException;
35-
import io.jooby.exception.TypeMismatchException;
3635
import io.jooby.internal.HashValue;
3736
import io.jooby.internal.MissingValue;
3837
import io.jooby.internal.SingleValue;
@@ -406,12 +405,8 @@ default boolean isSecure() {
406405
}
407406

408407
@Override
409-
default @NonNull <T> T convert(@NonNull ValueNode value, @NonNull Class<T> type) {
410-
T result = ValueConverters.convert(value, type, getRouter());
411-
if (result == null) {
412-
throw new TypeMismatchException(value.name(), type);
413-
}
414-
return result;
408+
default @NonNull <T> T convertOrNull(@NonNull ValueNode value, @NonNull Class<T> type) {
409+
return ValueConverters.convert(value, type, getRouter());
415410
}
416411

417412
@Override

jooby/src/main/java/io/jooby/ForwardingContext.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,11 @@ public <T> T convert(@NonNull ValueNode value, @NonNull Class<T> type) {
386386
return ctx.convert(value, type);
387387
}
388388

389+
@Nullable @Override
390+
public <T> T convertOrNull(@NonNull ValueNode value, @NonNull Class<T> type) {
391+
return ctx.convertOrNull(value, type);
392+
}
393+
389394
@NonNull @Override
390395
public <T> T decode(@NonNull Type type, @NonNull MediaType contentType) {
391396
return ctx.decode(type, contentType);

jooby/src/main/java/io/jooby/Value.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,16 @@ default boolean isObject() {
405405
*/
406406
@NonNull <T> T to(@NonNull Class<T> type);
407407

408+
/**
409+
* Convert this value to the given type. Support values are single-value, array-value and
410+
* object-value. Object-value can be converted to a JavaBean type.
411+
*
412+
* @param type Type to convert.
413+
* @param <T> Element type.
414+
* @return Instance of the type or <code>null</code>.
415+
*/
416+
@Nullable <T> T toNullable(@NonNull Class<T> type);
417+
408418
/**
409419
* Value as multi-value map.
410420
*

jooby/src/main/java/io/jooby/ValueConverter.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import edu.umd.cs.findbugs.annotations.NonNull;
1111
import io.jooby.internal.ValueConverters;
12+
import io.jooby.internal.converter.StringConstructorConverter;
1213
import io.jooby.internal.converter.ValueOfConverter;
1314

1415
/**
@@ -41,5 +42,6 @@ static List<ValueConverter> defaults() {
4142

4243
static void addFallbackConverters(List<ValueConverter> input) {
4344
input.add(new ValueOfConverter());
45+
input.add(new StringConstructorConverter());
4446
}
4547
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import java.util.Set;
1717

1818
import edu.umd.cs.findbugs.annotations.NonNull;
19+
import edu.umd.cs.findbugs.annotations.Nullable;
1920
import io.jooby.Context;
2021
import io.jooby.ValueNode;
2122
import io.jooby.exception.MissingValueException;
@@ -94,6 +95,11 @@ public <T> T to(@NonNull Class<T> type) {
9495
return ctx.convert(list.get(0), type);
9596
}
9697

98+
@Nullable @Override
99+
public <T> T toNullable(@NonNull Class<T> type) {
100+
return list.isEmpty() ? null : ctx.convertOrNull(list.get(0), type);
101+
}
102+
97103
@NonNull @Override
98104
public <T> List<T> toList(@NonNull Class<T> type) {
99105
return collect(new ArrayList<>(this.list.size()), type);

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import java.util.Map;
1717

1818
import edu.umd.cs.findbugs.annotations.NonNull;
19+
import edu.umd.cs.findbugs.annotations.Nullable;
1920
import io.jooby.Body;
2021
import io.jooby.Context;
2122
import io.jooby.MediaType;
@@ -88,6 +89,11 @@ public <T> T to(@NonNull Type type) {
8889
return ctx.decode(type, ctx.getRequestType(MediaType.text));
8990
}
9091

92+
@Nullable @Override
93+
public <T> T toNullable(@NonNull Type type) {
94+
return bytes.length == 0 ? null : ctx.decode(type, ctx.getRequestType(MediaType.text));
95+
}
96+
9197
@Override
9298
public Map<String, List<String>> toMultimap() {
9399
return Collections.emptyMap();

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import java.util.Map;
1818

1919
import edu.umd.cs.findbugs.annotations.NonNull;
20+
import edu.umd.cs.findbugs.annotations.Nullable;
2021
import io.jooby.Body;
2122
import io.jooby.Context;
2223
import io.jooby.MediaType;
@@ -103,6 +104,11 @@ public <T> T to(@NonNull Type type) {
103104
return ctx.decode(type, ctx.getRequestType(MediaType.text));
104105
}
105106

107+
@Nullable @Override
108+
public <T> T toNullable(@NonNull Type type) {
109+
return ctx.decode(type, ctx.getRequestType(MediaType.text));
110+
}
111+
106112
@Override
107113
public Map<String, List<String>> toMultimap() {
108114
return Collections.emptyMap();

0 commit comments

Comments
 (0)