Skip to content

Commit

Permalink
Fix 404 handling in place manager
Browse files Browse the repository at this point in the history
  • Loading branch information
hpehl committed Mar 5, 2024
1 parent 9e8a851 commit 6e1b7ff
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 49 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Fixed

- Fix 404 handling in place manager

## [1.3.1] - 2024-03-04

### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.Version;

/**
* A code generator which generates code / resources from freemarker templates.
Expand All @@ -32,7 +31,6 @@
*/
class CodeGenerator {

public static final Version VERSION = new Version(2, 3, 32);
private final Configuration config;

CodeGenerator(Class<?> resourceLoaderClass, String templates) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@

import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.tools.JavaFileObject;
Expand All @@ -33,14 +35,16 @@

import com.google.auto.common.BasicAnnotationProcessor;
import com.google.auto.service.AutoService;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSetMultimap;

import static com.google.auto.common.MoreElements.asType;
import static com.google.common.base.Strings.emptyToNull;
import static java.util.Collections.emptySet;

@SuppressWarnings("unused")
@AutoService({ Processor.class })
@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_11)
@SupportedAnnotationTypes({ "org.jboss.elemento.router.Route" })
public class RouteProcessor extends BasicAnnotationProcessor {

@Override
Expand Down Expand Up @@ -80,9 +84,9 @@ public Set<? extends Element> process(ImmutableSetMultimap<String, Element> elem
Element element = entry.getValue();
Route route = element.getAnnotation(Route.class);
// noinspection UnstableApiUsage
routes.add(new RouteInfo(Strings.emptyToNull(route.value()),
Strings.emptyToNull(route.title()),
Strings.emptyToNull(route.selector()),
routes.add(new RouteInfo(emptyToNull(route.value()),
emptyToNull(route.title()),
emptyToNull(route.selector()),
asType(element).getQualifiedName().toString()));
}

Expand Down
4 changes: 4 additions & 0 deletions router/src/main/java/org/jboss/elemento/router/Base.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ String relative(String path) {
return path;
}

boolean isRelative(String path) {
return empty || (path != null && path.startsWith(slashBase));
}

String absolute(String path) {
if (!empty && path != null) {
String safePath = path.startsWith("/") ? path : "/" + path;
Expand Down
95 changes: 53 additions & 42 deletions router/src/main/java/org/jboss/elemento/router/PlaceManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public class PlaceManager {

// ------------------------------------------------------ instance

private static final Place NOT_FOUND = new Place("/404", "Not found");
private static final Place NOT_FOUND = new Place("org.jboss.elemento.router.notFound");

private final Map<String, Place> routes;
private final Map<Place, Supplier<Page>> places;
Expand All @@ -75,6 +75,7 @@ public class PlaceManager {
private Function<String, String> title;
private Function<Place, Page> notFound;
private By linkSelector;
private String failedRoute;

public PlaceManager() {
this.routes = new HashMap<>();
Expand All @@ -87,6 +88,7 @@ public PlaceManager() {
this.title = Function.identity();
this.notFound = place -> new DefaultNotFound();
this.linkSelector = null;
this.failedRoute = null;
}

// ------------------------------------------------------ builder
Expand Down Expand Up @@ -162,25 +164,21 @@ public Place current() {
}

public Place place(String route) {
String relative = base.relative(route);
// TODO Find not only exact matches, but also closest matches
// like /a/b/c -> /a/b
return routes.getOrDefault(relative, NOT_FOUND);
Place place = findPlace(route);
return NOT_FOUND.equals(place) ? null : place;
}

public void start() {
bindClickHandler();
bindHistoryHandler();
Place place = place(location.pathname);
if (place != null) {
if (internalGoto(place)) {
updateHistory(place, false);
}
Place place = findPlace(location.pathname);
if (internalGoto(place)) {
updateHistory(place, false);
}
}

public void goTo(String route) {
goTo(place(route));
goTo(findPlace(route));
}

public void goTo(Place place) {
Expand All @@ -198,17 +196,11 @@ private void bindClickHandler() {
HTMLAnchorElement a = anchorElement(e);
if (a != null) {
URL url = new URL(a.href, location.origin);
if (url.origin.equals(location.origin)) { // only links of this origin
if (url.hash == null || url.hash.isEmpty()) { // no hash links
if (linkSelector == null || a.matches(linkSelector.toString())) {
Place place = place(url.pathname);
if (place != null) {
e.preventDefault();
if (internalGoto(place)) {
updateHistory(place, true);
}
}
}
if (shouldHandleLink(a, url)) {
Place place = findPlace(url.pathname);
e.preventDefault();
if (internalGoto(place)) {
updateHistory(place, true);
}
}
}
Expand All @@ -229,25 +221,43 @@ private HTMLAnchorElement anchorElement(Event event) {
return anchorElement;
}

private boolean shouldHandleLink(HTMLAnchorElement a, URL url) {
if (url.origin.equals(location.origin) && url.hash.isEmpty()) { // only links of this origin w/o a hash
if (base.isRelative(url.pathname)) { // and the same base relative path
return linkSelector == null || a.matches(linkSelector.toString());
}
}
return false;
}

private void bindHistoryHandler() {
bind(window, popstate, event -> {
if (event.state != null) {
String route = String.valueOf(event.state);
Place place = place(route);
if (place != null) {
internalGoto(place);
}
Place place = findPlace(route);
internalGoto(place);
}
});
}

private Place findPlace(String route) {
String relative = base.relative(route);
// TODO Find not only exact matches, but also closest matches
// like /a/b/c -> /a/b
Place place = routes.getOrDefault(relative, NOT_FOUND);
if (NOT_FOUND.equals(place)) {
failedRoute = route;
}
return place;
}

private boolean internalGoto(Place place) {
for (BeforePlaceHandler handler : beforeHandlers) {
if (!handler.shouldGoTo(this, place)) {
return false;
}
}
Page page = page(place);
Page page = findPage(place);
if (place.title != null) {
document.title = this.title.apply(place.title);
}
Expand All @@ -259,29 +269,30 @@ private boolean internalGoto(Place place) {
for (HTMLElement e : page.elements()) {
rootElement.appendChild(e);
}
current = place;
for (AfterPlaceHandler handler : afterHandlers) {
handler.afterPlace(this, place, page);

if (NOT_FOUND.equals(place)) {
return false;
} else {
current = place;
for (AfterPlaceHandler handler : afterHandlers) {
handler.afterPlace(this, place, page);
}
return true;
}
return true;
}

private Page page(Place place) {
if (place != null) {
if (places.containsKey(place)) {
Supplier<Page> supplier = places.get(place);
return supplier.get();
} else {
return notFound(place);
}
private Page findPage(Place place) {
if (places.containsKey(place)) {
Supplier<Page> supplier = places.get(place);
return supplier.get();
} else {
return notFound(NOT_FOUND);
return notFound();
}
}

private Page notFound(Place place) {
private Page notFound() {
if (notFound != null) {
return notFound.apply(place);
return notFound.apply(new Place(failedRoute, "Not found"));
} else {
return new DefaultNotFound();
}
Expand Down
21 changes: 21 additions & 0 deletions router/src/test/java/org/jboss/elemento/router/BaseTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,25 @@ void absolute() {
assertEquals("/a/b/a/b", nested.absolute("/a/b"));
assertEquals("/a/b/a/b/c", nested.absolute("/a/b/c"));
}

@Test
void baseRelative() {
Base empty = new Base("/");
assertTrue(empty.isRelative("/a"));
assertTrue(empty.isRelative("/a/b"));
assertTrue(empty.isRelative("/a/b/c"));
assertTrue(empty.isRelative("/b"));

Base simple = new Base("/a");
assertTrue(simple.isRelative("/a"));
assertTrue(simple.isRelative("/a/b"));
assertTrue(simple.isRelative("/a/b/c"));
assertFalse(simple.isRelative("/b"));

Base nested = new Base("/a/b");
assertFalse(nested.isRelative("/a"));
assertTrue(nested.isRelative("/a/b"));
assertTrue(nested.isRelative("/a/b/c"));
assertFalse(nested.isRelative("/b"));
}
}

0 comments on commit 6e1b7ff

Please sign in to comment.