/** * If there's no match, returns the result with {@link #notFound(Object) notFound} as the target * if it is set, otherwise returns {@code null}. */ public RouteResult<T> route(final HttpMethod method, final String uri) { final QueryStringDecoder decoder = new QueryStringDecoder(uri); final String[] tokens = StringUtil.split(Path.removeSlashesAtBothEnds(decoder.path()), '/'); MethodlessRouter<T> router = routers.get(method); if (router == null) { router = anyMethodRouter; } RouteResult<T> ret = router.route(tokens); if (ret != null) { return new RouteResult<T>(ret.target(), ret.pathParams(), decoder.parameters()); } if (router != anyMethodRouter) { ret = anyMethodRouter.route(tokens); if (ret != null) { return new RouteResult<T>(ret.target(), ret.pathParams(), decoder.parameters()); } } if (notFound != null) { // Return mutable map to be consistent, instead of // Collections.<String, String>emptyMap() return new RouteResult<T>(notFound, new HashMap<String, String>(), decoder.parameters()); } return null; }
/** Returns the number of routes in this router. */ public int size() { int ret = anyMethodRouter.size(); for (MethodlessRouter<T> router : routers.values()) { ret += router.size(); } return ret; }
/** * Given a target and params, this method tries to do the reverse routing and returns the path. * * <p>The params are put to placeholders in the path. The params can be a map of {@code * placeholder name -> value} or ordered values. If a param doesn't have a placeholder, it will be * put to the query part of the path. * * @return {@code null} if there's no match */ public String path(final T target, final Object... params) { final Collection<MethodlessRouter<T>> rs = routers.values(); for (MethodlessRouter<T> r : rs) { final String ret = r.path(target, params); if (ret != null) { return ret; } } return anyMethodRouter.path(target, params); }
/** * Given a target and params, this method tries to do the reverse routing and returns the path. * * <p>The params are put to placeholders in the path. The params can be a map of {@code * placeholder name -> value} or ordered values. If a param doesn't have a placeholder, it will be * put to the query part of the path. * * @return {@code null} if there's no match */ public String path(final HttpMethod method, final T target, final Object... params) { MethodlessRouter<T> router = (method == null) ? anyMethodRouter : routers.get(method); // Fallback to anyMethodRouter if no router is found for the method if (router == null) { router = anyMethodRouter; } final String ret = router.path(target, params); if (ret != null) { return ret; } // Fallback to anyMethodRouter if the router was not anyMethodRouter and no path is found return (router != anyMethodRouter) ? anyMethodRouter.path(target, params) : null; }
/** * Returns allowed methods for a specific URI. * * <p>For {@code OPTIONS *}, use {@link #allAllowedMethods()} instead of this method. */ public Set<HttpMethod> allowedMethods(final String uri) { final QueryStringDecoder decoder = new QueryStringDecoder(uri); final String[] tokens = StringUtil.split(Path.removeSlashesAtBothEnds(decoder.path()), '/'); if (anyMethodRouter.anyMatched(tokens)) { return allAllowedMethods(); } final Set<HttpMethod> ret = new HashSet<HttpMethod>(routers.size()); for (Map.Entry<HttpMethod, MethodlessRouter<T>> entry : routers.entrySet()) { final MethodlessRouter<T> router = entry.getValue(); if (router.anyMatched(tokens)) { final HttpMethod method = entry.getKey(); ret.add(method); } } return ret; }
/** Returns all methods that this router handles. For {@code OPTIONS *}. */ public Set<HttpMethod> allAllowedMethods() { if (anyMethodRouter.size() > 0) { final Set<HttpMethod> ret = new HashSet<HttpMethod>(9); ret.add(HttpMethod.CONNECT); ret.add(HttpMethod.DELETE); ret.add(HttpMethod.GET); ret.add(HttpMethod.HEAD); ret.add(HttpMethod.OPTIONS); ret.add(HttpMethod.PATCH); ret.add(HttpMethod.POST); ret.add(HttpMethod.PUT); ret.add(HttpMethod.TRACE); return ret; } else { return new HashSet<HttpMethod>(routers.keySet()); } }
/** Returns visualized routing rules. */ @Override public String toString() { // Step 1/2: Dump routers and anyMethodRouter in order final int numRoutes = size(); final List<String> methods = new ArrayList<String>(numRoutes); final List<String> paths = new ArrayList<String>(numRoutes); final List<String> targets = new ArrayList<String>(numRoutes); // For router for (Entry<HttpMethod, MethodlessRouter<T>> e : routers.entrySet()) { HttpMethod method = e.getKey(); MethodlessRouter<T> router = e.getValue(); aggregateRoutes(method.toString(), router.first().routes(), methods, paths, targets); aggregateRoutes(method.toString(), router.other().routes(), methods, paths, targets); aggregateRoutes(method.toString(), router.last().routes(), methods, paths, targets); } // For anyMethodRouter aggregateRoutes("*", anyMethodRouter.first().routes(), methods, paths, targets); aggregateRoutes("*", anyMethodRouter.other().routes(), methods, paths, targets); aggregateRoutes("*", anyMethodRouter.last().routes(), methods, paths, targets); // For notFound if (notFound != null) { methods.add("*"); paths.add("*"); targets.add(targetToString(notFound)); } // Step 2/2: Format the List into aligned columns: <method> <path> <target> int maxLengthMethod = maxLength(methods); int maxLengthPath = maxLength(paths); String format = "%-" + maxLengthMethod + "s %-" + maxLengthPath + "s %s\n"; int initialCapacity = (maxLengthMethod + 1 + maxLengthPath + 1 + 20) * methods.size(); StringBuilder b = new StringBuilder(initialCapacity); for (int i = 0; i < methods.size(); i++) { String method = methods.get(i); String path = paths.get(i); String target = targets.get(i); b.append(String.format(format, method, path, target)); } return b.toString(); }
/** Removes all routes leading to the target. */ public void removeTarget(final T target) { for (MethodlessRouter<T> r : routers.values()) { r.removeTarget(target); } anyMethodRouter.removeTarget(target); }
/** Removes the route specified by the path. */ public void removePath(final String path) { for (MethodlessRouter<T> r : routers.values()) { r.removePath(path); } anyMethodRouter.removePath(path); }