1414 */
1515package io .github .problem4j .spring .web ;
1616
17+ import static io .github .problem4j .spring .web .util .HierarchyTraversalMode .SUPERCLASS ;
18+
1719import io .github .problem4j .spring .web .resolver .ProblemResolver ;
1820import io .github .problem4j .spring .web .util .ClassDistanceEvaluation ;
21+ import io .github .problem4j .spring .web .util .GraphClassDistanceEvaluation ;
22+ import java .util .ArrayList ;
23+ import java .util .Comparator ;
24+ import java .util .HashMap ;
1925import java .util .List ;
26+ import java .util .Map ;
27+ import java .util .Objects ;
28+ import java .util .Optional ;
2029
2130/**
2231 * {@link ProblemResolverStore} implementation evaluates resolved based on class and its
2534 * <p>Resolvers are matched to exceptions by assignability, preferring the most specific exception
2635 * type.
2736 */
28- public class DefaultProblemResolverStore extends AbstractProblemResolverStore {
37+ public class DefaultProblemResolverStore implements ProblemResolverStore {
38+
39+ private final Map <Class <? extends Exception >, ProblemResolver > resolvers ;
40+ private final ClassDistanceEvaluation classDistanceEvaluation ;
2941
3042 /**
3143 * Creates a new store initialized with the given resolvers.
@@ -34,7 +46,7 @@ public class DefaultProblemResolverStore extends AbstractProblemResolverStore {
3446 * @throws NullPointerException if any resolver or its exception class is {@code null}
3547 */
3648 public DefaultProblemResolverStore (List <ProblemResolver > problemResolvers ) {
37- super (problemResolvers );
49+ this (problemResolvers , new GraphClassDistanceEvaluation ( SUPERCLASS ) );
3850 }
3951
4052 /**
@@ -48,6 +60,35 @@ public DefaultProblemResolverStore(List<ProblemResolver> problemResolvers) {
4860 */
4961 public DefaultProblemResolverStore (
5062 List <ProblemResolver > problemResolvers , ClassDistanceEvaluation classDistanceEvaluation ) {
51- super (problemResolvers , classDistanceEvaluation );
63+ Map <Class <? extends Exception >, ProblemResolver > copy = new HashMap <>(problemResolvers .size ());
64+ problemResolvers .forEach (
65+ resolver -> copy .put (resolver .getExceptionClass (), Objects .requireNonNull (resolver )));
66+ this .resolvers = Map .copyOf (copy );
67+ this .classDistanceEvaluation = classDistanceEvaluation ;
68+ }
69+
70+ /**
71+ * Returns a {@link ProblemResolver} for the given exception class.
72+ *
73+ * <p>This method searches for resolvers whose exception type is assignable from the given class,
74+ * selecting the most specific match.
75+ *
76+ * @param clazz exception class to resolve
77+ * @return an {@link Optional} containing the matching resolver, or empty if none found
78+ */
79+ @ Override
80+ public Optional <ProblemResolver > findResolver (Class <? extends Exception > clazz ) {
81+ List <ProblemResolver > candidates = new ArrayList <>();
82+ for (Map .Entry <Class <? extends Exception >, ProblemResolver > entry : resolvers .entrySet ()) {
83+ if (entry .getKey ().isAssignableFrom (clazz )) {
84+ candidates .add (entry .getValue ());
85+ }
86+ }
87+
88+ return candidates .stream ().min (Comparator .comparingInt (r -> calculateDistance (r , clazz )));
89+ }
90+
91+ private int calculateDistance (ProblemResolver resolver , Class <? extends Exception > clazz ) {
92+ return classDistanceEvaluation .calculate (clazz , resolver .getExceptionClass ());
5293 }
5394}
0 commit comments