package org.zalando.riptide; import org.springframework.http.client.ClientHttpResponse; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import static java.util.Collections.unmodifiableMap; import static java.util.stream.Collectors.toMap; final class DefaultRoutingTree<A> implements RoutingTree<A> { private final Navigator<A> navigator; private final Map<A, Route> routes; private final Optional<Route> wildcard; DefaultRoutingTree(final Navigator<A> navigator, final List<Binding<A>> bindings) { this(navigator, map(bindings)); } private DefaultRoutingTree(final Navigator<A> navigator, final Map<A, Route> routes) { this(navigator, unmodifiableMap(routes), Optional.ofNullable(routes.remove(null))); } private DefaultRoutingTree(final Navigator<A> navigator, final Map<A, Route> routes, final Optional<Route> wildcard) { this.navigator = navigator; this.routes = routes; this.wildcard = wildcard; } @Override public Navigator<A> getNavigator() { return navigator; } @Override public Set<A> keySet() { return routes.keySet(); } @Override public Optional<Route> get(final A attribute) { return Optional.ofNullable(routes.get(attribute)); } @Override public Optional<Route> getWildcard() { return wildcard; } @Override public RoutingTree<A> merge(final List<Binding<A>> bindings) { final List<Binding<A>> present = new ArrayList<>(routes.size() + 1); routes.forEach((attribute, route) -> present.add(Binding.create(attribute, route))); wildcard.ifPresent(route -> present.add(Binding.create(null, route))); return RoutingTree.dispatch(navigator, navigator.merge(present, bindings)); } private static <A> Map<A, Route> map(final List<Binding<A>> bindings) { return bindings.stream() .collect(toMap(Binding::getAttribute, Binding::getRoute, (u, v) -> { throw new IllegalArgumentException(String.format("Duplicate key %s", u)); }, LinkedHashMap::new)); } @Override public void execute(final ClientHttpResponse response, final MessageReader reader) throws Exception { final Optional<Route> route = navigator.navigate(response, this); if (route.isPresent()) { try { route.get().execute(response, reader); } catch (final NoWildcardException e) { executeWildcard(response, reader); } } else { executeWildcard(response, reader); } } private void executeWildcard(final ClientHttpResponse response, final MessageReader reader) throws Exception { wildcard.orElseThrow(NoWildcardException::new).execute(response, reader); } }