com.atlassian.router.internal.PathMatcher Maven / Gradle / Ivy
The newest version!
package com.atlassian.router.internal;
import com.atlassian.parsers.routing.path.routerpathLexer;
import com.atlassian.parsers.routing.path.routerpathParser;
import com.atlassian.router.ParseException;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ConsoleErrorListener;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.ParseTree;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class PathMatcher {
public static PathMatcher parse(String query) throws IOException {
return parse(new ByteArrayInputStream(query.getBytes(StandardCharsets.UTF_8)));
}
public static PathMatcher parse(InputStream stream) throws IOException {
CharStream input = new ANTLRInputStream(stream);
routerpathLexer lexer = new routerpathLexer(input);
lexer.removeErrorListener(ConsoleErrorListener.INSTANCE);
CommonTokenStream tokens = new CommonTokenStream(lexer);
routerpathParser parser = new routerpathParser(tokens);
BuildASTWalker buildAST = new BuildASTWalker();
parser.addParseListener(buildAST);
ParseTree parseTree = parser.parseRoot();
if (tokens.LT(1).getType() != Token.EOF) {
throw new ParseException();
}
return new PathMatcher(buildAST.matchers, buildAST.tail);
}
public final List matchers;
public final TailMatcher tail;
public Map params;
private PathMatcher(List matchers, TailMatcher tail) {
this.matchers = matchers;
this.tail = tail;
}
/**
* In the event the path matches, then
* get params will return
* the params derived from this path
*
* @param path to match
* @return "take a wild guess"
*/
public boolean matches(String path) {
List segments = Arrays.
stream(path.split("/")).
filter((x) -> !"".equalsIgnoreCase(x)).
collect(Collectors.toList());
while (path.startsWith("/")) {
path = path.substring(1);
}
params = new Hashtable<>();
if (segments.size() < matchers.size()) {
return false;
}
if (segments.size() > matchers.size() && tail == null) {
return false;
}
int i;
for (i = 0; i < matchers.size(); i++) {
if (!matchers.get(i).tryMatch(segments.get(i), params)) {
params = new Hashtable<>();
return false;
}
}
if (segments.size() > matchers.size()) {
tail.tryMatch(
String.join(
"/",
segments.stream().
skip(matchers.size()).
collect(Collectors.toList())
),
params
);
} else if (tail != null) {
tail.tryMatch("", params);
}
return true;
}
/**
* returns the map associated with
* the most recent invokation of matches,
* returns an empty map if matches failed.
*
* @return key value extraction of the path string supplied to matches
*/
public Map getParams() {
return params;
}
interface SegmentMatcher {
boolean tryMatch(String segment, Map params);
}
static class Static implements SegmentMatcher {
final String staticValue;
public Static(String value) {
this.staticValue = value;
}
public boolean tryMatch(String segment, Map params) {
return staticValue.equalsIgnoreCase(segment);
}
}
static class Parameter implements SegmentMatcher {
final String paramName;
public Parameter(String name) {
this.paramName = name;
}
public boolean tryMatch(String segment, Map params) {
params.put(paramName, segment);
return true;
}
}
static class TailMatcher {
final String paramName;
public TailMatcher(String name) {
this.paramName = name;
}
public boolean tryMatch(String segment, Map params) {
params.put(paramName, segment);
return true;
}
}
}