
co.cask.http.internal.PatternPathRouterWithGroups Maven / Gradle / Ivy
The newest version!
/*
* Copyright © 2017 Cask Data, Inc.
*
* 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 co.cask.http.internal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Matches incoming un-matched paths to destinations. Designed to be used for routing URI paths to http resources.
* Parameters within braces "{}" are treated as template parameter (a named wild-card pattern).
*
* @param represents the destination of the routes.
*/
public final class PatternPathRouterWithGroups {
//GROUP_PATTERN is used for named wild card pattern in paths which is specified within braces.
//Example: {id}
public static final Pattern GROUP_PATTERN = Pattern.compile("\\{(.*?)\\}");
// non-greedy wild card match.
private static final Pattern WILD_CARD_PATTERN = Pattern.compile("\\*\\*");
private final int maxPathParts;
private final List> patternRouteList;
public static PatternPathRouterWithGroups create(int maxPathParts) {
return new PatternPathRouterWithGroups<>(maxPathParts);
}
/**
* Initialize PatternPathRouterWithGroups.
*/
public PatternPathRouterWithGroups(int maxPathParts) {
this.maxPathParts = maxPathParts;
this.patternRouteList = new ArrayList<>();
}
/**
* Add a source and destination.
*
* @param source Source path to be routed. Routed path can have named wild-card pattern with braces "{}".
* @param destination Destination of the path.
*/
public void add(final String source, final T destination) {
// replace multiple slashes with a single slash.
String path = source.replaceAll("/+", "/");
path = (path.endsWith("/") && path.length() > 1)
? path.substring(0, path.length() - 1) : path;
String[] parts = path.split("/", maxPathParts + 2);
if (parts.length - 1 > maxPathParts) {
throw new IllegalArgumentException(String.format("Number of parts of path %s exceeds allowed limit %s",
source, maxPathParts));
}
StringBuilder sb = new StringBuilder();
List groupNames = new ArrayList<>();
for (String part : parts) {
Matcher groupMatcher = GROUP_PATTERN.matcher(part);
if (groupMatcher.matches()) {
groupNames.add(groupMatcher.group(1));
sb.append("([^/]+?)");
} else if (WILD_CARD_PATTERN.matcher(part).matches()) {
sb.append(".*?");
} else {
sb.append(part);
}
sb.append("/");
}
//Ignore the last "/"
sb.setLength(sb.length() - 1);
Pattern pattern = Pattern.compile(sb.toString());
patternRouteList.add(ImmutablePair.of(pattern, new RouteDestinationWithGroups(destination, groupNames)));
}
/**
* Get a list of destinations and the values matching templated parameter for the given path.
* Returns an empty list when there are no destinations that are matched.
*
* @param path path to be routed.
* @return List of Destinations matching the given route.
*/
public List> getDestinations(String path) {
String cleanPath = (path.endsWith("/") && path.length() > 1)
? path.substring(0, path.length() - 1) : path;
List> result = new ArrayList<>();
for (ImmutablePair patternRoute : patternRouteList) {
Map groupNameValuesBuilder = new HashMap<>();
Matcher matcher = patternRoute.getFirst().matcher(cleanPath);
if (matcher.matches()) {
int matchIndex = 1;
for (String name : patternRoute.getSecond().getGroupNames()) {
String value = matcher.group(matchIndex);
groupNameValuesBuilder.put(name, value);
matchIndex++;
}
result.add(new RoutableDestination<>(patternRoute.getSecond().getDestination(), groupNameValuesBuilder));
}
}
return result;
}
/**
* Helper class to store the groupNames and Destination.
*/
private final class RouteDestinationWithGroups {
private final T destination;
private final List groupNames;
public RouteDestinationWithGroups (T destination, List groupNames) {
this.destination = destination;
this.groupNames = groupNames;
}
public T getDestination() {
return destination;
}
public List getGroupNames() {
return groupNames;
}
}
/**
* Represents a matched destination.
* @param Type of destination.
*/
public static final class RoutableDestination {
private final T destination;
private final Map groupNameValues;
/**
* Construct the RouteableDestination with the given parameters.
*
* @param destination destination of the route.
* @param groupNameValues parameters
*/
public RoutableDestination(T destination, Map groupNameValues) {
this.destination = destination;
this.groupNameValues = groupNameValues;
}
/**
* @return destination of the route.
*/
public T getDestination() {
return destination;
}
/**
* @return Map of templated parameter and string representation group value matching the templated parameter as
* the value.
*/
public Map getGroupNameValues() {
return groupNameValues;
}
@Override
public String toString() {
return "RoutableDestination{" +
"destination=" + destination +
", groupNameValues=" + groupNameValues +
'}';
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy