spark.route.SimpleRouteMatcher Maven / Gradle / Ivy
/*
* Copyright 2011- Per Wendel
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package spark.route;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import spark.utils.MimeParse;
import spark.utils.SparkUtils;
/**
* Simple route matcher that is supposed to work exactly as Sinatra's
*
* @author Per Wendel
*/
public class SimpleRouteMatcher implements RouteMatcher {
private static final org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(SimpleRouteMatcher.class);
private List routes;
private static class RouteEntry {
private HttpMethod httpMethod;
private String path;
private String acceptedType;
private Object target;
private boolean matches(HttpMethod httpMethod, String path) {
if ( (httpMethod == HttpMethod.before || httpMethod == HttpMethod.after)
&& (this.httpMethod == httpMethod)
&& this.path.equals(SparkUtils.ALL_PATHS)) {
// Is filter and matches all
return true;
}
boolean match = false;
if (this.httpMethod == httpMethod) {
match = matchPath(path);
}
return match;
}
private boolean matchPath(String path) { // NOSONAR
if (!this.path.endsWith("*") && ((path.endsWith("/") && !this.path.endsWith("/")) // NOSONAR
|| (this.path.endsWith("/") && !path.endsWith("/")))) {
// One and not both ends with slash
return false;
}
if (this.path.equals(path)) {
// Paths are the same
return true;
}
// check params
List thisPathList = SparkUtils.convertRouteToList(this.path);
List pathList = SparkUtils.convertRouteToList(path);
int thisPathSize = thisPathList.size();
int pathSize = pathList.size();
if (thisPathSize == pathSize) {
for (int i = 0; i < thisPathSize; i++) {
String thisPathPart = thisPathList.get(i);
String pathPart = pathList.get(i);
if ((i == thisPathSize -1) && (thisPathPart.equals("*") && this.path.endsWith("*"))) {
// wildcard match
return true;
}
if ((!thisPathPart.startsWith(":"))
&& !thisPathPart.equals(pathPart)
&& !thisPathPart.equals("*")) {
return false;
}
}
// All parts matched
return true;
} else {
// Number of "path parts" not the same
// check wild card:
if (this.path.endsWith("*")) {
if (pathSize == (thisPathSize - 1) && (path.endsWith("/"))) {
// Hack for making wildcards work with trailing slash
pathList.add("");
pathList.add("");
pathSize += 2;
}
if (thisPathSize < pathSize) {
for (int i = 0; i < thisPathSize; i++) {
String thisPathPart = thisPathList.get(i);
String pathPart = pathList.get(i);
if (thisPathPart.equals("*") && (i == thisPathSize -1) && this.path.endsWith("*")) {
// wildcard match
return true;
}
if (!thisPathPart.startsWith(":")
&& !thisPathPart.equals(pathPart)
&& !thisPathPart.equals("*")) {
return false;
}
}
// All parts matched
return true;
}
// End check wild card
}
return false;
}
}
public String toString() {
return httpMethod.name() + ", " + path + ", " + target;
}
}
public SimpleRouteMatcher() {
routes = new ArrayList();
}
@Override
public List findTargetsForRequestedRoute(HttpMethod httpMethod, String path, String acceptType) {
List matchSet = new ArrayList();
List routeEntries = this.findTargetsForRequestedRoute(httpMethod, path);
for (RouteEntry routeEntry : routeEntries) {
if(acceptType != null) {
String bestMatch = MimeParse.bestMatch(Arrays.asList(routeEntry.acceptedType), acceptType);
if(routeWithGivenAcceptType(bestMatch)) {
matchSet.add(new RouteMatch(httpMethod, routeEntry.target, routeEntry.path, path, acceptType));
}
} else {
matchSet.add(new RouteMatch(httpMethod, routeEntry.target, routeEntry.path, path, acceptType));
}
}
return matchSet;
}
@Override
public RouteMatch findTargetForRequestedRoute(HttpMethod httpMethod, String path, String acceptType) {
List routeEntries = this.findTargetsForRequestedRoute(httpMethod, path);
RouteEntry entry = findTargetWithGivenAcceptType(routeEntries, acceptType);
return entry != null ? new RouteMatch(httpMethod, entry.target, entry.path, path, acceptType) : null;
}
private RouteEntry findTargetWithGivenAcceptType(List routeMatchs, String acceptType) {
if(acceptType != null && routeMatchs.size() > 0) {
Map acceptedMimeTypes = getAcceptedMimeTypes(routeMatchs);
String bestMatch = MimeParse.bestMatch(acceptedMimeTypes.keySet(), acceptType);
if(routeWithGivenAcceptType(bestMatch)) {
return acceptedMimeTypes.get(bestMatch);
} else {
return null;
}
} else {
if(routeMatchs.size() > 0) {
return routeMatchs.get(0);
}
}
return null;
}
private boolean routeWithGivenAcceptType(String bestMatch) {
return !MimeParse.NO_MIME_TYPE.equals(bestMatch);
}
private List findTargetsForRequestedRoute(HttpMethod httpMethod, String path) {
List matchSet = new ArrayList();
for (RouteEntry entry : routes) {
if (entry.matches(httpMethod, path)) {
matchSet.add(entry);
}
}
return matchSet;
}
@Override
public void parseValidateAddRoute(String route, String acceptType, Object target) {
try {
int singleQuoteIndex = route.indexOf(SINGLE_QUOTE);
String httpMethod = route.substring(0, singleQuoteIndex).trim().toLowerCase(); // NOSONAR
String url = route.substring(singleQuoteIndex + 1, route.length() - 1).trim(); // NOSONAR
// Use special enum stuff to get from value
HttpMethod method;
try {
method = HttpMethod.valueOf(httpMethod);
} catch (IllegalArgumentException e) {
LOG.error("The @Route value: "
+ route
+ " has an invalid HTTP method part: "
+ httpMethod
+ ".");
return;
}
addRoute(method, url, acceptType, target);
} catch (Exception e) {
LOG.error("The @Route value: " + route + " is not in the correct format", e);
}
}
private void addRoute(HttpMethod method, String url, String acceptedType, Object target) {
RouteEntry entry = new RouteEntry();
entry.httpMethod = method;
entry.path = url;
entry.target = target;
entry.acceptedType = acceptedType;
LOG.debug("Adds route: " + entry);
// Adds to end of list
routes.add(entry);
}
//can be cached? I don't think so.
private Map getAcceptedMimeTypes(List routes) {
Map acceptedTypes = new HashMap();
for (RouteEntry routeEntry : routes) {
if(!acceptedTypes.containsKey(routeEntry.acceptedType)) {
acceptedTypes.put(routeEntry.acceptedType, routeEntry);
}
}
return acceptedTypes;
}
@Override
public void clearRoutes() {
routes.clear();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy