package com.google.sitebricks.routing;
import net.jcip.annotations.Immutable;
import org.jetbrains.annotations.NotNull;
import java.util.*;
/**
* @author Dhanji R. Prasanna (dhanji@gmail.com)
*/
@Immutable
class PathMatcherChain implements PathMatcher {
private final List<PathMatcher> path;
private static final String PATH_SEPARATOR = "/";
public PathMatcherChain(String path) {
this.path = toMatchChain(path);
}
//converts a string path to a tree of heterogenous matchers
private static List<PathMatcher> toMatchChain(String path) {
String[] pieces = path.split(PATH_SEPARATOR);
List<PathMatcher> matchers = new ArrayList<PathMatcher>();
for (String piece : pieces) {
matchers.add((piece.startsWith(":")) ? new GreedyPathMatcher(piece) : new SimplePathMatcher(piece));
}
return Collections.unmodifiableList(matchers);
}
public String name() {
return null;
}
public boolean matches(String incoming) {
final Map<String, String> map = findMatches(incoming);
return null != map;
}
//TODO this whole path matching algorithm is in linear time, could easily be constant time
public Map<String, String> findMatches(String incoming) {
String[] pieces = incoming.split(PATH_SEPARATOR);
int i = 0;
//too many matchers, short circuit
if (path.size() > pieces.length)
return null;
Map<String, String> values = new HashMap<String, String>();
for (PathMatcher pathMatcher : path) {
//sanity to prevent fencepost
if (i == pieces.length)
return pathMatcher.matches("") ? values : null; //go greedy on index paths
String piece = pieces[i];
if (!pathMatcher.matches(piece))
return null;
//store variable as needed
final String name = pathMatcher.name();
if (null != name)
values.put(name, piece);
//next piece
i++;
}
return (i == pieces.length) ? values : null;
}
@Immutable
static class SimplePathMatcher implements PathMatcher {
private final String path;
SimplePathMatcher(String path) {
this.path = path;
}
public boolean matches(String incoming) {
return path.equals(incoming);
}
//TODO this whole path matching algorithm is in linear time, could easily be constant time
@NotNull
public Map<String, String> findMatches(String incoming) {
return Collections.emptyMap();
}
public String name() {
return null;
}
}
//matches anything, i.e. a variable :blah inside a path template
@Immutable
static class GreedyPathMatcher implements PathMatcher {
private final String variable;
public GreedyPathMatcher(String piece) {
this.variable = piece.substring(1);
}
public boolean matches(String incoming) {
return true;
}
@NotNull
public Map<String, String> findMatches(String incoming) {
return Collections.emptyMap();
}
public String name() {
return variable;
}
}
//matches nothing, i.e. always returns false (used for blocking sitebricks)
@Immutable
static class IgnoringPathMatcher implements PathMatcher {
public boolean matches(String incoming) {
return false;
}
@NotNull
public Map<String, String> findMatches(String incoming) {
return Collections.emptyMap();
}
public String name() {
return "";
}
}
static PathMatcher ignoring() {
return new IgnoringPathMatcher();
}
}