Skip to content

Commit 37fbbc7

Browse files
committed
add route.glob():boolean method fix #354
1 parent a482ceb commit 37fbbc7

File tree

9 files changed

+209
-66
lines changed

9 files changed

+209
-66
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package org.jooby.issues;
2+
3+
import org.jooby.test.ServerFeature;
4+
import org.junit.Test;
5+
6+
import com.google.common.collect.ImmutableMap;
7+
8+
public class Issue354 extends ServerFeature {
9+
10+
{
11+
get("/:glob", req -> req.route().reverse(req.route().glob() ? "yes" : "no"));
12+
get("/g/:glob",
13+
req -> req.route().reverse(ImmutableMap.of("glob", req.route().glob() ? "yes" : "no")));
14+
get("/s/*", req -> req.route().glob() ? "yes" : "no");
15+
get("/q/t?st", req -> req.route().glob() ? "yes" : "no");
16+
get("/w/**/*", req -> req.route().glob() ? "yes" : "no");
17+
}
18+
19+
@Test
20+
public void glob() throws Exception {
21+
request().get("/glob")
22+
.expect("/no");
23+
request().get("/g/glob")
24+
.expect("/g/no");
25+
26+
request().get("/s/glob")
27+
.expect("yes");
28+
request().get("/q/test")
29+
.expect("yes");
30+
request().get("/w/test")
31+
.expect("yes");
32+
}
33+
34+
}

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

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444

4545
import com.google.common.base.Strings;
4646
import com.google.common.collect.ImmutableList;
47-
import com.google.common.collect.ImmutableMap;
4847
import com.google.common.collect.Lists;
4948
import com.google.common.primitives.Primitives;
5049
import com.google.inject.Key;
@@ -1058,6 +1057,17 @@ public List<String> vars() {
10581057
return cpattern.vars();
10591058
}
10601059

1060+
/**
1061+
* Indicates if the {@link #pattern()} contains a glob charecter, like <code>?</code>,
1062+
* <code>*</code> or <code>**</code>.
1063+
*
1064+
* @return Indicates if the {@link #pattern()} contains a glob charecter, like <code>?</code>,
1065+
* <code>*</code> or <code>**</code>.
1066+
*/
1067+
public boolean glob() {
1068+
return cpattern.glob();
1069+
}
1070+
10611071
/**
10621072
* Recreate a route path and apply the given variables.
10631073
*
@@ -1113,22 +1123,22 @@ public <T> T attr(final String name) {
11131123
* @return A read only view of attributes.
11141124
*/
11151125
public Map<String, Object> attributes() {
1116-
return ImmutableMap.copyOf(attributes);
1126+
return Collections.unmodifiableMap(attributes);
11171127
}
11181128

