package com.vtence.molecule.middlewares;
import com.vtence.molecule.Application;
import com.vtence.molecule.Request;
import com.vtence.molecule.Response;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class URLMap extends AbstractMiddleware {
private final List<Mount> mounts = new ArrayList<>();
public URLMap() {
this(new NotFound());
}
public URLMap(Application fallback) {
this.connectTo(fallback);
}
public URLMap mount(String path, Application app) {
mounts.add(new Mount(path, app));
sortByMostSpecificPaths(mounts);
return this;
}
private void sortByMostSpecificPaths(List<Mount> mounts) {
mounts.sort((mount1, mount2) -> mount2.mountPoint.length() - mount1.mountPoint.length());
}
public void handle(Request request, Response response) throws Exception {
Optional<Mount> mount = mounts.stream().filter(m -> m.matches(request)).findFirst();
if (mount.isPresent()) {
mount.get().handle(request, response);
} else {
forward(request, response);
}
}
public interface MountPoint {
String app();
String uri(String path);
static MountPoint get(Request request) {
MountPoint mount = request.attribute(MountPoint.class);
if (mount == null) throw new IllegalArgumentException("Mount point not found");
return mount;
}
}
private static class Mount implements Application, MountPoint {
private final String mountPoint;
private final Application app;
public Mount(String mountPoint, Application app) {
this.mountPoint = mountPoint;
this.app = app;
}
public String app() {
return mountPoint;
}
public boolean matches(Request request) {
return request.path().startsWith(mountPoint) && pathInfo(request).startsWith("/");
}
public String pathInfo(Request request) {
if (mountPoint.equals("/")) return request.path();
String pathInfo = request.path().replaceFirst(mountPoint, "");
return pathInfo.isEmpty() ? "/" : pathInfo;
}
public String uri(String path) {
if (mountPoint.equals("/")) return path;
return mountPoint.concat(path.endsWith("/") ? stripTrailingSlash(path) : path);
}
private String stripTrailingSlash(String path) {
return path.replaceAll("/$", "");
}
public void handle(Request request, Response response) throws Exception {
request.path(pathInfo(request));
request.attribute(MountPoint.class, this);
app.handle(request, response);
}
}
}