io.polyglotted.common.web.PathRouter Maven / Gradle / Ivy
package io.polyglotted.common.web;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import io.polyglotted.common.model.Pair;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class PathRouter {
static final Pattern GROUP_PATTERN = Pattern.compile("\\{(.*?)\\}");
private static final Pattern WILD_CARD_PATTERN = Pattern.compile("\\*\\*");
private final int maxPathParts;
private final List> patternRouteList;
public static PathRouter create(int maxPathParts) { return new PathRouter<>(maxPathParts); }
private PathRouter(int maxPathParts) {
this.maxPathParts = maxPathParts;
this.patternRouteList = Lists.newArrayList();
}
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("/");
if (parts.length - 1 > maxPathParts) {
throw new IllegalArgumentException("Number of parts of path " + source + " exceeds allowed limit " + maxPathParts);
}
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(Pair.pair(pattern, new RouteDestinationWithGroups(destination, groupNames)));
}
public List> getDestinations(String path) {
String cleanPath = (path.endsWith("/") && path.length() > 1) ? path.substring(0, path.length() - 1) : path;
List> result = Lists.newArrayList();
for (Pair patternRoute : patternRouteList) {
ImmutableMap.Builder groupNameValuesBuilder = ImmutableMap.builder();
Matcher matcher = patternRoute._a.matcher(cleanPath);
if (matcher.matches()) {
int matchIndex = 1;
for (String name : patternRoute._b.groupNames) {
String value = matcher.group(matchIndex);
groupNameValuesBuilder.put(name, value);
matchIndex++;
}
result.add(new RoutableDestination<>(patternRoute._b.destination, groupNameValuesBuilder.build()));
}
}
return result;
}
@ToString
@RequiredArgsConstructor
private final class RouteDestinationWithGroups {
final T destination;
final List groupNames;
}
@RequiredArgsConstructor
public static final class RoutableDestination {
public final T destination;
public final Map groupNameValues;
}
}