Skip to content

Commit f39ace6

Browse files
committed
enum http parameters must be case-insensitive fix #592
1 parent d984d0e commit f39ace6

File tree

3 files changed

+74
-39
lines changed

3 files changed

+74
-39
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package org.jooby.issues;
2+
3+
import org.jooby.test.ServerFeature;
4+
import org.junit.Test;
5+
6+
public class Issue592 extends ServerFeature {
7+
8+
public static enum State {
9+
Draft, Submitted;
10+
}
11+
12+
{
13+
get("/592/:state", req -> req.param("state").toEnum(State.class));
14+
err((req, rsp, err) -> {
15+
rsp.send(err.getCause().getMessage());
16+
});
17+
}
18+
19+
@Test
20+
public void casInsensitiveEnum() throws Exception {
21+
request()
22+
.get("/592/draft")
23+
.expect("Draft");
24+
25+
request()
26+
.get("/592/SUBMITTED")
27+
.expect("Submitted");
28+
29+
request()
30+
.get("/592/missing")
31+
.expect("No enum constant org.jooby.issues.Issue592.State.missing");
32+
}
33+
}

jooby/src/main/java/org/jooby/Jooby.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2573,6 +2573,7 @@ private Injector bootstrap(final Config args,
25732573
xss(finalEnv);
25742574

25752575
/** dependency injection */
2576+
@SuppressWarnings("unchecked")
25762577
com.google.inject.Module joobyModule = binder -> {
25772578

25782579
/** type converters */
@@ -2729,7 +2730,7 @@ private Injector bootstrap(final Config args,
27292730
/** executors. */
27302731
executors.forEach(it -> it.accept(binder));
27312732
};
2732-
@SuppressWarnings("unchecked")
2733+
27332734
Injector injector = injectorFactory.apply(stage, joobyModule);
27342735

27352736
onStart.addAll(0, finalEnv.startTasks());

jooby/src/main/java/org/jooby/internal/BuiltinParser.java

Lines changed: 39 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.time.LocalDateTime;
2626
import java.time.ZoneOffset;
2727
import java.time.format.DateTimeParseException;
28+
import java.util.EnumSet;
2829
import java.util.List;
2930
import java.util.Map;
3031
import java.util.Optional;
@@ -47,39 +48,35 @@
4748
public enum BuiltinParser implements Parser {
4849

4950
Basic {
50-
private final Map<Class<?>, Function<String, Object>> parsers =
51-
ImmutableMap.<Class<?>, Function<String, Object>> builder()
52-
.put(BigDecimal.class, NOT_EMPTY.andThen(BigDecimal::new))
53-
.put(BigInteger.class, NOT_EMPTY.andThen(BigInteger::new))
54-
.put(Byte.class, NOT_EMPTY.andThen(Byte::valueOf))
55-
.put(byte.class, NOT_EMPTY.andThen(Byte::valueOf))
56-
.put(Double.class, NOT_EMPTY.andThen(Double::valueOf))
57-
.put(double.class, NOT_EMPTY.andThen(Double::valueOf))
58-
.put(Float.class, NOT_EMPTY.andThen(Float::valueOf))
59-
.put(float.class, NOT_EMPTY.andThen(Float::valueOf))
60-
.put(Integer.class, NOT_EMPTY.andThen(Integer::valueOf))
61-
.put(int.class, NOT_EMPTY.andThen(Integer::valueOf))
62-
.put(Long.class, NOT_EMPTY.andThen(this::toLong))
63-
.put(long.class, NOT_EMPTY.andThen(this::toLong))
64-
.put(Short.class, NOT_EMPTY.andThen(Short::valueOf))
65-
.put(short.class, NOT_EMPTY.andThen(Short::valueOf))
66-
.put(Boolean.class, NOT_EMPTY.andThen(this::toBoolean))
67-
.put(boolean.class, NOT_EMPTY.andThen(this::toBoolean))
68-
.put(Character.class, NOT_EMPTY.andThen(this::toCharacter))
69-
.put(char.class, NOT_EMPTY.andThen(this::toCharacter))
70-
.put(String.class, this::toString)
71-
.build();
51+
private final Map<Class<?>, Function<String, Object>> parsers = ImmutableMap
52+
.<Class<?>, Function<String, Object>> builder()
53+
.put(BigDecimal.class, NOT_EMPTY.andThen(BigDecimal::new))
54+
.put(BigInteger.class, NOT_EMPTY.andThen(BigInteger::new))
55+
.put(Byte.class, NOT_EMPTY.andThen(Byte::valueOf))
56+
.put(byte.class, NOT_EMPTY.andThen(Byte::valueOf))
57+
.put(Double.class, NOT_EMPTY.andThen(Double::valueOf))
58+
.put(double.class, NOT_EMPTY.andThen(Double::valueOf))
59+
.put(Float.class, NOT_EMPTY.andThen(Float::valueOf))
60+
.put(float.class, NOT_EMPTY.andThen(Float::valueOf))
61+
.put(Integer.class, NOT_EMPTY.andThen(Integer::valueOf))
62+
.put(int.class, NOT_EMPTY.andThen(Integer::valueOf))
63+
.put(Long.class, NOT_EMPTY.andThen(this::toLong))
64+
.put(long.class, NOT_EMPTY.andThen(this::toLong))
65+
.put(Short.class, NOT_EMPTY.andThen(Short::valueOf))
66+
.put(short.class, NOT_EMPTY.andThen(Short::valueOf))
67+
.put(Boolean.class, NOT_EMPTY.andThen(this::toBoolean))
68+
.put(boolean.class, NOT_EMPTY.andThen(this::toBoolean))
69+
.put(Character.class, NOT_EMPTY.andThen(this::toCharacter))
70+
.put(char.class, NOT_EMPTY.andThen(this::toCharacter))
71+
.put(String.class, this::toString)
72+
.build();
7273

7374
@Override
7475
public Object parse(final TypeLiteral<?> type, final Parser.Context ctx) throws Throwable {
7576
Function<String, Object> parser = parsers.get(type.getRawType());
7677
if (parser != null) {
7778
return ctx
78-
.param(values ->
79-
parser.apply(values.get(0))
80-
).body(body ->
81-
parser.apply(body.text())
82-
);
79+
.param(values -> parser.apply(values.get(0))).body(body -> parser.apply(body.text()));
8380
}
8481
return ctx.next();
8582
}
@@ -119,12 +116,11 @@ private Long toLong(final String value) {
119116
},
120117

121118
Collection {
122-
private final Map<Class<?>, Supplier<ImmutableCollection.Builder<?>>> parsers =
123-
ImmutableMap.<Class<?>, Supplier<ImmutableCollection.Builder<?>>> builder()
124-
.put(List.class, ImmutableList.Builder::new)
125-
.put(Set.class, ImmutableSet.Builder::new)
126-
.put(SortedSet.class, ImmutableSortedSet::naturalOrder)
127-
.build();
119+
private final Map<Class<?>, Supplier<ImmutableCollection.Builder<?>>> parsers = ImmutableMap.<Class<?>, Supplier<ImmutableCollection.Builder<?>>> builder()
120+
.put(List.class, ImmutableList.Builder::new)
121+
.put(Set.class, ImmutableSet.Builder::new)
122+
.put(SortedSet.class, ImmutableSortedSet::naturalOrder)
123+
.build();
128124

129125
private boolean matches(final TypeLiteral<?> toType) {
130126
return parsers.containsKey(toType.getRawType())
@@ -194,15 +190,20 @@ public Object parse(final TypeLiteral<?> type, final Parser.Context ctx)
194190
Class rawType = type.getRawType();
195191
if (Enum.class.isAssignableFrom(rawType)) {
196192
return ctx
197-
.param(values ->
198-
java.lang.Enum.valueOf(rawType, values.get(0).toUpperCase())
199-
).body(body ->
200-
java.lang.Enum.valueOf(rawType, body.text().toUpperCase())
201-
);
193+
.param(values -> toEnum(rawType, values.get(0)))
194+
.body(body -> toEnum(rawType, body.text()));
202195
} else {
203196
return ctx.next();
204197
}
205198
}
199+
200+
Object toEnum(final Class type, final String value) {
201+
Set<Enum> set = EnumSet.allOf(type);
202+
return set.stream()
203+
.filter(e -> e.name().equalsIgnoreCase(value))
204+
.findFirst()
205+
.orElseGet(() -> java.lang.Enum.valueOf(type, value));
206+
}
206207
},
207208

208209
Upload {

0 commit comments

Comments
 (0)