11191129
/**
11201130
* Test if the route matches the given verb, path, content type and accept header.
11211131
*
1122-
* @param verb A HTTP verb.
1132+
* @param method A HTTP verb.
11231133
* @param path Current HTTP path.
11241134
* @param contentType The <code>Content-Type</code> header.
11251135
* @param accept The <code>Accept</code> header.
11261136
* @return A route or an empty optional.
11271137
*/
1128-
public Optional<Route> matches(final String verb,
1138+
public Optional<Route> matches(final String method,
11291139
final String path, final MediaType contentType,
11301140
final List<MediaType> accept) {
1131-
String fpath = verb + path;
1141+
String fpath = method + path;
11321142
if (excludes.size() > 0 && excludes(fpath)) {
11331143
return Optional.empty();
11341144
}
@@ -1139,7 +1149,7 @@ public Optional<Route> matches(final String verb,
11391149
// keep accept when */*
11401150
List<MediaType> produces = result.size() == 1 && result.get(0).name().equals("*/*")
11411151
? accept : this.produces;
1142-
return Optional.of(asRoute(verb, matcher, produces));
1152+
return Optional.of(asRoute(method, matcher, produces));
11431153
}
11441154
}
11451155
return Optional.empty();
@@ -1286,14 +1296,14 @@ private boolean excludes(final String path) {
12861296
* @return All the types this route can consumes.
12871297
*/
12881298
public List<MediaType> consumes() {
1289-
return ImmutableList.copyOf(this.consumes);
1299+
return Collections.unmodifiableList(this.consumes);
12901300
}
12911301

12921302
/**
12931303
* @return All the types this route can produces.
12941304
*/
12951305
public List<MediaType> produces() {
1296-
return ImmutableList.copyOf(this.produces);
1306+
return Collections.unmodifiableList(this.produces);
12971307
}
12981308

12991309
@Override
@@ -1327,8 +1337,7 @@ private Route asRoute(final String method, final RouteMatcher matcher,
13271337
if (filter instanceof AssetProxy) {
13281338
filter = ((AssetProxy) filter).delegate();
13291339
}
1330-
return new RouteImpl(filter, method, matcher.path(), pattern, name, matcher.vars(), consumes,
1331-
produces, attributes, mapper);
1340+
return new RouteImpl(filter, this, method, matcher.path(), produces, matcher.vars(), mapper);
13321341
}
13331342

13341343
}
@@ -1400,6 +1409,21 @@ public <T> T attr(final String name) {
14001409
return route.attr(name);
14011410
}
14021411

1412+
@Override
1413+
public boolean glob() {
1414+
return route.glob();
1415+
}
1416+
1417+
@Override
1418+
public String reverse(final Map<String, Object> vars) {
1419+
return route.reverse(vars);
1420+
}
1421+
1422+
@Override
1423+
public String reverse(final Object... values) {
1424+
return route.reverse(values);
1425+
}
1426+
14031427
@Override
14041428
public String toString() {
14051429
return route.toString();
@@ -2041,6 +2065,31 @@ default <T> T attr(final String name) {
20412065
return (T) attributes().get(name);
20422066
}
20432067

2068+
/**
2069+
* Indicates if the {@link #pattern()} contains a glob charecter, like <code>?</code>,
2070+
* <code>*</code> or <code>**</code>.
2071+
*
2072+
* @return Indicates if the {@link #pattern()} contains a glob charecter, like <code>?</code>,
2073+
* <code>*</code> or <code>**</code>.
2074+
*/
2075+
boolean glob();
2076+
2077+
/**
2078+
* Recreate a route path and apply the given variables.
2079+
*
2080+
* @param vars Path variables.
2081+
* @return A route pattern.
2082+
*/
2083+
String reverse(final Map<String, Object> vars);
2084+
2085+
/**
2086+
* Recreate a route path and apply the given variables.
2087+
*
2088+
* @param values Path variable values.
2089+
* @return A route pattern.
2090+
*/
2091+
String reverse(final Object... values);
2092+
20442093
/**
20452094
* Normalize a path by removing double or trailing slashes.
20462095
*

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

Lines changed: 29 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -42,25 +42,17 @@ public class RouteImpl implements Route, Route.Filter {
4242

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

45-
private static ImmutableMap<String, Object> NO_ATTRS = ImmutableMap.of();
46-
47-
private String method;
45+
private Definition route;
4846

4947
private String path;
5048

51-
private String pattern;
52-
53-
private String name;
54-
5549
private Map<Object, String> vars;
5650

57-
private List<MediaType> consumes;
51+
private Filter filter;
5852

5953
private List<MediaType> produces;
6054

61-
private Filter filter;
62-
63-
private Map<String, Object> attributes;
55+
private String method;
6456

6557
public static RouteImpl notFound(final String method, final String path,
6658
final List<MediaType> produces) {
@@ -73,27 +65,18 @@ public static RouteImpl notFound(final String method, final String path,
7365

7466
public static RouteImpl fromStatus(final Filter filter, final String method,
7567
final String path, final String name, final List<MediaType> produces) {
76-
return new RouteImpl(filter, method, path, path, name, NO_VARS, MediaType.ALL, produces,
77-
NO_ATTRS, null) {
68+
return new RouteImpl(filter, new Route.Definition(method, path, filter)
69+
.name(name), method, path, produces, NO_VARS, null) {
7870
@Override
7971
public boolean apply(final String filter) {
8072
return true;
8173
}
8274
};
8375
}
8476

85-
public RouteImpl(final Filter filter, final String method, final String path,
86-
final String pattern, final String name, final Map<Object, String> vars,
87-
final List<MediaType> consumes, final List<MediaType> produces,
88-
final Map<String, Object> attributes, final Mapper<?> mapper) {
89-
this(filter, method, path, pattern, name, vars, consumes, produces,
90-
ImmutableMap.<String, Object> copyOf(attributes), mapper);
91-
}
92-
93-
public RouteImpl(final Filter filter, final String method, final String path,
94-
final String pattern, final String name, final Map<Object, String> vars,
95-
final List<MediaType> consumes, final List<MediaType> produces,
96-
final ImmutableMap<String, Object> attributes, final Mapper<?> mapper) {
77+
public RouteImpl(final Filter filter, final Definition route, final String method,
78+
final String path, final List<MediaType> produces, final Map<Object, String> vars,
79+
final Mapper<?> mapper) {
9780
this.filter = Option.of(mapper)
9881
.map(m -> Match(filter).of(
9982
Case(instanceOf(Route.OneArgHandler.class),
@@ -109,14 +92,11 @@ public RouteImpl(final Filter filter, final String method, final String path,
10992
}),
11093
Case($(), filter)))
11194
.getOrElse(filter);
95+
this.route = route;
11296
this.method = method;
97+
this.produces = produces;
11398
this.path = path;
114-
this.pattern = pattern;
115-
this.name = name;
11699
this.vars = vars;
117-
this.consumes = consumes;
118-
this.produces = produces;
119-
this.attributes = attributes;
120100
}
121101

122102
@Override
@@ -127,7 +107,7 @@ public void handle(final Request request, final Response response, final Chain c
127107

128108
@Override
129109
public Map<String, Object> attributes() {
130-
return attributes;
110+
return route.attributes();
131111
}
132112

133113
@Override
@@ -142,12 +122,12 @@ public String method() {
142122

143123
@Override
144124
public String pattern() {
145-
return pattern.substring(pattern.indexOf('/'));
125+
return route.pattern().substring(route.pattern().indexOf('/'));
146126
}
147127

148128
@Override
149129
public String name() {
150-
return name;
130+
return route.name();
151131
}
152132

153133
@Override
@@ -157,14 +137,29 @@ public Map<Object, String> vars() {
157137

158138
@Override
159139
public List<MediaType> consumes() {
160-
return consumes;
140+
return route.consumes();
161141
}
162142

163143
@Override
164144
public List<MediaType> produces() {
165145
return produces;
166146
}
167147

148+
@Override
149+
public boolean glob() {
150+
return route.glob();
151+
}
152+
153+
@Override
154+
public String reverse(final Map<String, Object> vars) {
155+
return route.reverse(vars);
156+
}
157+
158+
@Override
159+
public String reverse(final Object... values) {
160+
return route.reverse(values);
161+
}
162+
168163
@Override
169164
public String toString() {
170165
StringBuilder buffer = new StringBuilder();

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

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
import java.util.stream.Collectors;
3232

3333
import javaslang.Tuple;
34-
import javaslang.Tuple3;
34+
import javaslang.Tuple4;
3535

3636
public class RoutePattern {
3737

@@ -48,15 +48,22 @@ public class RoutePattern {
4848

4949
private List<String> reverse;
5050

51+
private boolean glob;
52+
5153
public RoutePattern(final String verb, final String pattern) {
5254
requireNonNull(verb, "A HTTP verb is required.");
5355
requireNonNull(pattern, "A path pattern is required.");
5456
this.pattern = normalize(pattern);
55-
Tuple3<Function<String, RouteMatcher>, List<String>, List<String>> result = rewrite(this,
57+
Tuple4<Function<String, RouteMatcher>, List<String>, List<String>, Boolean> result = rewrite(this,
5658
verb.toUpperCase(), this.pattern.replace("/**/", "/**"));
5759
matcher = result._1;
5860
vars = result._2;
5961
reverse = result._3;
62+
glob = result._4;
63+
}
64+
65+
public boolean glob() {
66+
return glob;
6067
}
6168

6269
public List<String> vars() {
@@ -87,7 +94,7 @@ public RouteMatcher matcher(final String path) {
8794
return matcher.apply(path);
8895
}
8996

90-
private static Tuple3<Function<String, RouteMatcher>, List<String>, List<String>> rewrite(
97+
private static Tuple4<Function<String, RouteMatcher>, List<String>, List<String>, Boolean> rewrite(
9198
final RoutePattern owner, final String verb, final String pattern) {
9299
List<String> vars = new LinkedList<>();
93100
String rwrverb = verbs(verb);
@@ -96,6 +103,7 @@ private static Tuple3<Function<String, RouteMatcher>, List<String>, List<String>
96103
int end = 0;
97104
boolean regex = !rwrverb.equals(verb);
98105
List<String> reverse = new ArrayList<>();
106+
boolean glob = false;
99107
while (matcher.find()) {
100108
String head = pattern.substring(end, matcher.start());
101109
patternBuilder.append(Pattern.quote(head));
@@ -105,14 +113,17 @@ private static Tuple3<Function<String, RouteMatcher>, List<String>, List<String>
105113
patternBuilder.append("([^/])");
106114
reverse.add(match);
107115
regex = true;
116+
glob = true;
108117
} else if ("*".equals(match)) {
109118
patternBuilder.append("([^/]*)");
110119
reverse.add(match);
111120
regex = true;
121+
glob = true;
112122
} else if (match.equals("/**")) {
113123
reverse.add(match);
114124
patternBuilder.append("($|/.*)");
115125
regex = true;
126+
glob = true;
116127
} else if (match.startsWith(":")) {
117128
regex = true;
118129
String varName = match.substring(1);
@@ -143,7 +154,7 @@ private static Tuple3<Function<String, RouteMatcher>, List<String>, List<String>
143154
reverse.add(tail);
144155
patternBuilder.append(Pattern.quote(tail));
145156
return Tuple.of(fn(owner, regex, regex ? patternBuilder.toString() : verb + pattern, vars),
146-
vars, reverse);
157+
vars, reverse, glob);
147158
}
148159

149160
private static String verbs(final String verb) {
@@ -195,4 +206,5 @@ public static String normalize(final String pattern) {
195206
public String toString() {
196207
return pattern;
197208
}
209+
198210
}

0 commit comments

Comments
 (0)