All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.blade.mvc.route.RouteMatcher Maven / Gradle / Ivy

There is a newer version: 1.7.2-beta
Show newest version
/**
 * Copyright (c) 2015, biezhi 王爵 ([email protected])
 * 

* 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 com.blade.mvc.route; import com.blade.Blade; import com.blade.exception.BladeException; import com.blade.kit.CollectionKit; import com.blade.mvc.http.HttpMethod; import com.blade.mvc.http.Path; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.URI; import java.net.URISyntaxException; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Default Route Matcher * * @author biezhi * @since 1.7.1-release */ public class RouteMatcher { private static final Logger LOGGER = LoggerFactory.getLogger(RouteMatcher.class); // Storage URL and route private Map routes = null; private Map interceptors = null; private List interceptorRoutes = CollectionKit.newArrayList(8); private static final Pattern PATH_VARIABLE_PATTERN = Pattern.compile(":(\\w+)"); private static final String PATH_VARIABLE_REPLACE = "([^/]+)"; private Map> regexRoutes = new HashMap<>(); private Map staticRoutes = new HashMap<>(); private Map regexRoutePatterns = new HashMap<>(); private Map indexes = new HashMap<>(); private Map patternBuilders = new HashMap<>(); public RouteMatcher(Routers routers) { this.update(routers); } public void update(){ this.update(Blade.$().routers()); } public void update(Routers routers){ this.routes = routers.getRoutes(); this.interceptors = routers.getInterceptors(); Collection inters = interceptors.values(); if (!inters.isEmpty()) { this.interceptorRoutes.addAll(inters); } this.staticRoutes.clear(); this.regexRoutePatterns.clear(); this.regexRoutes.clear(); this.indexes.clear(); this.register(); } public Route lookupRoute(String httpMethod, String path){ path = parsePath(path); String routeKey = path + '#' + httpMethod.toUpperCase(); Route route = staticRoutes.get(routeKey); if(null != route){ return route; } route = staticRoutes.get(path + "#ALL"); if(null != route){ return route; } Map uriVariables = CollectionKit.newLinkedHashMap(); HttpMethod requestMethod = HttpMethod.valueOf(httpMethod); Matcher matcher = regexRoutePatterns.get(requestMethod).matcher(path); boolean matched = matcher.matches(); if(!matched){ matcher = regexRoutePatterns.get(HttpMethod.ALL).matcher(path); matched = matcher.matches(); } if (matched) { int i; for (i = 1; matcher.group(i) == null; i++); FastRouteMappingInfo mappingInfo = regexRoutes.get(requestMethod).get(i); route = mappingInfo.getRoute(); // find path variable String uriVariable; int j = 0; while (++i <= matcher.groupCount() && (uriVariable = matcher.group(i)) != null) { uriVariables.put(mappingInfo.getVariableNames().get(j++), uriVariable); } route.setPathParams(uriVariables); LOGGER.trace("lookup path: " + path + " uri variables: " + uriVariables); } return route; } /** * Find a route * @param httpMethod httpMethod * @param path request path * @return return route object */ public Route getRoute(String httpMethod, String path) { return lookupRoute(httpMethod, path); } /** * Find all in before of the interceptor * @param path request path * @return return interceptor list */ public List getBefore(String path) { List befores = CollectionKit.newArrayList(); String cleanPath = parsePath(path); interceptorRoutes.forEach(route -> { if (matchesPath(route.getPath(), cleanPath) && route.getHttpMethod() == HttpMethod.BEFORE) { befores.add(route); } }); this.giveMatch(path, befores); return befores; } /** * Find all in after of the interceptor * @param path request path * @return return interceptor list */ public List getAfter(String path) { List afters = CollectionKit.newArrayList(); String cleanPath = parsePath(path); interceptorRoutes.forEach(route -> { if (matchesPath(route.getPath(), cleanPath) && route.getHttpMethod() == HttpMethod.AFTER) { afters.add(route); } }); this.giveMatch(path, afters); return afters; } /** * Sort of path * * @param uri request uri * @param routes route list */ private void giveMatch(final String uri, List routes) { Collections.sort(routes, (o1, o2) -> { if (o2.getPath().equals(uri)) { return o2.getPath().indexOf(uri); } return -1; }); } /** * Matching path * * @param routePath route path * @param pathToMatch match path * @return return match is success */ private boolean matchesPath(String routePath, String pathToMatch) { routePath = routePath.replaceAll(Path.VAR_REGEXP, Path.VAR_REPLACE); return pathToMatch.matches("(?i)" + routePath); } /** * Parse Path * * @param path route path * @return return parsed path */ private String parsePath(String path) { path = Path.fixPath(path); try { URI uri = new URI(path); return uri.getPath(); } catch (URISyntaxException e) { throw new BladeException(e); } } // a bad way void register() { List routeHandlers = new ArrayList<>(routes.values()); routeHandlers.addAll(interceptors.values()); for (Route route : routeHandlers) { String path = parsePath(route.getPath()); Matcher matcher = PATH_VARIABLE_PATTERN.matcher(path); boolean find = false; List uriVariableNames = new ArrayList<>(); while (matcher.find()) { if (!find) { find = true; } String group = matcher.group(0); uriVariableNames.add(group.substring(1)); // {id} -> id } HttpMethod httpMethod = route.getHttpMethod(); if (find || (httpMethod == HttpMethod.AFTER || httpMethod == HttpMethod.BEFORE) ) { if (regexRoutes.get(httpMethod) == null) { regexRoutes.put(httpMethod, new HashMap<>()); patternBuilders.put(httpMethod, new StringBuilder("^")); indexes.put(httpMethod, 1); } int i = indexes.get(httpMethod); regexRoutes.get(httpMethod).put(i, new FastRouteMappingInfo(route, uriVariableNames)); indexes.put(httpMethod, i + uriVariableNames.size() + 1); patternBuilders.get(httpMethod).append("(").append(matcher.replaceAll(PATH_VARIABLE_REPLACE)).append(")|"); } else { String routeKey = path + '#' + httpMethod.toString(); if (staticRoutes.get(routeKey) == null) { staticRoutes.put(routeKey, route); } } } for (Map.Entry entry : patternBuilders.entrySet()) { HttpMethod httpMethod = entry.getKey(); StringBuilder patternBuilder = entry.getValue(); if (patternBuilder.length() > 1) { patternBuilder.setCharAt(patternBuilder.length() - 1, '$'); } LOGGER.debug("Fast Route Method: {}, regex: {}", httpMethod, patternBuilder); regexRoutePatterns.put(httpMethod, Pattern.compile(patternBuilder.toString())); } } private class FastRouteMappingInfo { private Route route; private List variableNames; public FastRouteMappingInfo(Route route, List variableNames) { this.route = route; this.variableNames = variableNames; } public Route getRoute() { return route; } public List getVariableNames() { return variableNames; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy