
co.cask.http.PatternPathRouterWithGroups Maven / Gradle / Ivy
/*
* Copyright © 2014 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;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
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}
private static final Pattern GROUP_PATTERN = Pattern.compile("\\{(.*?)\\}");
// non-greedy wild card match.
private static final Pattern WILD_CARD_PATTERN = Pattern.compile("\\*\\*");
private final List> patternRouteList;
public static PatternPathRouterWithGroups create() {
return new PatternPathRouterWithGroups<>();
}
/**
* Initialize PatternPathRouterWithGroups.
*/
public PatternPathRouterWithGroups() {
this.patternRouteList = Lists.newArrayList();
}
/**
* 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("/");
StringBuilder sb = new StringBuilder();
List groupNames = Lists.newArrayList();
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 = Lists.newArrayList();
for (ImmutablePair patternRoute : patternRouteList) {
ImmutableMap.Builder groupNameValuesBuilder = ImmutableMap.builder();
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.build()));
}
}
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 Objects.toStringHelper(this)
.add("destination", destination)
.add("groupNameValues", groupNameValues)
.toString();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy