package me.geso.routes;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Do not use directly.
*
* @param <T>
*/
class PathRoute<T> {
private final String path;
private final Pattern pattern;
private final T destination;
private final List<String> namedGroups = new ArrayList<>();
private static final String STAR_KEY = "SSSstarSSS";
private static final String BRACE_NORMAL_PATTERN_RE = "\\{(?<braceName>[a-zA-Z_][a-zA-Z0-9_-]*)\\}";
private static final String STAR_PATTERN_RE = "\\*";
// For named regexp matcher (e.g. /{id:[0-9]{5}}/{title:[a-zA-Z_]+})
private static final String NAMED_REGEX_MATCHER_PATTERN_RE =
"\\{(?<regexName>[^:]+?):(?<regex>.+?)\\}(?<delimiter>/|$)";
// RegExp meta characters... We should escape these characters.
private static final String ESCAPE_PATTERN_RE = "[\\-{}\\[\\]+?\\.,\\\\\\^$|#\\s]";
private static final Pattern MATCH_PATTERN = Pattern.compile(
"(" + BRACE_NORMAL_PATTERN_RE + ")" + "|"
+ "(" + STAR_PATTERN_RE + ")" + "|"
+ "(" + NAMED_REGEX_MATCHER_PATTERN_RE + ")" + "|"
+ "(" + ESCAPE_PATTERN_RE + ")"
);
PathRoute(String path, T destination) {
this.path = path;
this.pattern = Pattern.compile(compileToRegexp(path));
this.destination = destination;
}
String compileToRegexp(String path) {
Matcher m = MATCH_PATTERN.matcher(path);
StringBuffer sb = new StringBuffer(path.length());
while (m.find()) {
if (m.group(1) != null) {
// {foo}
namedGroups.add(m.group("braceName"));
String replace = "(?<" + m.group("braceName")
+ ">[a-zA-Z0-9._-]+)";
m.appendReplacement(sb, replace);
} else if (m.group(3) != null) {
namedGroups.add(STAR_KEY);
String replace = "(?<" + STAR_KEY + ">.+)";
m.appendReplacement(sb, replace);
} else if (m.group(4) != null) {
namedGroups.add(m.group("regexName"));
String delimiter = m.group("delimiter").equals("/") ? "/" : "";
m.appendReplacement(sb, "(?<" + m.group("regexName") + ">" + m.group("regex") + ")" + delimiter);
} else if (m.group(8) != null) {
// foo.bar ... needs escape meta character
String replace = "\\\\" + m.group(8);
m.appendReplacement(sb, replace);
} else {
throw new RuntimeException();
}
}
m.appendTail(sb);
return sb.toString();
}
boolean match(final String path, final Map<String, String> captured) {
Matcher m = pattern.matcher(path);
if (m.matches()) {
for (String name : namedGroups) {
String key = name.equals(STAR_KEY) ? "*" : name;
captured.put(key, m.group(name));
}
return true;
}
return false;
}
T getDestination() {
return destination;
}
String getPath() {
return path;
}
}