Skip to content

Commit 31d8866

Browse files
committed
route attribute value: allows primitives, enum, class and string types fix #347
1 parent 8cfa97f commit 31d8866

11 files changed

Lines changed: 113 additions & 59 deletions

File tree

jooby-sitemap/src/main/java/org/jooby/sitemap/WebPageProvider.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,14 @@ public interface WebPageProvider {
3232
WebPageProvider SITEMAP = route -> {
3333
WebPage page = new WebPage();
3434
page.setName(route.pattern());
35-
route.attr("changefreq")
36-
.ifPresent(v -> page.setChangeFreq(ChangeFreq.valueOf(v.toUpperCase())));
37-
route.attr("priority")
38-
.ifPresent(v -> page.setPriority(Double.parseDouble(v)));
35+
ChangeFreq freq = route.attr("changefreq");
36+
if (freq != null) {
37+
page.setChangeFreq(freq);
38+
}
39+
Double priority = route.attr("priority");
40+
if (priority != null) {
41+
page.setPriority(priority);
42+
}
3943
return ImmutableList.of(page);
4044
};
4145

jooby-sitemap/src/test/java/org/jooby/internal/sitemap/WebPageProviderTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public void defsitemap() {
2727
public void sitemapWithFreq() {
2828
WebPageProvider sitemap = WebPageProvider.SITEMAP;
2929
List<WebPage> pages = sitemap
30-
.apply(new Route.Definition("get", "/path", () -> "").attr("changefreq", "always"));
30+
.apply(new Route.Definition("get", "/path", () -> "").attr("changefreq", ChangeFreq.ALWAYS));
3131
assertEquals(1, pages.size());
3232
assertEquals("/path", pages.get(0).getName());
3333
assertEquals(ChangeFreq.ALWAYS, pages.get(0).getChangeFreq());
@@ -39,8 +39,8 @@ public void sitemapWithPriority() {
3939
WebPageProvider sitemap = WebPageProvider.SITEMAP;
4040
List<WebPage> pages = sitemap
4141
.apply(new Route.Definition("get", "/path", () -> "")
42-
.attr("changefreq", "always")
43-
.attr("priority", "1.0"));
42+
.attr("changefreq", ChangeFreq.ALWAYS)
43+
.attr("priority", 1d));
4444
assertEquals(1, pages.size());
4545
assertEquals("/path", pages.get(0).getName());
4646
assertEquals(ChangeFreq.ALWAYS, pages.get(0).getChangeFreq());

jooby/src/main/java/org/jooby/Route.java

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@
2020

2121
import static com.google.common.base.Preconditions.checkArgument;
2222
import static java.util.Objects.requireNonNull;
23+
import static javaslang.API.Case;
24+
import static javaslang.API.Match;
25+
import static javaslang.Predicates.instanceOf;
2326

27+
import java.lang.reflect.Array;
2428
import java.lang.reflect.Method;
2529
import java.util.ArrayList;
2630
import java.util.Arrays;
@@ -42,6 +46,7 @@
4246
import com.google.common.collect.ImmutableList;
4347
import com.google.common.collect.ImmutableMap;
4448
import com.google.common.collect.Lists;
49+
import com.google.common.primitives.Primitives;
4550
import com.google.inject.Key;
4651
import com.google.inject.TypeLiteral;
4752

@@ -218,13 +223,14 @@ public interface Route {
218223
*/
219224
interface Attributes<T extends Attributes<T>> {
220225
/**
221-
* Set route attribute.
226+
* Set route attribute. Only primitives, string, class, enum or array of previous types are
227+
* allowed as attributes values.
222228
*
223229
* @param name Attribute's name.
224230
* @param value Attribute's value.
225231
* @return This instance.
226232
*/
227-
T attr(String name, String value);
233+
T attr(String name, Object value);
228234

229235
/**
230236
* Tell jooby what renderer should use to render the output.
@@ -653,7 +659,7 @@ public Group produces(final List<MediaType> types) {
653659
}
654660

655661
@Override
656-
public Group attr(final String name, final String value) {
662+
public Group attr(final String name, final Object value) {
657663
for (Definition definition : routes) {
658664
definition.attr(name, value);
659665
}
@@ -742,7 +748,7 @@ public Collection produces(final List<MediaType> types) {
742748
}
743749

744750
@Override
745-
public Collection attr(final String name, final String value) {
751+
public Collection attr(final String name, final Object value) {
746752
for (Definition definition : routes) {
747753
definition.attr(name, value);
748754
}
@@ -858,7 +864,7 @@ class Definition implements Attributes<Definition> {
858864

859865
private List<RoutePattern> excludes = Collections.emptyList();
860866

861-
private Map<String, String> attributes = new HashMap<>();
867+
private Map<String, Object> attributes = new HashMap<>();
862868

863869
/**
864870
* Creates a new route definition.
@@ -980,35 +986,41 @@ public String reverse(final Object... values) {
980986
return cpattern.reverse(values);
981987
}
982988

983-
/**
984-
* Set route attribute.
985-
*
986-
* @param name Attribute's name.
987-
* @param value Attribute's value.
988-
* @return This instance.
989-
*/
990989
@Override
991-
public Definition attr(final String name, final String value) {
992-
requireNonNull(name, "A name is required.");
993-
requireNonNull(value, "A value is required.");
990+
public Definition attr(final String name, final Object value) {
991+
requireNonNull(name, "Attribute name is required.");
992+
requireNonNull(value, "Attribute value is required.");
993+
994+
validate(value);
994995
attributes.put(name, value);
995996
return this;
996997
}
997998

999+
private boolean validate(final Object value) {
1000+
return Match(value).option(
1001+
Case(v -> Primitives.isWrapperType(Primitives.wrap(v.getClass())), true),
1002+
Case(instanceOf(String.class), true),
1003+
Case(instanceOf(Enum.class), true),
1004+
Case(instanceOf(Class.class), true),
1005+
Case(c -> c.getClass().isArray(), v -> validate(Array.get(v, 0))))
1006+
.getOrElseThrow(() -> new IllegalArgumentException("Unsupported attribute: " + value));
1007+
}
1008+
9981009
/**
9991010
* Get an attribute by name.
10001011
*
10011012
* @param name Attribute's name.
1002-
* @return Attribute's value.
1013+
* @return Attribute's value or <code>null</code>.
10031014
*/
1004-
public Optional<String> attr(final String name) {
1005-
return Optional.ofNullable(attributes.get(name));
1015+
@SuppressWarnings("unchecked")
1016+
public <T> T attr(final String name) {
1017+
return (T) attributes.get(name);
10061018
}
10071019

10081020
/**
10091021
* @return A read only view of attributes.
10101022
*/
1011-
public Map<String, String> attributes() {
1023+
public Map<String, Object> attributes() {
10121024
return ImmutableMap.copyOf(attributes);
10131025
}
10141026

@@ -1281,12 +1293,12 @@ public List<MediaType> produces() {
12811293
}
12821294

12831295
@Override
1284-
public Map<String, String> attributes() {
1296+
public Map<String, Object> attributes() {
12851297
return route.attributes();
12861298
}
12871299

12881300
@Override
1289-
public String attr(final String name) {
1301+
public <T> T attr(final String name) {
12901302
return route.attr(name);
12911303
}
12921304

@@ -1918,16 +1930,17 @@ default boolean apply(final String prefix) {
19181930
/**
19191931
* @return All the available attributes in the execution chain.
19201932
*/
1921-
Map<String, String> attributes();
1933+
Map<String, Object> attributes();
19221934

19231935
/**
19241936
* Attribute by name.
19251937
*
19261938
* @param name Attribute's name.
19271939
* @return Attribute value.
19281940
*/
1929-
default String attr(final String name) {
1930-
return attributes().get(name);
1941+
@SuppressWarnings("unchecked")
1942+
default <T> T attr(final String name) {
1943+
return (T) attributes().get(name);
19311944
}
19321945

19331946
/**

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public RouteChain(final RequestImpl req, final ResponseImpl rsp, final List<Rout
4747
this.rrsp = rsp;
4848

4949
// eager decision if we need to wrap a route to get all the attrs within the change.
50-
ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
50+
ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
5151
routes.forEach(r -> builder.putAll(r.attributes()));
5252
this.hasAttrs = builder.build().size() > 0;
5353
}
@@ -86,14 +86,14 @@ private RouteImpl get(final Route next) {
8686
}
8787

8888
private static Route attrs(final Route route, final List<Route> routes, final int i) {
89-
ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
89+
ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
9090
for (int t = i; t < routes.size(); t++) {
9191
builder.putAll(routes.get(t).attributes());
9292
}
93-
Map<String, String> attrs = builder.build();
93+
Map<String, Object> attrs = builder.build();
9494
return new Route.Forwarding(route) {
9595
@Override
96-
public Map<String, String> attributes() {
96+
public Map<String, Object> attributes() {
9797
return attrs;
9898
}
9999
};

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public class RouteImpl implements Route, Route.Filter {
3434

3535
private static Map<Object, String> NO_VARS = ImmutableMap.of();
3636

37-
private static ImmutableMap<String, String> NO_ATTRS = ImmutableMap.of();
37+
private static ImmutableMap<String, Object> NO_ATTRS = ImmutableMap.of();
3838

3939
private String method;
4040

@@ -52,7 +52,7 @@ public class RouteImpl implements Route, Route.Filter {
5252

5353
private Filter filter;
5454

55-
private Map<String, String> attributes;
55+
private Map<String, Object> attributes;
5656

5757
public static RouteImpl notFound(final String method, final String path,
5858
final List<MediaType> produces) {
@@ -77,15 +77,15 @@ public boolean apply(final String filter) {
7777
public RouteImpl(final Filter filter, final String method, final String path,
7878
final String pattern, final String name, final Map<Object, String> vars,
7979
final List<MediaType> consumes, final List<MediaType> produces,
80-
final Map<String, String> attributes) {
80+
final Map<String, Object> attributes) {
8181
this(filter, method, path, pattern, name, vars, consumes, produces,
82-
ImmutableMap.<String, String> copyOf(attributes));
82+
ImmutableMap.<String, Object> copyOf(attributes));
8383
}
8484

8585
public RouteImpl(final Filter filter, final String method, final String path,
8686
final String pattern, final String name, final Map<Object, String> vars,
8787
final List<MediaType> consumes, final List<MediaType> produces,
88-
final ImmutableMap<String, String> attributes) {
88+
final ImmutableMap<String, Object> attributes) {
8989
this.filter = filter;
9090
this.method = method;
9191
this.path = path;
@@ -104,7 +104,7 @@ public void handle(final Request request, final Response response, final Chain c
104104
}
105105

106106
@Override
107-
public Map<String, String> attributes() {
107+
public Map<String, Object> attributes() {
108108
return attributes;
109109
}
110110

jooby/src/main/java/org/jooby/internal/mvc/MvcRoutes.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public static List<Route.Definition> routes(final Env env, final RouteMetadata c
9999
}
100100

101101
List<Definition> definitions = new ArrayList<>();
102-
Map<String, String> attrs = attrs(routeClass.getAnnotations());
102+
Map<String, Object> attrs = attrs(routeClass.getAnnotations());
103103
methods
104104
.keySet()
105105
.stream()
@@ -121,7 +121,7 @@ public static List<Route.Definition> routes(final Env env, final RouteMetadata c
121121
List<Class<?>> verbs = methods.get(method);
122122
List<MediaType> produces = produces(method);
123123
List<MediaType> consumes = consumes(method);
124-
Map<String, String> localAttrs = new HashMap<>(attrs);
124+
Map<String, Object> localAttrs = new HashMap<>(attrs);
125125
localAttrs.putAll(attrs(method.getAnnotations()));
126126

127127
for (String path : expandPaths(rootPaths, method)) {
@@ -146,22 +146,22 @@ public static List<Route.Definition> routes(final Env env, final RouteMetadata c
146146
return definitions;
147147
}
148148

149-
private static Map<String, String> attrs(final Annotation[] annotations) {
150-
Map<String, String> result = new LinkedHashMap<>();
149+
private static Map<String, Object> attrs(final Annotation[] annotations) {
150+
Map<String, Object> result = new LinkedHashMap<>();
151151
for (Annotation annotation : annotations) {
152152
result.putAll(attrs(annotation));
153153
}
154154
return result;
155155
}
156156

157-
private static Map<String, String> attrs(final Annotation annotation) {
158-
Map<String, String> result = new LinkedHashMap<>();
157+
private static Map<String, Object> attrs(final Annotation annotation) {
158+
Map<String, Object> result = new LinkedHashMap<>();
159159
Class<? extends Annotation> annotationType = annotation.annotationType();
160160
if (!IGNORE.contains(annotationType)) {
161161
Method[] attrs = annotation.annotationType().getDeclaredMethods();
162162
for (Method attr : attrs) {
163163
Try.of(() -> attr.invoke(annotation))
164-
.onSuccess(value -> result.put(attrName(annotation, attr), value.toString()));
164+
.onSuccess(value -> result.put(attrName(annotation, attr), value));
165165
}
166166
}
167167
return result;

jooby/src/test/java/org/jooby/RouteCollectionTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public void renderer() {
1616
new Route.Collection(def)
1717
.renderer("json");
1818

19-
assertEquals("json", def.attr("renderer").get());
19+
assertEquals("json", def.attr("renderer"));
2020
}
2121

2222
@Test
@@ -26,7 +26,7 @@ public void attr() {
2626
new Route.Collection(def)
2727
.attr("foo", "bar");
2828

29-
assertEquals("bar", def.attr("foo").get());
29+
assertEquals("bar", def.attr("foo"));
3030
}
3131

3232
@Test

0 commit comments

Comments
 (0)