|
41 | 41 | import org.jooby.internal.RouteMatcher; |
42 | 42 | import org.jooby.internal.RoutePattern; |
43 | 43 |
|
| 44 | +import com.google.common.base.CaseFormat; |
44 | 45 | import com.google.common.base.Strings; |
45 | 46 | import com.google.common.collect.ImmutableList; |
46 | 47 | import com.google.common.collect.Lists; |
47 | 48 | import com.google.common.primitives.Primitives; |
48 | 49 | import com.google.inject.Key; |
49 | 50 | import com.google.inject.TypeLiteral; |
50 | 51 |
|
| 52 | +import javaslang.CheckedFunction1; |
| 53 | + |
51 | 54 | /** |
52 | 55 | * Routes are a key concept in Jooby. Routes are executed in the same order they are defined |
53 | 56 | * (even for Mvc Routes). |
@@ -273,14 +276,56 @@ public interface Route { |
273 | 276 | */ |
274 | 277 | interface Mapper<T> { |
275 | 278 |
|
276 | | - @SuppressWarnings({"rawtypes", "unchecked" }) |
277 | | - static Mapper<Object> compose(final Mapper it, final Mapper next) { |
278 | | - return v -> { |
279 | | - Object m = it.map(v); |
280 | | - return m == v ? next.map(v) : m; |
| 279 | + /** |
| 280 | + * Produces a new mapper by combining the two mapper into one. |
| 281 | + * |
| 282 | + * @param it The first mapper to apply. |
| 283 | + * @param next The second mapper to apply. |
| 284 | + * @return A new mapper. |
| 285 | + */ |
| 286 | + @SuppressWarnings({"rawtypes", "unchecked" }) |
| 287 | + static Mapper<Object> chain(final Mapper it, final Mapper next) { |
| 288 | + return create(it.name() + ">" + next.name(), v -> next.map(it.map(v))); |
| 289 | + } |
| 290 | + |
| 291 | + /** |
| 292 | + * Creates a new named mapper (just syntax suggar for creating a new mapper). |
| 293 | + * |
| 294 | + * @param name Mapper's name. |
| 295 | + * @param fn Map function. |
| 296 | + * @return A new mapper. |
| 297 | + */ |
| 298 | + static <T> Mapper<T> create(final String name, final CheckedFunction1<T, Object> fn) { |
| 299 | + return new Route.Mapper<T>() { |
| 300 | + @Override |
| 301 | + public String name() { |
| 302 | + return name; |
| 303 | + } |
| 304 | + |
| 305 | + @Override |
| 306 | + public Object map(final T value) throws Throwable { |
| 307 | + return fn.apply(value); |
| 308 | + } |
| 309 | + |
| 310 | + @Override |
| 311 | + public String toString() { |
| 312 | + return name(); |
| 313 | + } |
281 | 314 | }; |
282 | 315 | } |
283 | 316 |
|
| 317 | + /** |
| 318 | + * @return Mapper's name. |
| 319 | + */ |
| 320 | + default String name() { |
| 321 | + String name = Optional.ofNullable(Strings.emptyToNull(getClass().getSimpleName())) |
| 322 | + .orElseGet(() -> { |
| 323 | + String classname = getClass().getName(); |
| 324 | + return classname.substring(classname.lastIndexOf('.') + 1); |
| 325 | + }); |
| 326 | + return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, name); |
| 327 | + } |
| 328 | + |
284 | 329 | /** |
285 | 330 | * Map the type to something else. |
286 | 331 | * |
@@ -1341,7 +1386,7 @@ public String toString() { |
1341 | 1386 | private Route asRoute(final String method, final RouteMatcher matcher, |
1342 | 1387 | final List<MediaType> produces) { |
1343 | 1388 | return new RouteImpl(filter, this, method, matcher.path(), produces, |
1344 | | - matcher.vars(), mapper); |
| 1389 | + matcher.vars(), mapper); |
1345 | 1390 | } |
1346 | 1391 |
|
1347 | 1392 | } |
|
0 commit comments